2014-02-11 12 views
28

Ich habe ein Modellobjekt, das nicht von ActiveRecord :: Base abstammt und nicht in der Datenbank gespeichert ist. Ich habe einen Serializer dafür erstellt (mit dem gleichen Namen + "Serializer"), und in meinem Controller rufe ich render json: object_instance.Verwenden Sie Active_Model_Serializer mit einem Nicht-ActiveRecord-Objekt

Das Ergebnis ist eine Ausnahme von tief im Inneren von render.

Ich implementierte eine as_json Methode, die den Serializer instanziiert und aufruft, aber das Ergebnis ist dann eine fehlende Methode im Objekt read_attribute_for_serialization.

Ich möchte, dass sich mein Objekt wie ein ActiveModel-kompatibles Objekt verhält, zumindest so weit wie Active Model Serializer geht, aber ich sehe in seiner Dokumentation keinen Hinweis darauf.

Antwort

50

Nach Stunden fand ich endlich die Lösung. Das Modellobjekt muss es gehören thusly:

class ModelName 
    include ActiveModel::SerializerSupport 
end 

Diese Methoden implementiert innerhalb des Objekts benötigt, und auch erkennt automatisch die Serializer den Objektnamen übereinstimmt, so dass es transparent wie ein Activerecord-Objekt verwendet werden kann.

Dies funktioniert mit active_model_serializers Version 0.8.1 bis 0.9.3.

+3

Sie sollten Ihre eigene Antwort akzeptieren, da sie korrekt zu sein scheint. :) –

+0

Hey Robin können Sie ein Beispiel für Ihr Objekt für Menschen, die etwas ähnliches von Grund auf neu machen wollen? (Das ist ein nicht aktives Record-Objekt, das die Serializer-Unterstützung enthält) – Will

+0

Haben Sie es geschafft, ein Array verschachtelter serialisierter Objekte innerhalb des Modells zu serialisieren? – Chris

7

Ich trat gestern bei der Aktualisierung auf Rails 4.2. ActiveModel::SerializerSupport wurde für 0,10-stabil entfernt. Ich landete nur ein Alias ​​für meine Poros Zugabe, die den Trick zu tun schien:

alias :read_attribute_for_serialization :send 

(active_model_serializers 0.10.0.rc2)

Sie umfassen auch ActiveModel::Model in Ihrer Klasse hinzufügen müssen.

3

Mein Konto ist zu neu auf Karl Wilbur Antwort zu kommentieren, aber heres der Code, den ich so habe ich in einer Hash-Objekt übergeben konnte serialisiert:

class SerializableHash < Hash 
    alias :read_attribute_for_serialization :[] 
end 

Verbrauch:

o = SerializableHash.new(user: current_user, account: current_account) 
    respond_with(o, serializer: SettingsSerializer) 
0

Ausbau Nach @ Ratbeards Antwort funktioniert das Folgende mit jeder active_model_serializer Version 0.9.x und höher. Ich habe die minimale Menge an active_support und active_model-Klassen hinzugefügt, die benötigt werden, um das serialisierbare PORO-Hash-Objekt mit activemodel-Serializern außerhalb von Rails arbeiten zu lassen. Für meine Zwecke als Optimierung übergebe ich ein vorberechnetes serialisiertes Array von Objekten, das an anderer Stelle in der App berechnet wird. Anstatt die Daten neu zu berechnen, können Sie den Initialisierer in der ActiveModel :: Serializer-Unterklasse überladen, um ihn weiterzugeben, wenn er verfügbar ist.

lib/serializable_hash.rb

class SerializableHash < Hash 
    alias :read_attribute_for_serialization :[] 
end 

app/Serializer/E-Mail/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 

Der Vollständigkeit halber füge ich auch diese alternative Version, die eine Instanz Variable statt serializable Hash verwendet:

app/Serializer/E-Mail/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 

Dann Um das Objekt der obersten Ebene zu serialisieren, würden Sie einfach

require 'app/serializers/email/foo_serializer' 
require 'app/serializers/email/bar_serializer' 
Email::FooSerializer.new(Foo.first).as_json 

oder wenn Sie die vorher berechnete serialisierten Reihe von Bars verwenden wollen:

# 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 
+1

'Klasse SerializableHash BF4

3

ab active_model_serializers 0.10.x und Rails 4.2, hier ist es, was erforderlich verwenden, um ein einfaches altes Ruby-Objekt mit aktivem Modell Serializer:

Plain alten Rubin Objekt:

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 

Serializer:

class SearchResultSerializer < ActiveModel::Serializer 
    attributes :stories, :users, :friends 
end 
+0

mit 'active_model_serializers 0.10.0.rc4: Ich fand viel von dieser Einrichtung unnötig, siehe meine Antwort unten. Ich habe eine separate Antwort hinzugefügt, da Kommentare nicht zu gut mit Codeblöcken umgehen: – b73

10

Im Anschluss an mutexkid Antwort, mit active_model_serializers 0.10.0.rc4, ist Folgendes erforderlich ein einfaches altes Ruby-Objekt zu machen mit einem Serializer schön spielen:

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 
+0

Dies war mit Rails 4.0.13 – b73

+0

Dies ist auch dokumentiert (könnte mehr offensichtlich sein) und in der 0.10 linged. Serie: https://github.com/rails-api/active_model_serializers/blob/e1d1a3dbf969cfa48d91e978c8c7fd7e2f098e7c/docs/general/rendering.md#serializing-non-activerecord-objects https://github.com/rails-api/active_model_serializers/blob /e1d1a3dbf969cfa48d91e978c8c7fd7e2f098e7c/docs/ARCHITECTURE.md#what-does-a-serializable-resource-look-like – BF4

+0

Funktioniert gut mit Rails 5.0.1 und AMS 0.10.4 –