2009-08-25 9 views
32

Mam klasę, która ma wiele modułów, które są mieszane z nim na podstawie niektórych kryteriów runtime.Jak określić, które moduły zostały połączone w jedną klasę?

Chcę mieć możliwość uzyskania listy modułów, które zostały wymieszane z tą klasą. Jak możesz to robić?

UPDATE

Więc kiedy powiedział mi chodziło klasa obiektu, ponieważ jest to obiekt, który jest wydłużony w czasie wykonywania przy użyciu:

obj.extend(MyModule) 

obj.included_modules i obj.ancestors nie istnieje więc nie można pobrać modułów, które zostały zmiksowane.

Odpowiedz

38

Spróbuj:

MyClass.ancestors.select {|o| o.class == Module } 

na przykład:

>> Array.ancestors.select {|o| o.class == Module} 
=> [Enumerable, Kernel] 

UPDATE

Aby uzyskać moduły mieszane do instancji obiektu przy starcie musisz pobrać eigenclass instancji . Nie ma czysty sposób to zrobić w Ruby, ale dość powszechne idiom jest następujący:

(class << obj; self; end).included_modules 

Jeśli znajdziesz się za pomocą tego dużo, można je ogólnie dostępne:

module Kernel 
    def eigenclass 
    class << self 
     self 
    end 
    end 
end 

i roztwór jest następnie:

obj.eigenclass.included_modules 
+10

jest gotowe sposób, że nazwie 'included_modules '. – Swanand

+0

Tak jest. Dzięki za wskazówkę. –

+0

Okazało się, że skoczyłem z pistoletu. Podniosłem pytanie z kilkoma dodatkowymi szczegółami. –

39

to może być lepszy pomysł:

MyClass.included_modules 
irb(main):001:0> Array.included_modules 
=> [Enumerable, Kernel] 
21

Jeśli szukasz całej listy, najlepszym wyborem jest Swanand's answer.

Jeśli, z drugiej strony, warto sprawdzić, czy klasa obejmuje danego modułu operator < jest twoim przyjacielem:

module Foo 
end 

class Bar 
    include Foo 
end 

Bar < Foo 
# => true 
3

tylko klasy mogą metody historię i jeśli dodać metodę do instancji, którą faktycznie dodajesz do metaklasu obiektów. Moduł, którego szukasz, znajduje się na tej liście przodków metaclasu.

module TestModule; end 

obj = "test" 
obj.extend(TestModule) 

class Object 
    def metaclass 
    class << self; self; end 
    end 
end 

obj.metaclass.ancestors 
# => [TestModule, String, Comparable, Object, Kernel, BasicObject] 
6

Oto kolejny skuteczny sposób, aby sprawdzić, czy moduł został zawarte lub rozszerzony przez klasę.

Jak już wspomniano, można określić, czy moduł jest zawarty w klasie, sprawdzając metodę klasy included_modules, która istnieje we wszystkich klasach ruby.

Więc jeśli miał klasę o nazwie MyClass i chcesz sprawdzić, czy to zawarte moduł Comparable mógłby zrobić coś takiego:

# will return true if MyClass.include(Comparable) 
MyClass.included_modules.include?(Comparable) 

Jeśli chcesz sprawdzić, czy dana klasa ma rozszerzony moduł ActiveRecord::Querying, jak wszystkie klasy szyny modelu zrobić, rzeczywiście można to wykorzystać:

# will return true if MyClass.extend(ActiveRecord::Querying) 
MyClass.kind_of?(ActiveRecord::Querying) 

Dlaczego to działa? Cytując książkę Eloquent Ruby, by Russ Olsen:

Kiedy mieszać moduł do klasy, Ruby rewires hierarchii klas trochę, wkładając moduł jako rodzaj pseudo nadrzędnej klasy.

To oznacza również, że innym sposobem, aby określić, czy moduł został zawarte na swoją klasę jest zrobić coś takiego (choć nadal preferują metodę included_modules):

# will also return true if MyClass.include(Comparable) 
MyClass.new.kind_of?(Comparable) 
17
obj.singleton_class.included_modules 

A jeśli chcesz sprawdzić, czy moduł zawiera:

obj.singleton_class.include? MyModule 
+0

AFAIK, zdecydowanie jest to obecnie preferowany sposób na przetestowanie tego. – xiy

2

I jak na @ AlexParamonov rozwiązanie, ale grałem dookoła i okazało się, że to działa tak samo dobrze:

obj.class.include? MyModule

MyClass.include? MyModule

http://www.ruby-doc.org/core-2.1.2/Module.html#method-i-include-3F

Nie
+0

O ile nie zmodyfikowano obiektu singleton. Na przykład: 'a = []; a.extend (MyAwesomeModule) '. W tym przypadku 'a.class.included_modules' nie będzie zawierało' MyAwesomeModule', ale 'a.singleton_class.included_modules' będzie – AlexParamonov

Powiązane problemy