假设我们有一个 Virtus 模型 User
class User
include Virtus.model
attribute :name, String, default: 'John', lazy: true
end
然后我们创建该模型的一个实例并从 Virtus.model
扩展以动态添加另一个属性:
user = User.new
user.extend(Virtus.model)
user.attribute(:active, Virtus::Attribute::Boolean, default: true, lazy: true)
当前输出:
user.active? # => true
user.name # => 'John'
但是当我尝试获取 attributes
或通过 as_json
(或 to_json
)或 Hash< 将对象转换为 JSON 时
通过 to_h
我只得到后扩展属性 active
:
user.to_h # => { active: true }
是什么导致了这个问题,我怎样才能在不丢失数据的情况下转换对象?
附言
我找到了 github issue , 但似乎终究没有修复(那里推荐的方法也不太稳定)。
最佳答案
根据 Adrian 的发现,这里有一种方法可以修改 Virtus 以实现您想要的功能。所有规范都通过此修改。
从本质上讲,Virtus 已经有了父级 AttributeSet
的概念,但只有在 class 中包含 Virtus.model
时才会这样。 .
我们也可以扩展它以考虑实例,甚至允许在同一对象中使用多个 extend(Virtus.model)
(尽管这听起来不是最优的):
require 'virtus'
module Virtus
class AttributeSet
def self.create(descendant)
if descendant.respond_to?(:superclass) && descendant.superclass.respond_to?(:attribute_set)
parent = descendant.superclass.public_send(:attribute_set)
elsif !descendant.is_a?(Module)
if descendant.respond_to?(:attribute_set, true) && descendant.send(:attribute_set)
parent = descendant.send(:attribute_set)
elsif descendant.class.respond_to?(:attribute_set)
parent = descendant.class.attribute_set
end
end
descendant.instance_variable_set('@attribute_set', AttributeSet.new(parent))
end
end
end
class User
include Virtus.model
attribute :name, String, default: 'John', lazy: true
end
user = User.new
user.extend(Virtus.model)
user.attribute(:active, Virtus::Attribute::Boolean, default: true, lazy: true)
p user.to_h # => {:name=>"John", :active=>true}
user.extend(Virtus.model) # useless, but to show it works too
user.attribute(:foo, Virtus::Attribute::Boolean, default: false, lazy: true)
p user.to_h # => {:name=>"John", :active=>true, :foo=>false}
也许这值得为 Virtus 做一个 PR,你怎么看?
关于ruby-on-rails - 动态扩展 Virtus 实例属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44046603/