Aggiungere una directory a $ LOAD_PATH (Ruby)


96

Ho visto due tecniche comunemente usate per aggiungere la directory del file attualmente in esecuzione a $ LOAD_PATH (o $ :). Vedo i vantaggi di farlo nel caso in cui non lavori con una gemma. Uno sembra più prolisso dell'altro, ovviamente, ma c'è un motivo per andare l'uno sull'altro?

Il primo metodo dettagliato (potrebbe essere eccessivo):

$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))

e il più semplice, veloce e sporco:

$:.unshift File.dirname(__FILE__)

Qualche motivo per andare l'uno sull'altro?


2
Una versione leggermente meno prolissa di quella prolissa è:File.expand_path(File.dirname(__FILE__)).tap {|pwd| $LOAD_PATH.unshift(pwd) unless $LOAD_PATH.include?(pwd)}
Nathan Long

che ne dici della clausola "a meno che non"? Come possono i due precedenti essere equivalenti?
inger

Come qualcuno che è venuto qui per cercare di capire come usarlo, è super criptico. Non vedo da dove proviene il nome della directory negli esempi. Apprezzerei se qualcuno potesse chiarirlo.
SlySherZ

1
L'utilizzo __dir__(a partire da Ruby 2.0) può renderli più concisi.
Nathan Long,

Risposte:


51

Direi di andare con $:.unshift File.dirname(__FILE__)l'altro, semplicemente perché ne ho visto un utilizzo molto maggiore nel codice rispetto a $LOAD_PATHquello, ed è anche più breve!


Quando ho iniziato con Ruby, ovviamente pensavo che $ LOAD_PATH fosse migliore. Ma una volta che ti sei laureato dallo stato di principiante, userei $ LOAD_PATH solo se stavo cercando di rendere il mio codice più leggibile per un principiante. Meh è un compromesso. Dipende da quanto "pubblico" è il codice, a patto che l'utilizzo della memoria sia lo stesso per ciascuno, cosa che presumo essenzialmente sia.
boulder_ruby

9
Dipende dalla guida di stile che segui per il tuo progetto. La popolare Guida allo stile di Ruby dice "Evita di usare variabili speciali in stile Perl (come $ :, $ ;, ecc.). Sono piuttosto criptiche e il loro uso in tutto tranne che in script di una riga è sconsigliato."
bobmagoo

152

Il percorso di caricamento di Ruby è molto comunemente visto scritto come $:, ma solo perché è breve, non lo rende migliore. Se preferisci la chiarezza all'intelligenza, o se la brevità fine a se stessa ti rende pruriginoso, non devi farlo solo perché tutti gli altri lo sono. Di Ciao a ...

$LOAD_PATH

... e saluta a ...

# I don't quite understand what this is doing...
$:

29
Inoltre, è molto più difficile per Google per stringhe come "$:" che contengono solo simboli.
DSimon

23

Non mi piace molto il modo "veloce e sporco". Chiunque sia nuovo in Ruby mediterà su cosa $:.sia.

Lo trovo più ovvio.

libdir = File.dirname(__FILE__)
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)

O se mi interessa avere il percorso completo ...

libdir = File.expand_path(File.dirname(__FILE__))
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)

AGGIORNAMENTO 2009/09/10

Negli ultimi tempi ho fatto quanto segue:

$:.unshift(File.expand_path(File.dirname(__FILE__))) unless
    $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))

L'ho visto in un sacco di diversi progetti ruby ​​durante la navigazione su GitHub.

Sembra essere la convenzione?


@ LukeAntins, questo è davvero fantastico, ma dove dovrei "bootstrap" load_path nell'applicazione?
gaussblurinc

@gaussblurinc Da qualche parte "vicino alla parte superiore" della tua lib / applicazione, ma dipende davvero. Se avevi un binfile che era sempre relativo al tuo codeed è stato eseguito solo dal binfile ... bootstrap nel cestino. Se hai una libreria, avvia il bootstrap nella parte superiore del codice della libreria come in lib/code.rbper accedere a tutto ciò che si trova sotto lib/code/. Spero che questa escursione ti aiuti!
Luke Antins

1
RuboCop mi informa che __dir__può essere utilizzato per ottenere un percorso alla directory del file corrente.
Raphael

8

Se digiti il script/consoletuo progetto Rails e inserisci $:, otterrai un array che include tutte le directory necessarie per caricare Ruby. Il vantaggio di questo piccolo esercizio è che $:è un array. Stando così le cose, puoi eseguire funzioni su di esso come anteporre altre directory con il unshiftmetodo o l' <<operatore. Come hai sottinteso nella tua dichiarazione $:e $LOAD_PATHsono la stessa cosa.

Lo svantaggio di farlo nel modo rapido e sporco come hai detto è questo: se hai già la directory nel tuo percorso di avvio, si ripeterà.

Esempio:

Ho un plugin che ho creato chiamato todo. La mia directory è strutturata in questo modo:

/ --- venditore
  |
  | --- / plugins
        |
        | --- / todo
              |
              | --- / lib
                    |
                    | --- / app
                          |
                          | --- / modelli
                          | --- / controllers
              |
              | --- / rails
                    |
                    | --- init.rb

Nel file init.rb ho inserito il seguente codice:

## In vendor/plugins/todo/rails/init.rb
    %w{ models controllers models }.each do |dir|
      path = File.expand_path(File.join(File.dirname(__FILE__), '../lib', 'app', dir))
      $LOAD_PATH << path
      ActiveSupport::Dependencies.load_paths << path
      ActiveSupport::Dependencies.load_once_paths.delete(path)
    end 

Nota come dico al blocco di codice di eseguire le azioni all'interno del blocco sulle stringhe "models", "controller" e "models", dove ripeto "models". (Cordiali saluti, %w{ ... }è solo un altro modo per dire a Ruby di contenere un array di stringhe). Quando corro script/console, digito quanto segue:

>> puts $:

E lo digito in modo che sia più facile leggere il contenuto della stringa. L'output che ottengo è:

...
...
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/models
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/controllers
./Users/Me/mySites/myRailsApp/vendor/plugins/todo/lib/app/models

Come puoi vedere, sebbene questo sia un esempio semplice che potrei creare mentre utilizzo un progetto su cui sto attualmente lavorando, se non stai attento il modo veloce e sporco porterà a percorsi ripetuti. Il modo più lungo verificherà la presenza di percorsi ripetuti e si assicurerà che non si verifichino.

Se sei un programmatore Rails esperto, probabilmente hai un'idea molto chiara di quello che stai facendo e probabilmente non commetti l'errore di ripetere i percorsi. Se sei un principiante, sceglierei il modo più lungo finché non capisci davvero cosa stai facendo.


la tua risposta è molto utile e anche ben spiegata. Modifica suggerita: il metodo load_pathse load_once_paths.deletesono stati deprecati. Sarebbe essere aiutati ad aggiornare le linee che si riferiscono a loro come a: ActiveSupport::Dependencies.autoload_paths << path ActiveSupport::Dependencies.autoload_once_paths.delete(path)
Uzzar

8

Il meglio che ho trovato per l'aggiunta di una directory tramite il percorso relativo quando si utilizza Rspec. Lo trovo abbastanza prolisso, ma anche carino.

$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))


-2

So che è passato molto tempo da quando questa domanda è stata posta per la prima volta, ma ho un'ulteriore risposta che voglio condividere.

Ho diverse applicazioni Ruby che sono state sviluppate da un altro programmatore nel corso di diversi anni, e riutilizzano le stesse classi nelle diverse applicazioni sebbene possano accedere allo stesso database. Poiché ciò viola la regola DRY, ho deciso di creare una libreria di classi da condividere con tutte le applicazioni Ruby. Avrei potuto metterlo nella libreria Ruby principale, ma questo avrebbe nascosto il codice personalizzato nella base di codice comune, cosa che non volevo fare.

Ho avuto un problema in cui ho avuto un conflitto di nomi tra un nome già definito "profile.rb" e una classe che stavo usando. Questo conflitto non è stato un problema finché non ho provato a creare la libreria di codice comune. Normalmente, Ruby cerca prima le posizioni delle applicazioni, quindi va nelle posizioni $ LOAD_PATH.

Application_controller.rb non è riuscito a trovare la classe che ho creato e ha generato un errore sulla definizione originale perché non è una classe. Poiché ho rimosso la definizione della classe dalla sezione app / models dell'applicazione, Ruby non è riuscita a trovarla e l'ho cercata nei percorsi di Ruby.

Quindi, ho modificato la variabile $ LOAD_PATH per includere un percorso alla directory della libreria che stavo utilizzando. Questo può essere fatto nel file environment.rb al momento dell'inizializzazione.

Anche con la nuova directory aggiunta al percorso di ricerca, Ruby generava un errore perché preferibilmente prendeva per primo il file definito dal sistema. Il percorso di ricerca nella variabile $ LOAD_PATH cerca preferenzialmente prima i percorsi di Ruby.

Quindi, avevo bisogno di cambiare l'ordine di ricerca in modo che Ruby trovasse la classe nella mia libreria comune prima di cercare nelle librerie incorporate.

Questo codice lo ha fatto nel file environment.rb:

Rails::Initializer.run do |config|

* * * * *

path = []
path.concat($LOAD_PATH)
$LOAD_PATH.clear
$LOAD_PATH << 'C:\web\common\lib'
$LOAD_PATH << 'C:\web\common'
$LOAD_PATH.concat(path)

* * * * *

end

Non penso che tu possa utilizzare nessuno dei costrutti di codifica avanzati forniti in precedenza a questo livello, ma funziona perfettamente se vuoi impostare qualcosa al momento dell'inizializzazione nella tua app. È necessario mantenere l'ordine originale della variabile $ LOAD_PATH originale quando viene aggiunta di nuovo alla nuova variabile altrimenti alcune delle classi principali di Ruby vanno perse.

Nel file application_controller.rb, utilizzo semplicemente un file

require 'profile'
require 'etc' #etc

e questo carica i file della libreria personalizzata per l'intera applicazione, cioè non devo usare i comandi require in ogni controller.

Per me, questa era la soluzione che stavo cercando e ho pensato di aggiungerla a questa risposta per passare le informazioni.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.