【问题标题】:Mongoid field hash as StructMongoid 字段哈希为 Struct
【发布时间】:2017-01-29 12:09:14
【问题描述】:

是否可以将 mongoid field 配置为反序列化为 Struct 而不是 Hash ? (with defaults)

我的用例:一家公司的订阅计划以哈希形式存储在我的模型中。

以前作为哈希

class Company
  include Mongoid::Document
  field :subscription, type: Hash, default: {
      ends_at: 0,
      quantity: 0,
      started_at: 0,
      cancelled: false,
    }

我希望我不必写Company.first.subscription[:ends_at],我宁愿写Company.subscription.ends_at

我认为下面的方法会更好

class Company
  include Mongoid::Document
  field :subscription_plan, type: Struct, default: Struct.new(
    :ends_at, :quantity, :started_at, :cancelled
  ) do
    def initialize(
      ends_at: nil, 
      quantity: 0,
      starts_at: nil,
      cancelled: false
    ); super end
  end
end

如果计划可以在一个类中定义就更好了

class SubscriptionPlan < Struct.new(
  ends_at, :quantity, :starts_at, :cancelled
) do
  def initialize(
  ends_at: nil, 
  quantity: 0,
  starts_at: nil,
  cancelled: false
); super; end
end

class Company
  field :subscription_plan, type: SubscriptionPlan, default: SubscriptionPlan.new
end

我怎样才能让它工作?

【问题讨论】:

  • 你试过我的答案了吗?

标签: ruby serialization deserialization ruby-on-rails-5 mongoid6


【解决方案1】:

对此持保留态度,因为我从未使用过 MongoDB 或 Mongoid。 尽管如此,在谷歌上搜索“自定义类型”还是把我带到了这个documentation

这是自定义类型示例的改编版本:

class SubscriptionPlan

  attr_reader :ends_at, :quantity, :started_at, :cancelled

  def initialize(ends_at = 0, quantity = 0, started_at = 0, cancelled = false)
    @ends_at = ends_at
    @quantity = quantity
    @started_at = started_at
    @cancelled = cancelled
  end

  # Converts an object of this instance into a database friendly value.
  def mongoize
    [ends_at, quantity, started_at, cancelled]
  end

  class << self

    # Get the object as it was stored in the database, and instantiate
    # this custom class from it.
    def demongoize(array)
      SubscriptionPlan.new(*array)
    end

    # Takes any possible object and converts it to how it would be
    # stored in the database.
    def mongoize(object)
      case object
      when SubscriptionPlan then object.mongoize
      when Hash then SubscriptionPlan.new(object.values_at(:ends_at, :quantity, :started_at, :cancelled)).mongoize
      else object
      end
    end

    # Converts the object that was supplied to a criteria and converts it
    # into a database friendly form.
    def evolve(object)
      case object
      when SubscriptionPlan then object.mongoize
      else object
      end
    end
  end
end

class Company
  include Mongoid::Document
  field :subscription, type: SubscriptionPlan, default: SubscriptionPlan.new
end

这应该会让你更接近你想做的事情。

请注意,默认SubscriptionPlan 将由每个具有默认值的公司共享。如果您修改一家公司的默认计划,可能会导致一些奇怪的错误。

【讨论】:

  • 您好,我会尝试这样做,但请注意您提供了指向非常过时的 Mongoid 文档的链接(我认为是版本 2 或其他内容)。我正在使用 mongoid 边缘(mongoid 6 / MongoDB 3.x)。网址中甚至还有old
  • 你是对的。我没有检查,我只是使用了在 Google 上找到的第一个链接。
  • 我很幸运,文档似乎自老 mongoid 以来没有改变。
  • 嘿,我的代码似乎取得了进展。但是在一些序列化/反序列化过程中,我得到了NoMethodError: undefined method bson_type'`。我不确定这个方法是什么。
【解决方案2】:

我意识到我只是在重新实现一个没有 ID 的嵌套文档。最后,我决定为我的subscription 切换到一个普通的嵌入式文档,因为有一个额外的 ID 字段不是问题,而且我得到了 mongoid 范围作为奖励。如果我想支持任何键,我可以随时添加Mongoid::Attributes::Dynamic

尽管如此,对于想要创建自己的类型的人来说,问题和其他答案仍然相关。

【讨论】:

    猜你喜欢
    • 2011-05-13
    • 2016-07-19
    • 2014-01-04
    • 1970-01-01
    • 2017-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-29
    相关资源
    最近更新 更多