web-dev-qa-db-ja.com

ActiveRecord以外のオブジェクトでactive_model_serializerを使用する

ActiveRecord :: Baseの子孫ではなく、データベースに保存されていないモデルオブジェクトがあります。そのためのシリアライザー(同じ名前+「Serializer」)を作成し、コントローラーでrender json: object_instanceを呼び出しています。

結果は、renderの奥深くからの例外です。

シリアライザーをインスタンス化して呼び出すas_jsonメソッドを実装しましたが、その結果、オブジェクトread_attribute_for_serializationにメソッドがありません。

少なくともActiveModel Serializerに関しては、オブジェクトをActiveModel互換オブジェクトのように動作させたいのですが、ドキュメントにこれへの参照がありません。

32
Robin Daugherty

モデルオブジェクトには、次のように含める必要があります。

# active_model_serializers 0.10+
class ModelName
  include ActiveModel::Serialization
end

# active_model_serializers < 0.10
class ModelName
  include ActiveModel::SerializerSupport
end

これにより、オブジェクト内で必要なメソッドが実装され、オブジェクト名に一致するシリアライザーが自動検出されるため、ActiveRecordオブジェクトと同じように透過的に使用できます。

これは、active_model_serializersバージョン0.8.1から少なくとも0.9.3で機能します。

54
Robin Daugherty

Mutexkidの回答に続いて、active_model_serializers 0.10.0.rc4を使用して、単純な古いRubyオブジェクトをシリアライザーで適切に再生するには、次のものが必要です。

ポロ:

class SearchResult
  include ActiveModel::Serialization

  attr_reader :stories, :users, :friends

  def initialize(search:)
    @stories = search.stories
    @users = search.users
    @friends = search.friends
  end
end

シリアライザー:

class SearchResultSerializer < ActiveModel::Serializer
  attributes :stories, :users, :friends
end
12
b73

昨日、Rails 4.2にアップグレードしているときにこれに遭遇しました。ActiveModel::SerializerSupportは0.10-stableで削除されました。結局、POROにエイリアスを追加するだけでうまくいくようです。

  alias :read_attribute_for_serialization :send

(active_model_serializers 0.10.0.rc2)

また、クラスにinclude ActiveModel::Modelを追加する必要があります。

7
Karl Wilbur

active_model_serializers0.10.xおよびRails 4.2の時点で、アクティブなモデルシリアライザーでプレーンな古いRubyオブジェクトを使用するために必要なものは次のとおりです。

プレーンオールド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
5
mutexkid

私のアカウントは、Karl Wilburの回答にコメントするには新しすぎますが、ハッシュオブジェクトを渡してシリアル化できるように、追加したコードを次に示します。

class SerializableHash < Hash
  alias :read_attribute_for_serialization :[]
end

使用法:

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

@ratbeardの回答を拡張すると、以下はactive_model_serializerバージョン0.9.x以降で機能します。私は、POROシリアライズ可能ハッシュオブジェクトをRailsの外部のactivemodelシリアライザーと連携させるために必要な最小限のactive_supportクラスとactive_modelクラスを含めました。最適化としての私の目的のために、アプリの他の場所で計算された、事前に計算されたシリアル化されたオブジェクトの配列を渡します。再計算する代わりに、ActiveModel :: Serializerサブクラスの初期化子をオーバーロードして、使用可能な場合はそれを渡すことができます。

lib/serializable_hash.rb

class SerializableHash < Hash
  alias :read_attribute_for_serialization :[]
end

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)
    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

完全を期すために、シリアル化可能なハッシュの代わりにインスタンス変数を使用するこの代替バージョンも含めます。

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

次に、トップレベルオブジェクトのシリアル化を使用するには、次のようにします。

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
0
Jesse Sanford