Czym różni się load od wymagania w Rubim?

Odpowiedzi:

98

requirewyszukuje bibliotekę we wszystkich zdefiniowanych ścieżkach wyszukiwania, a także dołącza .rb lub .so do wprowadzonej nazwy pliku. Zapewnia również, że biblioteka jest dołączana tylko raz. Więc jeśli Twoja aplikacja wymaga biblioteki A i B oraz biblioteki B, biblioteka A również A zostanie załadowana tylko raz.

Dzięki temu loadmusisz dodać pełną nazwę biblioteki i jest ona ładowana za każdym razem, gdy dzwonisz load- nawet jeśli jest już w pamięci.

Nikolaus Gradwohl
źródło
Dziękuję, więc możemy powiedzieć, że .rb nie jest wymagany podczas ładowania. A „wymaga” nie zawsze woła, jeśli jest w pamięci?
Arpit Vaishnav
4
requireśledzi to, co zostało już załadowane poprzez tablicę globalną $LOADED_FEATURES( $"), która loadignoruje.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
39

Inna różnica między Kernel#requirei Kernel#loadpolega na tym, że Kernel#loadpobiera opcjonalny drugi argument, który umożliwia zawinięcie załadowanego kodu w anonimowy pusty moduł.

Niestety nie jest to zbyt przydatne. Po pierwsze, łatwo loadjest wydostać się z modułu ed kodem, po prostu uzyskując dostęp do globalnej przestrzeni nazw, tj. Nadal mogą wykonywać takie same czynności jak monkeypatch class ::String; def foo; end end. Po drugie, loadnie zwraca modułu, w którym opakowuje kod, więc w zasadzie trzeba go wyłowić ObjectSpace::each_object(Module)ręcznie.

Jörg W Mittag
źródło
Dobrze wiedzieć! Nie Kernel#loadmiałem pojęcia, miałem inny argument
Josh Bodah
Niestety nie jest to zbyt przydatne. Po pierwsze, łatwo loadjest wydostać się z modułu ed kodem, po prostu uzyskując dostęp do globalnej przestrzeni nazw, tj. Nadal mogą wykonywać takie same czynności jak monkeypatch class ::String; def foo; end end. Po drugie, loadnie zwraca modułu, w którym opakowuje kod, więc w zasadzie trzeba go wyłowić ObjectSpace::each_object(Module)ręcznie.
Jörg W Mittag
4

Uruchomiłem aplikację Rails i w Gemfile miałem określony niestandardowy klejnot, który utworzyłem z opcją „require: false”. Teraz, kiedy ładowałem serwer rails lub konsolę railsów, byłem w stanie zażądać gem w inicjatorze, a gem został załadowany. Jednak gdy przeprowadziłem test funkcji specyfikacji z rspec i kapibarą, wystąpił błąd ładowania. Byłem całkowicie zdumiony, dlaczego Gem nie został znaleziony w $ LOAD_PATH podczas uruchamiania testu.

Więc przejrzałem wszystkie różne sposoby, które ładują, wymagają, współdziałają rubygemów i bundlerów. A oto podsumowanie moich ustaleń, które pomogły mi znaleźć rozwiązanie mojego konkretnego problemu:

Załaduj

1) Możesz przekazać mu bezwzględną ścieżkę do pliku ruby ​​i wykona kod z tego pliku.

load('/Users/myuser/foo.rb')

2) Możesz przekazać względną ścieżkę do załadowania. Jeśli jesteś w tym samym katalogu co plik, znajdzie go:

> load('./foo.rb')
foo.rb loaded!
=> true

Ale jeśli spróbujesz załadować plik z innego katalogu za pomocą load (), nie znajdzie go ze ścieżką względną opartą na bieżącym katalogu roboczym (np ./):

> load('./foo.rb')
LoadError: cannot load such file -- foo.rb

3) Jak pokazano powyżej, load zawsze zwraca prawdę (jeśli nie można załadować pliku, to podnosi a LoadError).

4) Importowane są zmienne globalne, klasy, stałe i metody, ale nie są to zmienne lokalne.

5) Dwukrotne wywołanie load w tym samym pliku spowoduje dwukrotne wykonanie kodu z tego pliku. Jeśli określony plik definiuje stałą, zdefiniuje tę stałą dwukrotnie, co powoduje wyświetlenie ostrzeżenia.

6) $ LOAD_PATH to tablica ścieżek bezwzględnych. Jeśli przekażesz load tylko nazwę pliku, przejdzie przez pętlę $ LOAD_PATH i wyszuka plik w każdym katalogu.

> $LOAD_PATH.push("/Users/myuser")
> load('foo.rb')
foo.rb loaded!
 => true

wymagać

1) Dwukrotne wywołanie tego samego pliku spowoduje wykonanie go tylko raz. Jest również wystarczająco sprytne, aby nie ładować tego samego pliku dwukrotnie, jeśli odwołasz się do niego raz ścieżką względną i raz ścieżką bezwzględną.

2) require zwraca true, jeśli plik został wykonany, a false, jeśli nie.

3) wymagają śledzenia, które pliki zostały już załadowane w zmiennej globalnej $ LOADED_FEATURES.

4) Nie musisz dołączać rozszerzenia pliku:

require 'foo'

5) require będzie szukać foo.rb, ale także dynamicznych plików bibliotek, takich jak foo.so, foo.o lub foo.dll. W ten sposób możesz wywołać kod w języku Ruby.

6) require nie sprawdza katalogu bieżącego, ponieważ katalog bieżący domyślnie nie znajduje się w $ LOAD_PATH.

7) require_relative przyjmuje ścieżkę względem bieżącego pliku, a nie katalogu roboczego procesu.

Rubygems

1) Rubygems to menedżer pakietów przeznaczony do łatwego zarządzania instalacją bibliotek Ruby zwanych klejnotami.

2) Pakuje swoją zawartość jako plik zip zawierający kilka plików ruby ​​i / lub plików bibliotek dynamicznych, które mogą być importowane przez twój kod, wraz z niektórymi metadanymi.

3) Rubygems zastępuje domyślną metodę require własną wersją. Ta wersja przejrzy zainstalowane przez Ciebie klejnoty oprócz katalogów w $ LOAD_PATH. Jeśli Rubygems znajdzie plik w twoich klejnotach, doda ten klejnot do twojej $ LOAD_PATH.

4) Polecenie instalacji klejnotu określa wszystkie zależności klejnotu i instaluje je. W rzeczywistości instaluje wszystkie zależności klejnotu przed zainstalowaniem samego klejnotu.

Bundler

1) Program Bundler pozwala określić wszystkie klejnoty, których potrzebuje twój projekt, i opcjonalnie wersje tych klejnotów. Następnie polecenie bundle instaluje wszystkie te klejnoty i ich zależności.

2) W pliku o nazwie Gemfile określasz, które klejnoty potrzebujesz.

3) Polecenie bundle instaluje również wszystkie klejnoty wymienione w Gemfile.lock w określonych wersjach.

4) Umieszczenie bundle exec przed poleceniem, np. Bundle exec rspec, zapewnia, że ​​require wczyta wersję klejnotu określoną w twoim Gemfile.lock.

Szyny i Bundler

1) W config / boot.rb, wymagaj uruchomienia „bundler / setup”. Bundler upewnia się, że Ruby może znaleźć wszystkie klejnoty w pliku Gemfile (i wszystkie ich zależności). require 'bundler / setup' automatycznie wykryje twój Gemfile i udostępni wszystkie klejnoty w twoim Gemfile dla Rubiego (z technicznego punktu widzenia, to umieszcza klejnoty "na ścieżce ładowania"). Możesz myśleć o tym jako o dodaniu dodatkowych mocy, które wymagają „rubygemów”.

ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])

2) Teraz, gdy Twój kod jest już dostępny dla Rubiego, możesz wymagać klejnotów, których potrzebujesz. Na przykład możesz wymagać słowa „sinatra”. Jeśli masz wiele zależności, możesz powiedzieć „wymagaj wszystkich klejnotów w moim pliku Gemfile”. Aby to zrobić, umieść następujący kod bezpośrednio po żądaniu „bundler / setup”:

Bundler.require(:default)

3) Domyślnie wywołanie Bundler.require będzie wymagało każdego klejnotu w twoim Gemfile. Jeśli wiersz w pliku Gemfile mówi gem 'foo',: require => false to upewni się, że foo jest zainstalowane, ale nie wywoła require. Jeśli chcesz użyć klejnotu, musisz zadzwonić do require ('foo').

Biorąc więc pod uwagę ten zakres wiedzy, wróciłem do kwestii mojego testu i zdałem sobie sprawę, że muszę jawnie wymagać klejnotu w rails_helper.rb, ponieważ Bundler.setup dodał go do $ LOAD_PATH, ale wymaga: false precluded Bundler. Wymagają wyraźnego wymagania tego . A potem problem został rozwiązany.

Donato
źródło