Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JsonApi PaginationLinks doesn't work outside of ActionController #1268

Closed
jpaas opened this issue Oct 14, 2015 · 12 comments
Closed

JsonApi PaginationLinks doesn't work outside of ActionController #1268

jpaas opened this issue Oct 14, 2015 · 12 comments

Comments

@jpaas
Copy link

jpaas commented Oct 14, 2015

I'm trying to use Grape+Kaminari+AMS 1.0 (head) and I get a stack trace like so...

(NoMethodError)undefined method `query_parameters' for nil:NilClass
/Users/jpaas/.rvm/gems/ruby-2.2.2@imdcloud/bundler/gems/active_model_serializers-da7e6dc795ac/lib/active_model/serializer/adapter/json_api/pagination_links.rb:52:in `query_parameters'
/Users/jpaas/.rvm/gems/ruby-2.2.2@imdcloud/bundler/gems/active_model_serializers-da7e6dc795ac/lib/active_model/serializer/adapter/json_api/pagination_links.rb:17:in `block in serializable_hash'
/Users/jpaas/.rvm/gems/ruby-2.2.2@imdcloud/bundler/gems/active_model_serializers-da7e6dc795ac/lib/active_model/serializer/adapter/json_api/pagination_links.rb:16:in `each'
/Users/jpaas/.rvm/gems/ruby-2.2.2@imdcloud/bundler/gems/active_model_serializers-da7e6dc795ac/lib/active_model/serializer/adapter/json_api/pagination_links.rb:16:in `each_with_object'
/Users/jpaas/.rvm/gems/ruby-2.2.2@imdcloud/bundler/gems/active_model_serializers-da7e6dc795ac/lib/active_model/serializer/adapter/json_api/pagination_links.rb:16:in `serializable_hash'
/Users/jpaas/.rvm/gems/ruby-2.2.2@imdcloud/bundler/gems/active_model_serializers-da7e6dc795ac/lib/active_model/serializer/adapter/json_api.rb:212:in `links_for'
/Users/jpaas/.rvm/gems/ruby-2.2.2@imdcloud/bundler/gems/active_model_serializers-da7e6dc795ac/lib/active_model/serializer/adapter/json_api.rb:103:in `serializable_hash_for_collection'
/Users/jpaas/.rvm/gems/ruby-2.2.2@imdcloud/bundler/gems/active_model_serializers-da7e6dc795ac/lib/active_model/serializer/adapter/json_api.rb:61:in `serializable_hash'
/Users/jpaas/.rvm/gems/ruby-2.2.2@imdcloud/bundler/gems/active_model_serializers-da7e6dc795ac/lib/active_model/serializable_resource.rb:14:in `serializable_hash'

It seems to assume that the context has been set as an ActionDispatch::Request. I would try setting it, but I can't figure out how to get it in Grape.

@jpaas
Copy link
Author

jpaas commented Oct 14, 2015

Looking at the code, I can't even see how this would work for ActionController. The SerializableResource filters the adapter options, so that they must be one of [:include, :fields, :adapter, :meta, :meta_key, :links]. Which means there's no way the adapter options could contain a :context entry.

@bf4 bf4 mentioned this issue Oct 14, 2015
@bf4
Copy link
Member

bf4 commented Oct 14, 2015

@jpaas good bug report. https://github.com/rails-api/active_model_serializers/blob/da7e6dc795ac4f6eb0af63c19c46638b13d0d04e/test/action_controller/json_api/pagination_test.rb should cover that it works

I agree that we are sometime too eager to add foreign objects to the app. It should really be encapsulated. context should maybe be called 'request_context' and has two methods we use: original_url and query_parameters

@jpaas
Copy link
Author

jpaas commented Oct 14, 2015

I was using a fork of grape-active_model_serializers https://github.com/Thanx/grape-active_model_serializers.git, but once I started using the jsonapi adapter, I built my own very simple grape json formatter:

module API
  module AmsFormatter
    class << self
      def call(resource, env)
        serializer_options = {}
        serializer_options.merge!(env['ams_serializer_options']) if env['ams_serializer_options']
        # For some reason serializable_hash doesn't include meta in the response, so I have to use as_json
        ActiveModel::SerializableResource.new(resource, serializer_options).as_json.to_json
      end
    end
  end
end

I also had to create this little grape helper to pass my serializer options to the formatter via the env:

        def render(resources, ams_serializer_options = {})
          env['ams_serializer_options'] = ams_serializer_options
          resources
        end

So now I can implement a grape endpoint like this and put metadata into the jsonapi response:

    formatter :json, API::AmsFormatter

     get ':id' do
       render(Resource.find(params[:id]), meta: { foo: 'bar' })
     end

@jpaas
Copy link
Author

jpaas commented Oct 14, 2015

Oh and BTW, my workaround for this problem at the moment was to subclass the JsonApi adapter to simply skip links for now...

module API
  class AmsJsonapiAdapter < ActiveModel::Serializer::Adapter::JsonApi
    def links_for(serializer, options)
      {}
    end
  end
end

@bf4
Copy link
Member

bf4 commented Oct 14, 2015

@jpaas would you want to submit the formatter here as a PR?

Also, you should be able to just to_json not as_json.to_json

@jpaas
Copy link
Author

jpaas commented Oct 14, 2015

Ah yes @bf4 to_json alone works. I thought I tried that at one point but had trouble. I can submit as a PR. Where would you like it in the namespace?

@bf4
Copy link
Member

bf4 commented Oct 14, 2015

I'm thinking Grape::ActiveModelSerializers in lib/grape/active_model_serializers or something like that and maybe in lib grape-active_model_serializers.rb that requires active_model_serializers and grape/active_model_serializers. Just try something that makes sense to you

jpaas added a commit to jpaas/active_model_serializers that referenced this issue Oct 15, 2015
@hut8
Copy link
Contributor

hut8 commented Oct 25, 2015

I just spent like 6 hours figuring this out. I'm using the same set of tools as @jpaas and I can get the links to work if I pass in the (bastardized) context where I would least expect it ⁉️

So I changed the code at #1268 (comment) to:

# A grape response formatter that can be used as 'formatter :json, Grape::Formatters::ActiveModelSerializers'
#                                                                                                                                                                                # Serializer options can be passed as a hash from your grape endpoint using env[:active_model_serializer_options],
# or better yet user the render helper in Grape::Helpers::ActiveModelSerializers
module Grape
  module Formatters
    module ActiveModelSerializers
      RequestContext = Struct.new(:original_url, :query_parameters)                                                                                                              
      class << self
        def call(resource, env)                                                                                                                                                            serializer_options = {}
          serializer_options.merge!(env[:active_model_serializer_options]) if env[:active_model_serializer_options]

          serializer_options[:context] = RequestContext.new(env['REQUEST_URI'],
                                                            env['rack.request.query_hash'])
          ActiveModel::SerializableResource.new(resource, serializer_options).to_json(serializer_options)
        end
      end
    end
  end
end

Without passing my little context hack into to_json, they don't make their way into PaginationLinks's context constructor parameter. Interestingly, when I pass them to as_json instead, I get Ruby out! 😮

johnhamelink pushed a commit to johnhamelink/active_model_serializers that referenced this issue Nov 19, 2015
johnhamelink pushed a commit to johnhamelink/active_model_serializers that referenced this issue Nov 19, 2015
 - adds handling for when the returned resource is not serializable via ams
 - fix for when resource is an Array
 - fixing last checkin
 - Adds intellij .idea file to .gitignore
 - Moves grape include to grape namespace. Changes Enumerable to Array because a plain hash is enumerable.
 - rubocop corrections
 - Rebased and squashed commits, resubmitted to CI
johnhamelink pushed a commit to johnhamelink/active_model_serializers that referenced this issue Nov 23, 2015
 - adds handling for when the returned resource is not serializable via ams
 - fix for when resource is an Array
 - fixing last checkin
 - Adds intellij .idea file to .gitignore
 - Moves grape include to grape namespace. Changes Enumerable to Array because a plain hash is enumerable.
 - rubocop corrections
 - Rebased and squashed commits, resubmitted to CI
 - Remove .idea from gitignore
 - Add integration test
johnhamelink pushed a commit to johnhamelink/active_model_serializers that referenced this issue Nov 23, 2015
 - adds handling for when the returned resource is not serializable via ams
 - fix for when resource is an Array
 - fixing last checkin
 - Adds intellij .idea file to .gitignore
 - Moves grape include to grape namespace. Changes Enumerable to Array because a plain hash is enumerable.
 - rubocop corrections
 - Rebased and squashed commits, resubmitted to CI
 - Remove .idea from gitignore
 - Add integration test
johnhamelink pushed a commit to johnhamelink/active_model_serializers that referenced this issue Nov 23, 2015
 - adds handling for when the returned resource is not serializable via ams
 - fix for when resource is an Array
 - fixing last checkin
 - Adds intellij .idea file to .gitignore
 - Moves grape include to grape namespace. Changes Enumerable to Array because a plain hash is enumerable.
 - rubocop corrections
 - Rebased and squashed commits, resubmitted to CI
 - Remove .idea from gitignore
 - Add integration test
johnhamelink pushed a commit to johnhamelink/active_model_serializers that referenced this issue Nov 24, 2015
 - adds handling for when the returned resource is not serializable via ams
 - fix for when resource is an Array
 - Moves grape include to grape namespace. Changes Enumerable to Array because a plain hash is enumerable.
 - Add integration test
 - Refine scope of Grape version dependency
 - Assert that the response is equal to a manually defined JSON string
 - Add single module to include in Grape projects
 - Create a Serializable Resource to test rails-api from Grape
 - Update docs
 - Fix discrepency between ActiveRecord 4.0 - 4.1 and 4.2
johnhamelink pushed a commit to johnhamelink/active_model_serializers that referenced this issue Nov 24, 2015
 - adds handling for when the returned resource is not serializable via ams
 - fix for when resource is an Array
 - Moves grape include to grape namespace. Changes Enumerable to Array because a plain hash is enumerable.
 - Add integration test
 - Refine scope of Grape version dependency
 - Assert that the response is equal to a manually defined JSON string
 - Add single module to include in Grape projects
 - Create a Serializable Resource to test rails-api from Grape
 - Update docs
 - Fix discrepency between ActiveRecord 4.0 - 4.1 and 4.2
 - Updated Changelog
johnhamelink pushed a commit to johnhamelink/active_model_serializers that referenced this issue Nov 24, 2015
 - adds handling for when the returned resource is not serializable via ams
 - fix for when resource is an Array
 - Moves grape include to grape namespace. Changes Enumerable to Array because a plain hash is enumerable.
 - Add integration test
 - Refine scope of Grape version dependency
 - Assert that the response is equal to a manually defined JSON string
 - Add single module to include in Grape projects
 - Create a Serializable Resource to test rails-api from Grape
 - Update docs
 - Fix discrepency between ActiveRecord 4.0 - 4.1 and 4.2
 - Updated Changelog
johnhamelink pushed a commit to johnhamelink/active_model_serializers that referenced this issue Nov 24, 2015
 - adds handling for when the returned resource is not serializable via ams
 - fix for when resource is an Array
 - Moves grape include to grape namespace. Changes Enumerable to Array because a plain hash is enumerable.
 - Add integration test
 - Refine scope of Grape version dependency
 - Assert that the response is equal to a manually defined JSON string
 - Add single module to include in Grape projects
 - Create a Serializable Resource to test rails-api from Grape
 - Update docs
 - Fix discrepency between ActiveRecord 4.0 - 4.1 and 4.2
 - Updated Changelog
johnhamelink pushed a commit to johnhamelink/active_model_serializers that referenced this issue Nov 26, 2015
 - adds handling for when the returned resource is not serializable via ams
 - fix for when resource is an Array
 - Moves grape include to grape namespace. Changes Enumerable to Array because a plain hash is enumerable.
 - Add integration test
 - Refine scope of Grape version dependency
 - Assert that the response is equal to a manually defined JSON string
 - Add single module to include in Grape projects
 - Create a Serializable Resource to test rails-api from Grape
 - Update docs
 - Fix discrepency between ActiveRecord 4.0 - 4.1 and 4.2
 - Updated Changelog
johnhamelink pushed a commit to johnhamelink/active_model_serializers that referenced this issue Nov 26, 2015
 - adds handling for when the returned resource is not serializable via ams
 - fix for when resource is an Array
 - Moves grape include to grape namespace. Changes Enumerable to Array because a plain hash is enumerable.
 - Add integration test
 - Refine scope of Grape version dependency
 - Assert that the response is equal to a manually defined JSON string
 - Add single module to include in Grape projects
 - Create a Serializable Resource to test rails-api from Grape
 - Update docs
 - Fix discrepency between ActiveRecord 4.0 - 4.1 and 4.2
 - Updated Changelog
@remear remear closed this as completed Mar 17, 2016
@vipin8169
Copy link

vipin8169 commented Mar 29, 2016

I am also getting the same issue. On executing ActiveModel::SerializableResource.new(@admins, adapter: :json_api).to_json

Although, ActiveModel::SerializableResource.new(@admins, adapter: :json_api) gives the following response:
#<ActiveModel::SerializableResource:0x000000054789e8 @resource=#<ActiveRecord::Relation [#<Admin id: 36, email: "[email protected]", encrypted_password: "$2a$10$u1E0eoEymfPs.rmUu9KZce9dXWQ/cIE5PCjKJbJFPmf...", remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, failed_attempts: 0, locked_at: nil, created_at: "2016-03-28 05:15:17", updated_at: "2016-03-28 05:15:17">, #<Admin id: 20, email: "[email protected]", encrypted_password: "$2a$10$e5J4nS2wlUtVyoP95qVWvux36bihsE2Pk8.Rn/n8.3f...", remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, failed_attempts: 0, locked_at: nil, created_at: "2016-03-28 05:15:16", updated_at: "2016-03-28 05:15:16">, #<Admin id: 22, email: "[email protected]", encrypted_password: "$2a$10$Fi0FaprD1t4MH1uK29IiVuM6M5ZBlb7CIJteIpdHyYF...", remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, failed_attempts: 0, locked_at: nil, created_at: "2016-03-28 05:15:16", updated_at: "2016-03-28 05:15:16">, #<Admin id: 37, email: "[email protected]", encrypted_password: "$2a$10$2MGzfCyarGQJcmp1RV2BBeU0xNKqalkyukwszutS5/t...", remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, failed_attempts: 0, locked_at: nil, created_at: "2016-03-28 05:15:17", updated_at: "2016-03-28 05:15:17">, #<Admin id: 5, email: "[email protected]", encrypted_password: "$2a$10$d5dXU.HH3SQjPPNEijzoau7di9dwwVk4TsgnCV9Xxga...", remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, failed_attempts: 0, locked_at: nil, created_at: "2016-03-28 05:15:15", updated_at: "2016-03-28 05:15:15">, #<Admin id: 14, email: "[email protected]", encrypted_password: "$2a$10$sM9p3J6g04TnfMl/nTtezuXdwW3uHoebIhvuW1t1ptI...", remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, failed_attempts: 0, locked_at: nil, created_at: "2016-03-28 05:15:15", updated_at: "2016-03-28 05:15:15">, #<Admin id: 27, email: "[email protected]", encrypted_password: "$2a$10$ztJkxiM88UnROguht0mEUea/GVRK0KPO856vQimaIV3...", remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, failed_attempts: 0, locked_at: nil, created_at: "2016-03-28 05:15:16", updated_at: "2016-03-28 05:15:16">, #<Admin id: 2, email: "[email protected]", encrypted_password: "$2a$10$CcYUeV5yayJYaJ0cfcKGVOo7FtxxHpuCFUw.GRcAxAO...", remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, failed_attempts: 0, locked_at: nil, created_at: "2016-03-28 05:15:14", updated_at: "2016-03-28 05:15:14">, #<Admin id: 10, email: "[email protected]", encrypted_password: "$2a$10$8zSpB.qRVkCJSJRPP.630uOGyMOFQRHGKpB6h58COsx...", remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, failed_attempts: 0, locked_at: nil, created_at: "2016-03-28 05:15:15", updated_at: "2016-03-28 05:15:15">, #<Admin id: 15, email: "[email protected]", encrypted_password: "$2a$10$BfFV7CZmofSJC8ObT8f9OuE.ntSXNvtstd3MUuEolyu...", remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, failed_attempts: 0, locked_at: nil, created_at: "2016-03-28 05:15:15", updated_at: "2016-03-28 05:15:15">]>, @adapter_opts={:adapter=>:json_api}, @serializer_opts={}>

@Kosmin
Copy link

Kosmin commented Apr 18, 2016

I've used @hut8's approach, but changed it for rails 4.1 and active_model_serializers version 0.10.rc3, to solve my problem. My problem was that I wanted to render a view using the html format and serialize a bunch of paginated objects using the json_api to initialize a client-side JS framework. Without the serialization context, I get undefined method 'query_parameters' for nil:NilClass

Since I'm not doing this within the context of a render, but rather a helper method, and I'd rather stay away from rendering inside a variable (since that seems like a hack which does more than I want), this is what I ended up doing

  def serialized_objects(objects)
    @serialized_objects =
      ActiveModel::SerializableResource.new(objects, {}).as_json(serialization_context_options)
  end

  def serialization_context_options
    {
      serialization_context: Struct.new(:request_url, :query_parameters).new(
        request.path,
        env['rack.request.query_hash']
      )
    }
  end

@onomated
Copy link
Contributor

Is Grape support complete? I've followed this thread and updated to the latest 0.10.0 release and getting the following error after following the Grape Support documentation

"status":500
    "title":"Internal Server Error"
    "detail":"key not found: :serialization_context"
    "meta":{"error_class":"KeyError"
    "backtrace":
    "~/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/active_model_serializers-0.10.0/lib/active_model_serializers/adapter/json_api/pagination_links.rb:12:in `fetch'"
    "~/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/active_model_serializers-0.10.0/lib/active_model_serializers/adapter/json_api/pagination_links.rb:12:in `initialize'"
    "~/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/active_model_serializers-0.10.0/lib/active_model_serializers/adapter/json_api.rb:506:in `new'"
    "~/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/active_model_serializers-0.10.0/lib/active_model_serializers/adapter/json_api.rb:506:in `pagination_links_for'"
    "~/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/active_model_serializers-0.10.0/lib/active_model_serializers/adapter/json_api.rb:131:in `success_document'"
    "~/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/active_model_serializers-0.10.0/lib/active_model_serializers/adapter/json_api.rb:48:in `serializable_hash'"
    "~/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/active_model_serializers-0.10.0/lib/active_model_serializers/adapter/base.rb:29:in `as_json'"
    "~/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/active_model_serializers-0.10.0/lib/active_model_serializers/serializable_resource.rb:8:in `to_json'"
    "~/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/active_model_serializers-0.10.0/lib/active_model_serializers/serializable_resource.rb:8:in `to_json'"
    "~/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/active_model_serializers-0.10.0/lib/active_model_serializers/logging.rb:6

@bf4
Copy link
Member

bf4 commented May 29, 2016

That's a bug. Please open a new issue

B mobile phone

On May 29, 2016, at 9:36 AM, Onome [email protected] wrote:

Is Grape support complete? I've followed this thread and updated to the latest 0.10.0 release and getting the following error after following the Grape Support documentation

"status":500
"title":"Internal Server Error"
"detail":"key not found: :serialization_context"
"meta":{"error_class":"KeyError"
"backtrace":
"/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/active_model_serializers-0.10.0/lib/active_model_serializers/adapter/json_api/pagination_links.rb:12:in fetch'" "~/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/active_model_serializers-0.10.0/lib/active_model_serializers/adapter/json_api/pagination_links.rb:12:ininitialize'"
"
/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/active_model_serializers-0.10.0/lib/active_model_serializers/adapter/json_api.rb:506:in new'" "~/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/active_model_serializers-0.10.0/lib/active_model_serializers/adapter/json_api.rb:506:inpagination_links_for'"
"/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/active_model_serializers-0.10.0/lib/active_model_serializers/adapter/json_api.rb:131:in success_document'" "~/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/active_model_serializers-0.10.0/lib/active_model_serializers/adapter/json_api.rb:48:inserializable_hash'"
"
/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/active_model_serializers-0.10.0/lib/active_model_serializers/adapter/base.rb:29:in as_json'" "~/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/active_model_serializers-0.10.0/lib/active_model_serializers/serializable_resource.rb:8:into_json'"
"/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/active_model_serializers-0.10.0/lib/active_model_serializers/serializable_resource.rb:8:in `to_json'"
"
/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/active_model_serializers-0.10.0/lib/active_model_serializers/logging.rb:6

You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants