2012-01-23 14 views
5

Mam plik zewnętrzny: path_to_external_file.rb z niektórych definicji klasy:Ładowanie plików zewnętrznych w klasie/modułu

class A 
    some_definitions 
end 

I chcę, aby załadować że wewnątrz modułu B tak, że klasa A określone powyżej mogą być, o którym mowa jako B::A. Próbowałem:

class B 
    load('path_to_external_file.rb') 
end 

ale A jest zdefiniowana w głównym środowisku, a nie w B:

A #=> A 
B.constants # => [] 

Jak mogę załadować plików zewnętrznych w ramach niektórych klas/modułu?

Edit powinienem odczytać pliki zewnętrzne jako ciągi i ocenić je w Class.new{...} i include że klasy w ciągu B?

+0

do jakiego końca? Dlaczego nie możesz po prostu użyć klasy A bezpośrednio?Czy możesz czerpać korzyści z jego modularyzacji? 'load' i' require' nie będą faktycznie ładować klasy do modułu, tylko ładują kod źródłowy, więc twoje klasy są zdefiniowane dokładnie tak, jak są w pliku. Nie wiesz, dlaczego chcesz to zrobić? – brad

+1

@brad Ponieważ te pliki zewnętrzne mają być napisane przez użytkowników i mogą być nazwane arbitralnie. Jeśli zdefiniuję te klasy w głównym środowisku, zepsują przestrzeń nazw. – sawa

+0

Uważaj na manipulowanie innymi obszarami nazw za pomocą 'ObjectSpace # each_object'. – Reactormonk

Odpowiedz

4

Nie możesz. Przynajmniej przy użyciu load lub , pliki Ruby będą zawsze oceniane w górnym kontekście.

Można obejść ten problem na dwa sposoby:

  • Definiowanie class B::A bezpośrednio (ale prawdopodobnie starając się uniknąć tego)
  • Zastosowanie eval(File.read("path_to_external_file.rb")) ciągu swojej klasie B

Edycja : Być może ta biblioteka jest dla Ciebie interesująca: https://github.com/dreamcat4/script/blob/master/intro.txt

3

Generalnie nie jest dobrym pomysłem zdefiniowanie klasy jako "klasy A", ale potem "magiczne" sprawdzenie jej w module B. Jeśli chcesz odwołać się do klasy A jako B :: A, powinieneś zdefiniować ją za pomocą:

module B 
    class A 
    # contents 
    end 
end 

czyli

class B::A 
    # contents 
end 

przeciwnym razie ktoś, kto czyta kod będzie mylić. W tym przypadku nie uzyskuje się niczego w jasności, zwięzłości lub wygodzie, używając "sztuczek", więc prostszy kod jest lepszy. Tutaj jest lekcja: cechy metaprogramowania Ruby są świetne, ale nie ma potrzeby ich używać bezinteresownie. Używaj ich tylko wtedy, gdy naprawdę coś zyskujesz. W przeciwnym razie po prostu sprawisz, że twój kod będzie trudny do zrozumienia.

ALE, po przeczytaniu komentarza, wygląda na to, że istnieje naprawdę dobry powód, aby zrobić coś takiego w swoim przypadku. Sugeruję, że poniższe rozwiązanie byłoby jeszcze lepsze niż to, co sobie wyobrażasz:

m = Module.new 
m.module_eval("class C; end") 
m.constants 
=> [:C] 
m.const_get(:C) 
=> #<Module:0xfd0da0>::C 

Widzisz? Jeśli chcesz mieć "gwarantowaną unikalną" przestrzeń nazw, możesz użyć anonimowego modułu. Możesz przechowywać te moduły w haszach lub innych strukturach danych i wyciągnąć je z nich w razie potrzeby. To rozwiązuje problem, o którym wspomniałeś, że użytkownicy Twojej aplikacji dodadzą własne klasy i nie chcesz, aby imiona się kolidowały.

Powiązane problemy