Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access class variables from module?

Tags:

ruby

I was wondering how can I access class variables from modules

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

I took a look at cattr_accessor and mattr_accessor from ActiveSupport, but still can't find a way to solve this.

like image 459
Quarktum Avatar asked May 04 '15 03:05

Quarktum


1 Answers

Class variables in Ruby are weird when it comes to inheritance. Unless you know exactly what you're messing with there, it's best to avoid them. You might think you aren't using inheritance in this case, but what include actually does is inserts Entity into the ancestors of Person. See:

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

The particular behavior is tricky to describe, but the short version is that basically @@rules is shared between Entity, Person, and Car! Look:

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

You probably don't want that!

It's better to use a class instance variable here, which is actually separate for each class.

module Entity 
  # create the class instance variable methods when this is included
  def self.included klass
    klass.singleton_class.send(:attr_reader, :rules)
  end

  def foo
    puts self.class.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
like image 151
Max Avatar answered Nov 07 '22 08:11

Max