thr*_*801 6 ruby activerecord attributes metaprogramming ruby-on-rails
所以,以为我昨晚有这个工作,可以发誓.现在它没有任何问题,我认为有时间寻求帮助.
我在数据库中定义动态字段,半EAV风格,现在让我们说清楚我不在乎听到你对EAV是否是个好主意的意见:)
不管怎么说Im做了一点不同于香港专业教育学院在过去所做的,基本上如果添加的属性(或域),是,我创建了一个附加列到一个特定的属性表迁移并运行它(或者删除一个) - 反正,因为在中间有一个类别层,它是定义所有属性的直接关系,我不能使用实际的属性名称作为列名,因为属性是特定于类别的.
所以,如果它可以帮助你想象
Entity
belongs_to :category
Category
has_many :entities
EntityAttribute
belongs_to :category
EntityAttributeValue
belongs_to :entity_attribute
belongs_to :entity
Run Code Online (Sandbox Code Playgroud)
EAV表在创建新属性时水平跨越,其中列标记为attribute_1 attribute_2,其中包含该特定实体的值.
无论如何 - 我试图让方法在实体模型上动态化,所以我可以调用@ entity.actual_attribute_name,而不是@ entity.entity_attribute_value.field_5
这是我认为正在运行的代码 -
def method_missing(method, *args)
return if self.project_category.blank?
puts "Sorry, I don't have #{method}, let me try to find a dynamic one."
puts "let me try to find a dynamic one"
keys = self.project_category.dynamic_fields.collect {|o| o.name.to_sym }
if keys.include?(method)
field = self.project_category.dynamic_fields.select { |field| field.name.to_sym == method.to_sym && field.project_category.id == self.project_category.id }.first
fields = self.project_category.dynamic_field_values.select {|field| field.name.to_sym == method }
self.project_category_field_value.send("field_#{field.id}".to_sym, *args)
end
end
Run Code Online (Sandbox Code Playgroud)
然后今天我回到了代码,我意识到虽然我可以设置在轨道控制台的属性,它会返回正确的字段,当我保存后,EntityAttributeValue没有被更新的记录(表示为self.project_category_field_value,上面. )
寻找到它之后,所以进一步的它看起来像我不得不添加before_update或before_save回调手动保存的属性,多数民众赞成在我注意到,在回调,将重新运行method_missing的回调,因为如果该对象被复制(而新对象是原始对象的副本),或者其他什么,我不太确定.但是在保存过程中或之前的某个时刻,我的属性消失了.
所以,嗯,我想我一半的方式回答了我自己的问题打字出来后,我需要设置一个实例变量,并检查它是否存在于我的method_missing方法的beginningish(右?)也许这不是发生了什么事我不知道,但我也问是否有更好的方法来做我想做的事情.
如果使用method_missing是一个坏主意,请解释原因,因为通过关于方法缺失的帖子,我听到有人抨击它,但没有一个人打扰提供一个合理的解释为什么方法丢失是一个坏的解决方案.
提前致谢.
这是该部门正在进行的一些非常紧张的计划method_missing。你应该拥有的更像是这样的:
def method_missing(name, *args)
if (method_name = dynamic_attribute_method_for(name))
method_name.send(*args)
else
super
end
end
Run Code Online (Sandbox Code Playgroud)
然后您可以尝试将其分为两部分。第一个是创建一个方法,决定是否可以处理具有给定名称的调用(此处)dynamic_attribute_method_for,第二个是所讨论的实际方法。前者的工作是确保后者在调用时正常工作,可能是define_method为了避免下次访问相同的方法名称时再次经历所有这些。
该方法可能如下所示:
def dynamic_attribute_method_for(name)
dynamic_attributes = ...
type = :reader
attribute_name = name.to_s.sub(/=$/) do
type = :writer
''
end
unless (dynamic_attributes.include?(attribute_name))
return
end
case (type)
when :writer
define_method(name) do |value|
# Whatever you need
end
else
define_method(name) do
# Whatever you need
end
end
name
end
Run Code Online (Sandbox Code Playgroud)
我无法判断您的方法中发生了什么,因为结构不清楚,并且它似乎高度依赖于您的应用程序的上下文。
从设计的角度来看,您可能会发现制作一个封装所有这些功能的专用包装类更容易。object.attribute_name您无需调用,而是调用object.dynamic_attributes.attribute_name在这种情况下dynamic_attributes按需创建的位置:
def dynamic_attributes
@dynamic_attributes ||= DynamicAccessor.new(self)
end
Run Code Online (Sandbox Code Playgroud)
当该对象初始化时,它将使用所需的任何方法预先配置自身,并且您不必处理该方法缺少的内容。