Хэш монгоидного поля как структура
Можно ли настроить монгоид field
десериализовать как Struct
а не Hash
? ( со значениями по умолчанию)
Мой вариант использования: компания с планом подписки, хранящимся в моей модели в виде хэша.
Ранее как хеш
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 ответа
Возьмите это с крошкой соли, поскольку я никогда не использовал ни MongoDB, ни Mongoid. Тем не менее, поиск в Google для "нестандартного типа" привел меня к этой документации.
Вот адаптированная версия примера пользовательского типа:
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
будет предоставлен каждой компании по умолчанию. Это может привести к некоторым странным ошибкам, если вы измените план по умолчанию в одной компании.
Я понял, что просто переопределяю вложенный документ без идентификатора. В конце концов я решил перейти на обычный встроенный документ для моего subscription
, так как наличие дополнительного поля ID не проблема, и я получаю mongoid области в качестве бонуса. Я всегда могу добавить Mongoid::Attributes::Dynamic
в случае, если я хочу поддержать любой ключ.
Тем не менее вопрос и другой ответ остается актуальным для того, кто хочет создавать свои типы.