Mongoid字段哈希为Struct

时间:2017-01-29 12:09:14

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

是否可以将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

我怎样才能让它发挥作用?

2 个答案:

答案 0 :(得分:1)

带上一粒盐,因为我从未使用过MongoDB或Mongoid。 仍然,谷歌搜索&#34;自定义类型&#34;把我带到了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。如果您修改一家公司的默认计划,可能会导致一些奇怪的错误。

答案 1 :(得分:0)

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

然而,问题和其他答案仍然适用于想要创建自己的类型的人。