我需要从数据库中的各个表中检索列名,以便为这些表对应的类动态设置attr_accessor
方法。这需要从父类完成。
class Child < Parent
attr_accessor :id, :column_a, :etc.
end
Parent
&#39; #column_names
方法确实按我打算的方式工作。它返回子类指定的任何表的列名数组。但是,在从#inherited
方法调用时,它不会返回任何列名。当我尝试为子类设置attr_accessor
方法时,返回一个空数组。
我正在尝试动态设置这些attr_accessor
方法,使用#class_eval
将它们插入子类。这是对#column_names
的调用发生的地方以及返回空数组的位置。
module SomeModule
class Parent
def self.inherited(child)
child.class_eval do
self.column_names.each { |att| attr_accessor att.to_sym }
end
end
#...
def self.table_name
"#{self.to_s.downcase}s"
end
def self.column_names
sql = "#... WHERE table_name = '#{table_name}';"
columns = []
exec(sql).each do |col-data|
columns << col_data["column_name"]
end
columns
end
def self.exec(sql, args=[])
#...
end
#...
end
end
我的理解是,这应该基本上创建一个从Child
到Child.column_names
的来电,我认为这样做。我只是不知道为什么没有填充返回数组。我已尝试移动column_names
方法,但行为的唯一变化(取决于放置的位置)是子类的NoMethodError
。
答案 0 :(得分:0)
不幸的是,inherited上的文档并不详细,但我看到问题在这里 - 它是self
class_eval
的价值块。
NoMethodError是因为未在子类上定义column_names
方法,而self引用class_eval
块内的子类,
这是一个简单的修复,在self
阻止之前将class_eval
设置为变量。
希望以下示例清楚,我测试并运行。请注意,与您的代码唯一的真正区别在于inherited
方法。所有其他的东西都只是测试它,因为我无法按原样运行你的代码。
require 'byebug'
class ParentClass
def self.inherited(child)
parent_class = self # Store value of self in a variable
# because it will change in class_eval
# Then call column_names on this variable
child.class_eval do
parent_class.column_names.each { |attr| attr_accessor attr.to_sym }
end
end
def self.column_names
[:foo, :bar]
end
end
class ChildClass < ParentClass
def initialize
end
end
child = ChildClass.new
child.foo = "bar"
puts child.foo
# => "bar"
这个问题在javascript中更常见,javascript具有更严格的隔离范围级别。换句话说,self
的值更改频繁。