48

I have a serializer

class FundingSerializer < ActiveModel::Serializer
  attributes :id, 

  has_one :user
  has_one :tournament
  embed :ids, include: true
end

That initializes with the proper associations

FundingSerializer.new(Funding.first).to_json

yields

"{\"users\":[{\"id\":2,\"first_name\":\"Nick\"}],\"tournaments\":[{\"id\":1,\"end_date\":\"2013-07-21T23:18:54.981Z\",\"start_date\":\"2013-07-14T23:18:54.980Z\"}],\"funding\":{\"id\":1}}"

but,

FundingSerializer.new(Funding.all).to_json

gets this error.

undefined method `read_attribute_for_serialization' for #<ActiveRecord::Relation::ActiveRecord_Relation_Funding:0x007f998910a250>
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/activerecord-4.0.0/lib/active_record/relation/delegation.rb:121:in `method_missing'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/activerecord-4.0.0/lib/active_record/relation/delegation.rb:68:in `method_missing'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/active_model_serializers-0.8.1/lib/active_model/serializer.rb:99:in `block in attribute'
    from (eval):3:in `_fast_attributes'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/active_model_serializers-0.8.1/lib/active_model/serializer.rb:466:in `rescue in attributes'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/active_model_serializers-0.8.1/lib/active_model/serializer.rb:454:in `attributes'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/active_model_serializers-0.8.1/lib/active_model/serializer.rb:478:in `_serializable_hash'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/active_model_serializers-0.8.1/lib/active_model/serializer.rb:360:in `serializable_hash'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/active_model_serializers-0.8.1/lib/active_model/serializer.rb:344:in `as_json'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/activesupport-4.0.0/lib/active_support/json/encoding.rb:50:in `block in encode'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/activesupport-4.0.0/lib/active_support/json/encoding.rb:81:in `check_for_circular_references'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/activesupport-4.0.0/lib/active_support/json/encoding.rb:49:in `encode'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/activesupport-4.0.0/lib/active_support/json/encoding.rb:34:in `encode'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/activesupport-4.0.0/lib/active_support/core_ext/object/to_json.rb:16:in `to_json'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/active_model_serializers-0.8.1/lib/active_model/serializer.rb:333:in `to_json'
    from (irb):1
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/railties-4.0.0/lib/rails/commands/console.rb:90:in `start'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/railties-4.0.0/lib/rails/commands/console.rb:9:in `start'
    from /Users/nicholasshook/.rvm/gems/ruby-2.0.0-p247@pokerfund/gems/railties-4.0.0/lib/rails/commands.rb:64:in `<top (required)>'

I do not want to simply render json: Funding.all because I would like to pass this json around to other objects in my rails application and with an angularjs app. Thanks,

shicholas
  • 6,123
  • 3
  • 27
  • 38
  • Although, [SandOnTeeth's answer](http://stackoverflow.com/a/39318636/1836143) might work, this is not the official API. Please, refer to the [the documentation](https://github.com/rails-api/active_model_serializers/blob/master/docs/howto/outside_controller_use.md#using-activemodelserializers-outside-of-a-controller). – Alexander Popov Nov 17 '16 at 07:53

8 Answers8

177

I think this is what you're looking for:

ActiveModel::ArraySerializer.new(Funding.all, each_serializer: FundingSerializer).to_json

See this Thoughtbot blog post for an example.

John
  • 2,788
  • 2
  • 18
  • 13
  • Awesome, just what I needed. This should be on the AMS wiki on github. – Matjaz Muhic Mar 06 '14 at 18:43
  • This gives me `uninitialized constant ActiveModel::ArraySerializer` – Amit Erandole Aug 29 '14 at 17:35
  • I completely agree with you @MatjazMuhic. – Mirko Akov Nov 20 '14 at 09:48
  • @MirkoAkov it is now for a while. Added by me. :) – Matjaz Muhic Nov 20 '14 at 16:23
  • I am getting a `Load Error` as I am calling it from `lib/modules`. What should be the `require ` file syntax for it? – Vedant Agarwala Apr 02 '15 at 12:44
  • did you add `gem "active_model_serializers"` to your gemfile? – John Apr 03 '15 at 13:25
  • 17
    This does not work for "active_model_serializers" gem >= 0.10.0. ::ActiveModel::ArraySerializer was moved to ::ActiveModel::Serializer::ArraySerializer and structure of classes has been significantly changed. – Zeke Fast Jun 11 '15 at 14:16
  • @DanFairaizl Looks like your wish might come true soon. – Jason Swett Jul 16 '15 at 20:57
  • 7
    @ZekeFast for anyone googling their way over here treading the waters of the latest AMS master (as of April 2016) and found themselves dealing with this problem, please keep in mind that you need to use ActiveModel::Serializer::CollectionSerializer instead since apparently the AMS team will be deprecating the other class soon. ActiveModel::Serializer::CollectionSerializer.new(object.myrelation, each_serializer: MySerializer) – Alfredo Gallegos Apr 11 '16 at 03:03
  • 6
    `ActiveModel::Serializer::CollectionSerializer.new(object.myrelation, serializer: MySerializer)` worked for me (`each_serializer` option was ignored) – biomancer Jul 26 '16 at 13:01
  • The mentioned approach with `CollectionSerializer` works only, if you want to use the `attributes` adapter. What's more, even if that works, this API is not public and should not be used. The official API for this is via `ActiveModelSerializers::SerializableResource`. For more info, see [the documentation](https://github.com/rails-api/active_model_serializers/blob/master/docs/howto/outside_controller_use.md#using-activemodelserializers-outside-of-a-controller). – Alexander Popov Nov 17 '16 at 07:49
28

I'm not sure if this is an idiomatic solution, but it should work:

Funding.all.map{|f| FundingSerializer.new(f)}.to_json
DNNX
  • 6,215
  • 2
  • 27
  • 33
  • 2
    that did work, I just thought that there would be a solution along the lines of ActiveModel::ArraySerializer.new … – shicholas Jul 11 '13 at 01:58
  • 1
    Under the hood of [ActiveModel::ArraySerializer](https://github.com/rails-api/active_model_serializers/blob/master/lib/active_model/serializer/array_serializer.rb) is basically this exact implementation. map over the collection and return a a serialized instance. – Blair Anderson Jan 30 '15 at 19:02
  • 3
    Does not work properly on "active_model_serializers" >= 0.10.0. – Zeke Fast Jun 11 '15 at 14:17
10

This worked for me in version 0.10.2:

ActiveModelSerializers::SerializableResource.new(Funding.all, each_serializer: FundingSerializer).to_json

aNoble
  • 7,033
  • 2
  • 37
  • 33
8

Now you can do that in this way (using AMS v0.10.2):

ActiveModel::Serializer.serializer_for(Funding.all).to_json

EDIT (03.03.2017)
This method is not working anymore. Use aNoble's answer:

ActiveModelSerializers::SerializableResource.new(Funding.all).to_json
Community
  • 1
  • 1
Andrei
  • 537
  • 5
  • 5
  • 2
    As of version `0.10.2` [aNoble](http://stackoverflow.com/a/39606516/1836143) provides the correct answer. Also, check out [the documentation](https://github.com/rails-api/active_model_serializers/blob/master/docs/howto/outside_controller_use.md#using-activemodelserializers-outside-of-a-controller) on the topic. – Alexander Popov Nov 17 '16 at 07:51
  • I 'm using AMS 0.10.2, ActiveModel::Serializer.serializer_for(Leaderboard.all).to_json, always returuns "{ }" if even there are dozens of Leaderboard records – Muhammad Faisal Iqbal Dec 02 '16 at 10:43
  • @MuhammadFaisalIqbal please, check my updated answer – Andrei Mar 03 '17 at 15:50
6

Looks like they are adjusting this again in the newest ActiveModel::Serializers gem version. You no longer can call to_json on the ArraySerializer (which is not ActiveModel::Serializer::ArraySerializer).

Here's what I did to get around it.

let(:resource)        { Funding.all }
let(:serializer)      { ActiveModel::Serializer::ArraySerializer.new(resource, each_serializer: FundingSerializer)
let(:serialization)   { ActiveModel::Serializer::Adapter.create(serializer) }
subject               { JSON.parse(serialization.to_json) }

Calling subject will get you the json you are after.

Here are a couple of more resources which dive into testing setup for further reading: http://eclips3.net/2015/01/24/testing-active-model-serializer-with-rspec/ https://robots.thoughtbot.com/validating-json-schemas-with-an-rspec-matcher

Mitch Nick
  • 302
  • 2
  • 8
6

You can explicitly provide the collection serializer as well.

render json: Funding.all, serializer: ActiveModel::ArraySerializer, each_serializer: FundingSerializer
Vignesh Jayavel
  • 984
  • 11
  • 11
3

Testing the response for active_model_serializers with version >= 0.10.0 I've done this simple helper for RSpec:

module AMSHelper
  def ams_array_serializer(collection, options: {}, adapter: :json)
    serializer = ActiveModel::Serializer::ArraySerializer.new(collection)
    adapter_class = ActiveModel::Serializer::Adapter.adapter_class(adapter)
    adapter_class.new(serializer, options)
  end

  def ams_serializer(object, options: {}, adapter: :json)
    serializer = ActiveModel::Serializer.serializer_for(object).new(object)
    adapter_class = ActiveModel::Serializer::Adapter.adapter_class(adapter)

    adapter_class.new(serializer, options)
  end
end

RSpec.configure do |config|
  config.include AMSHelper, type: :request
end

So you can simply test with:

RSpec.describe "Posts", type: :request do
  describe "GET /" do
    it "returns http success" do
      get posts_path
      expect(response_body).to eq(ams_array_serializer(Post.all).to_json)
    end
  end
end

I hope this could be useful for someone.

dalpo
  • 370
  • 4
  • 9
  • `ams_serializer` is working but `ams_array_serializer` is not working. http://stackoverflow.com/questions/36278413/serialize-an-array-of-models-using-active-model-serializers – Vipin Verma Mar 29 '16 at 07:50
1

assume you have a serializer class(Foo) not match your resources name(Bar), I use following approach to easily serialize objects:

class BaseSerializer < ActiveModel::Serializer
  def self.collection_serialize(resources)
    ActiveModelSerializers::SerializableResource.new(resources, each_serializer: self)
  end
end

let Foo serializer inherit BaseSerializer:

class FooSerializer < BaseSerializer
  ...
end

use FooSerializer in controller or outside:

bar = Bar.all
FooSerializer.collection_serialize(bar).to_json
Yi Feng Xie
  • 4,378
  • 1
  • 26
  • 29