Ruby 中的动态 类、Class.new、解包 Sequel::Model
Dynamic classes in Ruby, Class.new, unpacking Sequel::Model
我是 ruby 的新手,正在尝试弄清楚下面 Sequel gem 中的 class 声明是如何在幕后工作的。
class Enumeration < Sequel::Model(DB[:enumerations]); end
快速调查 Sequel gem 的代码后,在我看来模块方法 Sequel::Model return 是一个 class 实例配置 class 属性。 Class的return实例然后在继承层次中使用,所以我试图通过代码测试我的理解;
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
根据我的理解,class 变量 table_name 在创建 Class 的实例时被设置并传播到它的子 class。然而,似乎并非如此。
有人可以解释 Sequel::Model 背后的概念以及我的示例实现中的问题以获得相同的结果。
此示例中的行为类似于使用上面的匿名 classes 时的行为。
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
Sequel 库似乎通过调用模块方法向子 class 传播有关数据集集的信息(通过匿名 class。)
但是,我想不通; Ruby 语言的什么构造允许下面的状态 class 知道它仅限于 table 枚举中名称设置为 'status'.
的记录
class Status < Sequel::Model(DB[:enumerations].where(name: 'status')); end
因此,大部分代码都是正确的,您对它的工作原理的假设也是正确的。真是太棒了,绝对不是ruby的新手级别!
所以,什么不起作用。当你定义一些 class 的 subclass 时,subclass 继承了所有的“class 方法”(在引号中,因为实际上没有这样的东西),但是实例变量不被继承(即 - class Class 实例的实例变量):
class A
@foo = 1
end
A.instance_variables #=> [:@foo]
class B < A; end
B.instance_variables #=> []
这意味着,如果您在 model
方法中创建的匿名 class 上调用 setter,它只会在 class 上设置实例变量.匿名 class 的子 classes 不会继承它。你可以看到这会起作用:
Enumeration = MySequel::MyModel.model(:enumerations)
EnumerationValue = MySequel::MyModel.model(:enumeration_values)
Enumeration.table_name #=> :enumerations
EnumerationValue.table_name #=> :enumeration_values
老实说,将您的匿名 class 转入此处确实没有太大收获。只有当您还缓存了那些 classes 并且对该方法的单独调用两次返回相同的 Class 对象时,这才有意义。
如果你仍然希望 table_name
是可继承的,你需要对此非常明确,而不是使用 attr_accessor
,你需要直接查看 superclass :
attr_setter :table_name
def table_name
@table_name || (superclass.table_name if superclass.respond_to?(:table_name))
end
您可能需要在此处考虑一些调整。以上版本
table 名称在定义 Enumeration 或 EnumerationValue 时设置,并保存在父 class(匿名 class)中。
与 javascript.
的原型继承相比,就我个人而言,掌握这个概念要简单得多
可以通过Enumeration和EnumerationValue的superclass访问class。
# frozen_string_literal: true
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
end
extend ClassMethods
end
end
class Enumeration < MySequel::MyModel.model(:enumerations); end
class EnumerationValue < MySequel::MyModel.model(:enumeration_values); end
p Enumeration.superclass.table_name # prints :enumerations
p EnumerationValue.superclass.table_name # prints :enumeration_values
The Sequel library is seemingly propagating the information about the dataset set through call to module method to the child class
是的,的确如此。 Sequel 使用 inherited
callback to copy the class instance variables over to the subclass (see lib/sequel/model/base.rb#843
).
这是您的示例的一个非常基本的版本 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
因此,子classes 将其 class 实例变量(此处 @table_name
)设置为与(匿名)父 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
我是 ruby 的新手,正在尝试弄清楚下面 Sequel gem 中的 class 声明是如何在幕后工作的。
class Enumeration < Sequel::Model(DB[:enumerations]); end
快速调查 Sequel gem 的代码后,在我看来模块方法 Sequel::Model return 是一个 class 实例配置 class 属性。 Class的return实例然后在继承层次中使用,所以我试图通过代码测试我的理解;
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
根据我的理解,class 变量 table_name 在创建 Class 的实例时被设置并传播到它的子 class。然而,似乎并非如此。
有人可以解释 Sequel::Model 背后的概念以及我的示例实现中的问题以获得相同的结果。
此示例中的行为类似于使用上面的匿名 classes 时的行为。
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
Sequel 库似乎通过调用模块方法向子 class 传播有关数据集集的信息(通过匿名 class。)
但是,我想不通; Ruby 语言的什么构造允许下面的状态 class 知道它仅限于 table 枚举中名称设置为 'status'.
的记录class Status < Sequel::Model(DB[:enumerations].where(name: 'status')); end
因此,大部分代码都是正确的,您对它的工作原理的假设也是正确的。真是太棒了,绝对不是ruby的新手级别!
所以,什么不起作用。当你定义一些 class 的 subclass 时,subclass 继承了所有的“class 方法”(在引号中,因为实际上没有这样的东西),但是实例变量不被继承(即 - class Class 实例的实例变量):
class A
@foo = 1
end
A.instance_variables #=> [:@foo]
class B < A; end
B.instance_variables #=> []
这意味着,如果您在 model
方法中创建的匿名 class 上调用 setter,它只会在 class 上设置实例变量.匿名 class 的子 classes 不会继承它。你可以看到这会起作用:
Enumeration = MySequel::MyModel.model(:enumerations)
EnumerationValue = MySequel::MyModel.model(:enumeration_values)
Enumeration.table_name #=> :enumerations
EnumerationValue.table_name #=> :enumeration_values
老实说,将您的匿名 class 转入此处确实没有太大收获。只有当您还缓存了那些 classes 并且对该方法的单独调用两次返回相同的 Class 对象时,这才有意义。
如果你仍然希望 table_name
是可继承的,你需要对此非常明确,而不是使用 attr_accessor
,你需要直接查看 superclass :
attr_setter :table_name
def table_name
@table_name || (superclass.table_name if superclass.respond_to?(:table_name))
end
您可能需要在此处考虑一些调整。以上版本
table 名称在定义 Enumeration 或 EnumerationValue 时设置,并保存在父 class(匿名 class)中。
与 javascript.
的原型继承相比,就我个人而言,掌握这个概念要简单得多可以通过Enumeration和EnumerationValue的superclass访问class。
# frozen_string_literal: true
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
end
extend ClassMethods
end
end
class Enumeration < MySequel::MyModel.model(:enumerations); end
class EnumerationValue < MySequel::MyModel.model(:enumeration_values); end
p Enumeration.superclass.table_name # prints :enumerations
p EnumerationValue.superclass.table_name # prints :enumeration_values
The Sequel library is seemingly propagating the information about the dataset set through call to module method to the child class
是的,的确如此。 Sequel 使用 inherited
callback to copy the class instance variables over to the subclass (see lib/sequel/model/base.rb#843
).
这是您的示例的一个非常基本的版本 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
因此,子classes 将其 class 实例变量(此处 @table_name
)设置为与(匿名)父 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