Использовать active_model_serializer с объектом без ActiveRecord
У меня есть объект модели, который не происходит из ActiveRecord:: Base и не сохраняется в базе данных. Я создал для него сериализатор (с тем же именем + "Сериализатор" ), и в моем контроллере я вызываю render json: object_instance
.
Результатом является исключение из глубины render
.
Я реализовал метод as_json
, который запускает сериализатор и вызывает его, но результатом является отсутствующий метод в объекте read_attribute_for_serialization
.
Я хочу, чтобы мой объект работал как объект, совместимый с ActiveModel, по крайней мере, по мере того, как работает Active Serializer, но я не вижу ссылки на это в своей документации.
Ответы
Ответ 1
Через несколько часов я наконец нашел решение. Объект модели должен включать его таким образом:
class ModelName
include ActiveModel::SerializerSupport
end
Это реализует методы, необходимые в объекте, а также автоматически обнаруживает сериализатор, соответствующий имени объекта, поэтому его можно использовать прозрачно, как объект ActiveRecord.
Это работает с active_model_serializers версии с 0.8.1 по 0.9.3.
Ответ 2
После ответа mutexkid, с active_model_serializers 0.10.0.rc4, требуется сделать простой старый объект с рубиновым эффектом с помощью сериализатора:
PORO:
class SearchResult
include ActiveModel::Serialization
attr_reader :stories, :users, :friends
def initialize(search:)
@stories = search.stories
@users = search.users
@friends = search.friends
end
end
Serializer:
class SearchResultSerializer < ActiveModel::Serializer
attributes :stories, :users, :friends
end
Ответ 3
Я столкнулся с этим вчера при обновлении до Rails 4.2. ActiveModel::SerializerSupport
был удален для 0.10-стабильной. Я закончил тем, что просто добавил псевдоним для своих POROs, которые, казалось, сделали трюк:
alias :read_attribute_for_serialization :send
(active_model_serializers 0.10.0.rc2)
Вам также нужно добавить include ActiveModel::Model
в свой класс.
Ответ 4
Моя учетная запись слишком новая, чтобы комментировать ответ Карла Уилбера, но вот код, который я добавил, чтобы передать сериализацию хеш-объекта:
class SerializableHash < Hash
alias :read_attribute_for_serialization :[]
end
Использование:
o = SerializableHash.new(user: current_user, account: current_account)
respond_with(o, serializer: SettingsSerializer)
Ответ 5
по состоянию на active_model_serializers 0.10.x и Rails 4.2, здесь требуется использовать простой старый объект ruby с активными сериализаторами модели:
Обычный старый рубиновый объект:
class SearchResult
extend ActiveModel::Naming
include ActiveModel::Serialization
attr_accessor :stories, :users, :friends
def id
hash
end
def initialize(search:)
@stories = search.stories
@users = search.users
@friends = search.friends
end
def attributes
{ stories: stories,
users: users,
friends: friends
}
end
end
Сериализатор:
class SearchResultSerializer < ActiveModel::Serializer
attributes :stories, :users, :friends
end
Ответ 6
Развернувшись на @ratbeard, ответьте на следующие действия с любым active_model_serializer версии 0.9.x и выше. Я включил минимальное количество классов active_support и active_model, необходимых для того, чтобы получить сериализуемый хеш-объект PORO для работы с сериализаторами activemodel вне рельсов. Для моих целей в качестве оптимизации я передаю предварительно вычисленный сериализованный массив объектов, который вычисляется в другом месте приложения. Вместо повторного вычисления вы можете перегрузить инициализатор в подклассе ActiveModel:: Serializer, чтобы передать его, если он доступен.
Библиотека /serializable _hash.rb
class SerializableHash < Hash
alias :read_attribute_for_serialization :[]
end
приложение/сериализаторов/электронная почта /foo _serializer.rb
require 'active_model/serializer'
require 'active_support/core_ext/object'
require 'active_support/inflector'
require 'active_support/notifications'
class Email::FooSerializer < ActiveModel::Serializer
attributes :id, :bars, :created_at, :updated_at
def initialize(foo, precomputed_serialized_array_of_bars =nil)
unless serialized_bars
serialized_bars = {}
foo.bar.each do | bar |
serialized_searches[bar.baz] ||= []
serialized_searches[bar.baz] << Email::BarSerializer.new(bar).attributes
end
end
super(SerializableHash[
id: foo.id,
bars: serialized_bars,
created_at: foo.created_at
updated_at: foo.updated_at
])
end
end
Для полноты я также включаю эту альтернативную версию, которая использует переменную экземпляра вместо сериализуемого хэша:
приложение/сериализаторов/электронная почта /foo _serializer.rb
require 'active_model/serializer'
require 'active_support/core_ext/object'
require 'active_support/inflector'
require 'active_support/notifications'
class Email::FooSerializer < ActiveModel::Serializer
attributes :id, :bars, :created_at, :updated_at
def initialize(foo, precomputed_serialized_array_of_bars=nil)
@bars = precomputed_serialized_array_of_bars
super(foo)
end
def bars
if @bars
return @bars
else
serialized_bars = {}
object.bars.each do | bar |
serialized_bars[bar.baz] ||= []
serialized_bars[bar.baz] << Email::BarSerializer.new(bar).attributes
end
return serialized_bars
end
end
end
Затем, чтобы использовать сериализацию объекта верхнего уровня, вы просто выполните
require 'app/serializers/email/foo_serializer'
require 'app/serializers/email/bar_serializer'
Email::FooSerializer.new(Foo.first).as_json
ИЛИ, если вы хотите использовать предварительно вычисленный сериализованный массив баров:
# pre-compute array_of_serialized_bars somewhere up here
require 'app/serializers/email/foo_serializer'
require 'app/serializers/email/bar_serializer'
Email::FooSerializer.new(Foo.first, array_of_pre_serialized_bars).as_json