我在创建包含数组自定义对象的Mongoid文档时遇到了一些问题。
我的具体情况我打算存储一组BaseDevice
个对象。 BaseDevice
类已经使用Mongoid的custom fields支持进行了分类和序列化。这在单个对象上非常有效。
为了存储BaseDevice
的数组,我创建了以下类:
class BaseDeviceArray < Array
class << self
def demongoize(object)
object ? object.map{ |obj| BaseDevice.demongoize obj } : new
end
def evolve(object)
case
when BaseDeviceArray then object.mongoize
else object
end
end
end
def mongoize
self.map(&:mongoize)
end
end
mongoid文档看起来像这样
class MongoPeriph
include Mongoid::Document
field :devices, type: BaseDeviceArray
end
假设some_devices
是一个包含两个BaseDevice
个实例的数组。
会发生以下情况:当我将some_devices
分配给正常工作的MongoPeriph
实例的设备字段时。
mp = MongoPeriph.create
mp.devices = some_devices
mp.devices # => [#<BaseDevice:0x007fa84bac0080>,#<BaseDevice:0x007fa84baaff78>]
当尝试将push
,pop
,shift
,unshift
方法发送到mongoid文档中的设备字段时,似乎什么也没发生。更改未显示在mp
对象上。此外,当通过索引引用其中一个对象时(即,在调用mp.devices[0].some_method
时),世界不会改变。
当从数组中弹出对象时,在每个pop
上都会给出一个新对象。这是预期的,因为反序列化器为每个BaseDevice
实例化一个新的pop
对象,但内部字段没有更新,即对象停留在那里,一个可以无休止地弹出。
使用与mongoid文档分开的BaseDeviceArray
按预期工作:
foo = BaseDeviceArray.new
foo << BaseDevice.new
导致带有BaseDevice对象的数组。
顺便说一下。我在网上找到了另一个approach。这是实现我需要的更通用的方式,但它是 monkey-patches Mongoid。我试图避免的东西。此外,该解决方案似乎与我的方法有相同的问题。
答案 0 :(得分:1)
您的代码中的问题是您拥有#mongoize
(实例)方法,但实际上您需要::mongoize
(类)方法。你永远不会创建BaseDeviceArray
的实例,因此实例方法是无用的。
以下是我如何使用::mongoize
方法实现我在mongo Hash
中使用带有数组值的单个键的示例。另外,我想将结果数组放入一个带有ids的哈希值作为键,以便于查找。
def demongoize(hash)
return validate_hash(hash)["TestRecord"].each_with_object({}) do |r, m|
rec = TestRecord.new(r)
m[rec.case_id] = rec
end
end
def mongoize(object)
case object
when Array then {"TestRecord" => object.map(&:mongoize)}
when Hash
if object["TestRecord"]
# this gets actually called when doing TestRun.new(hash)
mongoize(demongoize(object))
else
{"TestRecord" => object.values.map(&:mongoize)}
end
else raise("dunno how to convert #{object.class} into records JSON")
end
end
def evolve(object)
# can't see how we want to process this here yet
# docs.mongodb.com/ruby-driver/master/tutorials/6.0.0/mongoid-documents
object
end
我想很久以前就完成了op任务,但是觉得有人觉得它很有用。