2015-05-04 6 views
5

Ich habe mich gefragt, wie ich Klassenvariablen von ModulenWie kann man auf Klassenvariablen vom Modul zugreifen?

module Entity 
    def foo 
    # puts @@rules 
    end 
end 

class Person 
    include Entity 

    attr_accessor :id, :name 

    @@rules = [[:id, :int, :not_null], 
      [:name, :string, :not_null]] 
end 

class Car 
    include Entity 

    attr_accessor :id, :year 

    @@rules = [[:id, :string, :not_null], 
      [:year:, :int, :not_null]] 
end 

p = Person.new 
c = Car.new 

p.foo # [[:id, :int, :not_null], [:name, :string, :not_null]] 
c.foo # [[:id, :string, :not_null], [:year, :int, :not_null]] 

Ich nahm noch einen Blick auf cattr_accessor und mattr_accessor von ActiveSupport, zugreifen kann, sondern eine Art zu lösen, nicht finden kann.

Antwort

1

Es ist nicht die eleganteste Lösung, aber class_eval Werke:

module Entity 
    def foo 
    self.class.class_eval('@@rules') 
    end 
end 

Edit: Eigentlich etwas sauberer kann class_variable_get

module Entity 
    def foo 
    self.class.class_variable_get(:@@rules) 
    end 
end 
+0

Nun, ty! Der Code funktioniert und das ist großartig! Ich hoffe eine Antwort, in der der Code eleganter wäre! – Quarktum

12

Klassenvariablen in Ruby zu benutzen sind seltsam, wenn es um die Vererbung kommt. Wenn Sie nicht genau wissen, womit Sie es zu tun haben, sollten Sie sie am besten vermeiden. Sie könnten denken, dass Sie in diesem Fall keine Vererbung verwenden, aber was include tatsächlich tut, ist die Inserts Entity in die Vorfahren von Person. Siehe:

Person.ancestors 
# [Person, Entity, Object, Kernel, BasicObject] 

Das besondere Verhalten schwierig zu beschreiben, aber die kurze Version ist, dass im Grunde @@rules zwischen Entity geteilt wird, Person, undCar! Aussehen:

Entity.class_variable_set(:@@rules, 'foo') 
puts Car.class_variable_get(:@@rules) 
# foo 
puts Person.class_variable_get(:@@rules) 
# foo 

Wahrscheinlich wollen Sie das nicht!

Es ist besser, hier eine Klasse Instanz Variable zu verwenden, die eigentlich für jede Klasse getrennt ist.

+0

Ich mag diese Antwort, aber in diesem Fall wird das Attribut 'rules' offengelegt – Quarktum

+0

Sie können es genauso leicht zu einem Leser als zu einem Accessor machen, ich werde meine Antwort bearbeiten. – Max

+0

Es wird immer noch belichtet (wie auch immer, Reflektion als erste Antwort verwendend, kann auch darauf zugegriffen werden) – Quarktum

0

Zusätzlich zu den bereits gegebenen Antworten, hier ist etwas, das ich irgendwann wieder gefunden habe.

module MyModule 
    @@my_variable = 5 
    define_singleton_method(:my_variable) do 
    @@my_variable 
    end 
end 

Jetzt werden Sie in der Lage sein, den Klassenvariable auf zwei Arten zugreifen: MyModule::my_variable oder MyModule.my_variable.

Dies funktioniert jetzt wie ein attr_reader. Sie können eine zweite Singleton-Methode für die Zuweisung definieren.

0

Dies ist nicht wirklich meine Antwort, es ist eine Variation der @ Max-Antwort, nur ohne die Variable @rules freizulegen (siehe @ Quarktum Kommentare).

Der Unterschied hier ist, dass ich die #module_exec method verwende, die Instanzvariablenzugriff erlaubt (im Gegensatz zu #module_eval).

Auch ich bin der Definition der .foo und #foo Methoden unter den Anwendungsbereich der einschließlich Klasse, so dass die Methoden, die Klassen Methoden anstatt des Moduls Methoden (Test mit Car.methods false Car Methoden ohne Vererbung sehen) sind.

module Entity 
    # create the class instance variable methods when this is included 
    def self.included klass 
    klass.module_exec do 
     @rules ||= [] 
     def self.foo 
     puts @rules 
     end 
     def foo 
     self.class.foo 
     end 
    end 
    end 
end 

class Person 
    include Entity 

    attr_accessor :id, :name 

    @rules = [[:id, :int, :not_null], 
       [:name, :string, :not_null]] 
end 

class Car 
    include Entity 

    attr_accessor :id, :year 

    @rules = [[:id, :string, :not_null], 
       [:year, :int, :not_null]] 
end 
Verwandte Themen