Dodawanie katalogu do $ LOAD_PATH (Ruby)

96

Widziałem dwie powszechnie używane techniki dodawania katalogu aktualnie wykonywanego pliku do $ LOAD_PATH (lub $ :). Widzę zalety robienia tego na wypadek, gdybyś nie pracował z klejnotem. Jedno wydaje się być bardziej rozwlekłe niż drugie, oczywiście, ale czy jest powód, aby wybierać jedno nad drugim?

Pierwsza, szczegółowa metoda (może być przesadą):

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

i prostsze, szybkie i brudne:

$:.unshift File.dirname(__FILE__)

Czy jest jakiś powód, aby iść z jednym nad drugim?

Mark W.
źródło
2
Nieco mniej gadatliwy wersja z nich jest gadatliwy:File.expand_path(File.dirname(__FILE__)).tap {|pwd| $LOAD_PATH.unshift(pwd) unless $LOAD_PATH.include?(pwd)}
Nathan Długa
a co z klauzulą ​​„chyba że”? W jaki sposób te dwa powyższe mogą być równoważne?
inger
Jako ktoś, kto przyjechał tutaj, aby spróbować zrozumieć, jak tego użyć, jest to bardzo tajemnicze. Nie widzę, skąd pochodzi nazwa katalogu w przykładach. Byłbym wdzięczny, gdyby ktoś mógł to wyjaśnić.
SlySherZ
1
Używanie __dir__(od Ruby 2.0) może uczynić którekolwiek z nich bardziej zwięzłym.
Nathan Long

Odpowiedzi:

52

Powiedziałbym, że przejdź $:.unshift File.dirname(__FILE__)do drugiego, po prostu dlatego, że widziałem o wiele więcej użycia go w kodzie niż ten $LOAD_PATH, a także jest krótszy!

Ryan Bigg
źródło
Kiedy po raz pierwszy zacząłem korzystać z Rubiego, oczywiście pomyślałem, że $ LOAD_PATH jest lepszy. Ale po przejściu ze statusu początkującego użyłbym $ LOAD_PATH tylko wtedy, gdybym próbował uczynić mój kod bardziej czytelnym dla początkującego. Meh to kompromis. Zależy to od tego, jak „publiczny” jest kod, o ile użycie pamięci jest takie samo dla każdego z nich, co, jak zakładam, zasadniczo jest.
boulder_ruby
9
Zależy od przewodnika po stylu, który zastosujesz w swoim projekcie. Popularny przewodnik po stylu Ruby mówi: „Unikaj używania specjalnych zmiennych w stylu Perla (takich jak $ :, $; itp.). Są one dość tajemnicze i odradza się ich używanie w skryptach innych niż jednowierszowe”.
bobmagoo
153

Ścieżka ładowania Rubiego jest bardzo często zapisywana jako $:, ale tylko dlatego, że jest krótka, nie czyni jej lepszą. Jeśli wolisz klarowność od sprytu lub jeśli zwięzłość sama w sobie powoduje swędzenie, nie musisz tego robić tylko dlatego, że wszyscy inni tak robią. Powiedz cześć ...

$LOAD_PATH

... i pożegnaj się z ...

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

źródło
29
Ponadto Google jest znacznie trudniejsze w przypadku ciągów takich jak „$:”, które zawierają tylko symbole.
DSimon
23

Nie przepadam za sposobem „szybkim i nieczystym”. Każdy nowy w Ruby będzie się zastanawiał, co $:.jest.

Uważam to za bardziej oczywiste.

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

Albo jeśli zależy mi na pełnej ścieżce ...

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

AKTUALIZACJA 2009/09/10

Ostatnio robiłem następujące rzeczy:

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

Widziałem to w wielu różnych projektach Ruby podczas przeglądania GitHub.

Wydaje się, że to konwencja?

Luke Antins
źródło
@LukeAntins, to jest naprawdę świetne, ale gdzie powinienem "załadować" load_path w aplikacji?
gaussblurinc
@gaussblurinc Gdzieś „na górze” twojej biblioteki / aplikacji, ale to naprawdę zależy. Gdybyś miał binplik, który był zawsze względny w stosunku do twojego codei był uruchamiany tylko przez binplik ... bootstrap w bin. Jeśli masz bibliotekę, wykonaj ładowanie początkowe na górze kodu swojej biblioteki, tak jak w, lib/code.rbaby uzyskać dostęp do wszystkiego, co znajduje się poniżej lib/code/. Mam nadzieję, że ta wędrówka pomoże!
Luke Antins,
1
RuboCop informuje mnie, że __dir__można go użyć do uzyskania ścieżki do katalogu bieżącego pliku.
Raphael
8

Jeśli wpiszesz script/consoleswój projekt Rails i wejdziesz $:, otrzymasz tablicę zawierającą wszystkie katalogi potrzebne do załadowania Rubiego. Wnioskiem z tego małego ćwiczenia jest $:tablica. W związku z tym możesz wykonywać na nim funkcje, takie jak poprzedzanie innych katalogów unshiftmetodą lub <<operatorem. Jak zasugerowałeś w swoim oświadczeniu $:i $LOAD_PATHsą takie same.

Wadą robienia tego w szybki i brudny sposób, jak wspomniałeś, jest to: jeśli masz już katalog w ścieżce rozruchowej, powtórzy się.

Przykład:

Mam utworzoną przeze mnie wtyczkę o nazwie todo. Mój katalog ma następującą strukturę:

/---sprzedawca
  |
  | --- / plugins
        |
        | --- / todo
              |
              | --- / lib
                    |
                    | --- / app
                          |
                          | --- / modele
                          | --- / controllers
              |
              | --- / rails
                    |
                    | --- init.rb

W pliku init.rb wpisałem następujący kod:

## 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 

Zwróć uwagę, jak mówię blokowi kodu, aby wykonywał działania wewnątrz bloku do łańcuchów „modele”, „kontrolery” i „modele”, gdzie powtarzam „modele”. (FYI, %w{ ... }to po prostu kolejny sposób, aby powiedzieć Ruby'emu, aby przechował tablicę łańcuchów). Kiedy biegnę script/console, wpisuję:

>> puts $:

Piszę to, aby łatwiej było odczytać zawartość ciągu. Wynik, który otrzymuję, to:

...
...
./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

Jak widać, chociaż jest to tak prosty przykład, jaki mógłbym stworzyć, korzystając z projektu, nad którym obecnie pracuję, jeśli nie będziesz ostrożny, szybki i brudny sposób doprowadzi do powtarzających się ścieżek. Dłuższa droga spowoduje sprawdzenie powtarzających się ścieżek i upewnienie się, że nie występują.

Jeśli jesteś doświadczonym programistą Railsów, prawdopodobnie masz bardzo dobre pojęcie o tym, co robisz i prawdopodobnie nie popełnisz błędu polegającego na powtarzaniu ścieżek. Jeśli jesteś nowicjuszem, poszedłbym dłuższą drogą, dopóki naprawdę nie zrozumiesz, co robisz.

Dyba
źródło
twoja odpowiedź jest bardzo pomocna i dobrze wyjaśniona. Sugerowana edycja: metoda load_pathsi load_once_paths.deletezostały wycofane. ActiveSupport::Dependencies.autoload_paths << path ActiveSupport::Dependencies.autoload_once_paths.delete(path)
Pomocne
8

Najlepsze, z jakimi spotkałem się przy dodawaniu katalogu poprzez ścieżkę względną podczas korzystania z Rspec. Uważam, że jest wystarczająco rozwlekły, ale nadal jest to fajny jeden liniowiec.

$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
Dave Robertson
źródło
1

Istnieje perełka, która pozwoli Ci skonfigurować ścieżkę ładowania z ładniejszym i czystszym kodem. Sprawdź to: https://github.com/nayyara-samuel/load-path .

Posiada również dobrą dokumentację

Rubyist
źródło
-2

Wiem, że minęło dużo czasu, odkąd zadano to pytanie po raz pierwszy, ale mam dodatkową odpowiedź, którą chcę się podzielić.

Mam kilka aplikacji Ruby, które zostały opracowane przez innego programistę przez kilka lat i ponownie używają tych samych klas w różnych aplikacjach, chociaż mogą uzyskiwać dostęp do tej samej bazy danych. Ponieważ narusza to zasadę DRY, zdecydowałem się utworzyć bibliotekę klas, która będzie współdzielona przez wszystkie aplikacje Rubiego. Mógłbym umieścić go w głównej bibliotece Ruby, ale to ukryłoby niestandardowy kod we wspólnej bazie kodu, czego nie chciałem robić.

Wystąpił problem polegający na konflikcie nazw między już zdefiniowaną nazwą „profile.rb” a klasą, której używałem. Ten konflikt nie był problemem, dopóki nie spróbowałem stworzyć wspólnej biblioteki kodu. Zwykle Ruby szuka najpierw lokalizacji aplikacji, a następnie przechodzi do lokalizacji $ LOAD_PATH.

Plik application_controller.rb nie mógł znaleźć klasy, którą utworzyłem, i zgłosił błąd do oryginalnej definicji, ponieważ nie jest to klasa. Ponieważ usunąłem definicję klasy z sekcji aplikacji / modeli aplikacji, Ruby nie mógł jej tam znaleźć i szukał jej w ścieżkach Rubiego.

Tak więc zmodyfikowałem zmienną $ LOAD_PATH, aby zawierała ścieżkę do katalogu biblioteki, którego używałem. Można to zrobić w pliku environment.rb w czasie inicjalizacji.

Nawet po dodaniu nowego katalogu do ścieżki wyszukiwania, Ruby zgłaszał błąd, ponieważ najpierw pobierał plik zdefiniowany przez system. Ścieżka wyszukiwania w zmiennej $ LOAD_PATH preferencyjnie przeszukuje najpierw ścieżki Rubiego.

Musiałem więc zmienić kolejność wyszukiwania, aby Ruby znalazł klasę w mojej wspólnej bibliotece, zanim przeszukał wbudowane biblioteki.

Ten kod zrobił to w pliku 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

Nie sądzę, abyś mógł użyć żadnej z zaawansowanych konstrukcji kodowania podanych wcześniej na tym poziomie, ale działa dobrze, jeśli chcesz skonfigurować coś w czasie inicjalizacji w swojej aplikacji. Musisz zachować oryginalną kolejność oryginalnej zmiennej $ LOAD_PATH po jej ponownym dodaniu do nowej zmiennej, w przeciwnym razie niektóre główne klasy Rubiego zostaną utracone.

W pliku application_controller.rb po prostu używam pliku

require 'profile'
require 'etc' #etc

a to ładuje niestandardowe pliki bibliotek dla całej aplikacji, tj. nie muszę używać poleceń wymagających w każdym kontrolerze.

Dla mnie było to rozwiązanie, którego szukałem i pomyślałem, że dodam je do tej odpowiedzi, aby przekazać informacje.

Timothy Dooling
źródło