我需要能够创建一个基于Virtus的对象,接受一个哈希或字符串。如果它是一个哈希,那么正常的行为就是完美的。如果它是一个普通字符串,那么我需要将它转换为{'id' => "STRING"}。此时,我不确定如何/在哪里重写initialize方法来执行此功能。或者也许有一种不同的方式。非常感谢您的专业知识。
class Contact
include Virtus.model
attribute :id, String
end
class Account
include Virtus.model
attribute :id, String
attribute :contact, Contact
attribute :name, String
end
account = Account.new("1234")
account.id #>1234
# and still work like this
account = Account.new(id: '1234', contact: '123456', name: 'Bob Jones')
account.id #>1234
account.contact.id #>123456
account.name #>Bob Jones样本数据
{'id' => '1234', 'contact' => '123456', 'name' => 'Bob Jones'}所以在联系人和客户之间。我需要它们能够用一个字符串初始化,这个字符串填充了@id参数。
发布于 2017-10-26 03:00:06
Virtus’ author’s opinion的问题是你不应该使用Virtus进行清理。正如你从@engineersmnky的回答中看到的,覆盖initialize方法需要更多的工作。
相反,包装你传入的对象会更容易:
class Hashable
def initialize(hashable_object)
@obj = hashable_object
end
def to_hash
case @obj
when Hash then @obj
when String
{ "id" => @obj }
else
raise "No conversion from #{@obj.class} to hash"
end
end
end
Account.new(Hashable.new(relationship))发布于 2017-10-26 02:44:29
注意:这仅用于启迪目的,可能会产生难以调试的意外后果。
如果您确实想这样做,您可以像这样覆盖Virtus::InstanceMethods::Constructor:
module VirtusOverride
def self.included(base)
raise "#{base.name} must include Virtus.model prior to including VirtusOverride" unless base.included_modules.include?(Virtus::InstanceMethods)
end
def initialize(*attributes)
super(construct(attributes))
end
private
def construct(attributes)
return attributes.first if valid_constructor?(attributes)
build_attributes_from_array(attributes)
end
def valid_constructor?(attributes)
return false unless attributes.count == 1
constructor = attributes.first
constructor.is_a?(Hash) &&
!(attribute_set.flat_map {|a| [a.name,a.name.to_s]} & constructor.keys).empty?
end
def build_attributes_from_array(attributes)
attribute_set.map(&:name).zip(attributes).to_h
end
end 然后根据需要将其包括在内
class Account
include Virtus.model
include VirtusOverride
attribute :id, String
attribute :contact, Hash
end 现在,您可以将选项positional传递给属性定义(例如Account.new(id,contact))或作为Hash传递。
示例:
Account.new("1234",{name: 'mnky'})
#=> #<Account:0x2a071b8 @id="1234", @contact={:name=>"mnky"}>
Account.new(id: "1234", contact: {name: 'mnky'})
#=> #<Account:0x2b42b78 @id="1234", @contact={:name=>"mnky"}>你可以给Virtus::InstanceMethods::Constructor打补丁来实现同样的效果,但我并不是一个很支持这种哲学的人,因为这可能会给其他开发人员带来困惑,因为模块的包含提供了粒度和清晰度。
更新
class Account
include Virtus.model
include VirtusOverride
attribute :id, String
attribute :contact, Contact
end
class Contact
include Virtus.model
include VirtusOverride
attribute :id, String
end
Account.new(id: '1234', contact: '123456', name: 'Bob Jones')
#=> #<Account:0x2bcb060 @id="1234", @contact=#<Contact:0x2bc9c50 @id="123456">, @name="Bob Jones">
Account.new('1234', '123456', 'Bob Jones')
#=> #<Account:0x2faea00 @id="1234", @contact=#<Contact:0x2fae880 @id="123456">, @name="Bob Jones">
Account.new('id' => '1234', 'contact' => '123456', 'name' => 'Bob Jones')
#=> #<Account:0x2faffc0 @id="1234", @contact=#<Contact:0x2fafb70 @id="123456">, @name="Bob Jones">https://stackoverflow.com/questions/46938598
复制相似问题