I am new to ruby and trying to figure out how the class declaration below from Sequel gem works behind the scene.
class Enumeration < Sequel::Model(DB[:enumerations]); end
Upon quick investigation of Sequel gem's code, it seems to me that the module method Sequel::Model returns a class instance with a configured class attribute. The return instance of Class is then used in inheritance hierarchy, so I tried to test my understanding through the code;
module MySequel
class MyModel
module ClassMethods
attr_accessor :table_name
def model(table_name)
klass = Class.new(self)
klass.table_name = table_name
puts klass.table_name # prints table_name, it does get set for the first class Class object klass
klass
end
end
extend ClassMethods
end
end
class Enumeration < MySequel::MyModel.model(:enumerations); end
class EnumerationValue < MySequel::MyModel.model(:enumeration_values); end
p Enumeration.table_name # prints nil
p EnumerationValue.table_name # prints nil
Based on my understanding the class variable table_name gets set while creating an instance of Class and gets propagated into it's child class. However, that doesn't seem to be the case.
Can someone please explain the concept behind Sequel::Model and the problem with my sample implementation to achieve the same result.
The behaviour in this example is similar to when using the anonymous classes above.
class TestModel
@@table_name = 'test'
def TestModel.table_name
@@table_name
end
def TestModel.table_name=(value)
@@table_name = value
end
end
TestModel.table_name = 'posts'
p TestModel.table_name # prints posts
class ChildTestModel < TestModel; end
puts Enumeration.table_name # prints nil
The Sequel library is seemingly propagating the information about the dataset set through call to module method to the child class (through anonymous class.)
However, I can't figure out; what construct of the Ruby language allows the Status class below to know that it's restricted to a record in table enumerations with name set to 'status'.
class Status < Sequel::Model(DB[:enumerations].where(name: 'status')); end
The Sequel library is seemingly propagating the information about the dataset set through call to module method to the child class
Yes, indeed. Sequel uses the inherited
callback to copy the class instance variables over to the subclass (see lib/sequel/model/base.rb#843
).
Here's a very basic version for your example class:
module MySequel
class MyModel
module ClassMethods
attr_accessor :table_name
def model(table_name)
klass = Class.new(self)
klass.table_name = table_name
klass
end
def inherited(subclass)
instance_variables.each do |var|
value = instance_variable_get(var)
subclass.instance_variable_set(var, value)
end
end
end
extend ClassMethods
end
end
As a result, the subclasses will have their class instance variables (here @table_name
) set to the same values as the (anonymous) parent class:
class Enumeration < MySequel::MyModel.model(:enumerations); end
class EnumerationValue < MySequel::MyModel.model(:enumeration_values); end
p Enumeration.table_name #=> :enumerations
p EnumerationValue.table_name #=> :enumeration_values