ruby-on-rails-3.2 - custom - ruby on rails fast json
Use active_model_serializer con un objeto que no sea ActiveRecord (6)
Tengo un objeto modelo que no desciende de ActiveRecord :: Base y no está almacenado en la base de datos. Creé un serializador para él (con el mismo nombre + "Serializador"), y en mi controlador estoy llamando a render json: object_instance
.
El resultado es una excepción desde lo profundo del render
.
Implementé un método as_json
que as_json
una instancia del serializador y lo llama, pero el resultado es un método faltante en el objeto, read_attribute_for_serialization
.
Quiero que mi objeto actúe como un objeto compatible con ActiveModel al menos en lo que respecta al Active Model Serializer, pero no veo ninguna referencia a esto en su documentación.
Ampliando la respuesta de @ ratbeard, los siguientes trabajos funcionan con cualquier versión de Active_model_serializer 0.9.xy superior. Incluí la cantidad mínima de clases active_support y active_model necesarias para que el objeto hash serializable PORO funcione con los serializadores activemodel fuera de los rieles. A mis fines como una optimización, paso una matriz de objetos serializada previamente calculada en otra parte de la aplicación. En lugar de volver a calcular, puede sobrecargar el inicializador en la subclase ActiveModel :: Serializer para pasarlo si está disponible.
lib / serializable_hash.rb
class SerializableHash < Hash
alias :read_attribute_for_serialization :[]
end
aplicación / serializadores / correo electrónico / 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
Para completar, también incluyo esta versión alternativa que usa una variable de instancia en lugar del hash serializable:
app / serializers / email / 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
Luego, para usar serializar el objeto de nivel superior simplemente haría
require ''app/serializers/email/foo_serializer''
require ''app/serializers/email/bar_serializer''
Email::FooSerializer.new(Foo.first).as_json
O si desea utilizar la matriz serializada precomputada de barras:
# 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
El objeto modelo necesita incluirlo así:
class ModelName
include ActiveModel::SerializerSupport
end
Esto implementa los métodos necesarios dentro del objeto y también descubre automáticamente que el serializador coincide con el nombre del objeto, por lo que se puede usar de forma transparente como un objeto ActiveRecord.
Esto funciona con active_model_serializers versión 0.8.1 hasta al menos 0.9.3.
Me encontré con esto ayer mientras actualizaba a Rails 4.2. ActiveModel::SerializerSupport
se ha eliminado para 0.10-stable. Terminé simplemente agregando un alias a mi PORO que parecía hacer el truco:
alias :read_attribute_for_serialization :send
(active_model_serializers 0.10.0.rc2)
También deberá agregar include ActiveModel::Model
en su clase.
Mi cuenta es demasiado nueva para comentar sobre la respuesta de Karl Wilbur, pero aquí está el código que agregué para que pudiera pasar un objeto hash para serializar:
class SerializableHash < Hash
alias :read_attribute_for_serialization :[]
end
Uso:
o = SerializableHash.new(user: current_user, account: current_account)
respond_with(o, serializer: SettingsSerializer)
Siguiendo con la respuesta de mutexkid, con active_model_serializers 0.10.0.rc4, se requiere lo siguiente para hacer que un objeto Ruby antiguo se vea bien con un serializador:
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
Serializador:
class SearchResultSerializer < ActiveModel::Serializer
attributes :stories, :users, :friends
end
a partir de active_model_serializers 0.10.x y Rails 4.2, esto es lo que se requiere para usar un objeto Ruby antiguo y plano con serializadores modelo activos:
Objeto simple de rubí antiguo:
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
Serializador:
class SearchResultSerializer < ActiveModel::Serializer
attributes :stories, :users, :friends
end