假设我们有一个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通过to_h将对象转换为JSON时,我仅获得后扩展属性active:
user.to_h # => { active: true }
是什么引起问题,如何在不丢失数据的情况下转换对象?
P.S.
我已经找到了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进行公关,您如何看待?

关于ruby-on-rails - 动态扩展Virtus实例属性,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/44046603/

10-13 02:13