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

Grape formatter feature requested in #1258 - Rebased and Repushed (#1273) #1336

Merged
merged 1 commit into from
Dec 18, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Breaking changes:

Features:

- [#1336](https://github.com/rails-api/active_model_serializers/pull/1336) Added support for Grape >= 0.13, < 1.0
- [#1291](https://github.com/rails-api/active_model_serializers/pull/1291) Add logging (@maurogeorge)
- [#1225](https://github.com/rails-api/active_model_serializers/pull/1225) Better serializer lookup, use nested serializer when it exists (@beauby)
- [#1172](https://github.com/rails-api/active_model_serializers/pull/1172) Better serializer registration, get more than just the first module (@bf4)
Expand Down
1 change: 1 addition & 0 deletions active_model_serializers.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,5 @@ Gem::Specification.new do |spec|
spec.add_development_dependency 'bundler', '~> 1.6'
spec.add_development_dependency 'timecop', '~> 0.7'
spec.add_development_dependency 'minitest-reporters'
spec.add_development_dependency 'grape', ['>= 0.13', '< 1.0']
end
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ This is the documentation of AMS, it's focused on the **0.10.x version.**
|----|-----|----
| Ember.js | 0.9.x | [active-model-adapter](https://github.com/ember-data/active-model-adapter)
| Ember.js | 0.10.x + | [docs/integrations/ember-and-json-api.md](integrations/ember-and-json-api.md)
| Grape | 0.10.x + | [#1258](https://github.com/rails-api/active_model_serializers/issues/1258) |
| Grape | 0.10.x + | [docs/integrations/grape.md](integrations/grape.md) |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

awesome! Thanks for editing this and the other doc files!

| Grape | 0.9.x | https://github.com/jrhe/grape-active_model_serializers/ |
| Sinatra | 0.9.x | https://github.com/SauloSilva/sinatra-active-model-serializers/

Expand Down
19 changes: 19 additions & 0 deletions docs/integrations/grape.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Integration with Grape

[Grape](https://github.com/ruby-grape/grape) is an opinionated micro-framework for creating REST-like APIs in ruby.

ActiveModelSerializers currently supports Grape >= 0.13, < 1.0

To add [Grape](https://github.com/ruby-grape/grape) support, enable the formatter and helper functions by including `Grape::ActiveModelSerializers` in your base endpoint. For example:

```ruby
module Example
class Dummy < Grape::API
require 'grape/active_model_serializers'
include Grape::ActiveModelSerializers
mount Example::V1::Base
end
end
```

Aside from this, [configuration](../general/configuration_options.md) of ActiveModelSerializers is exactly the same.
14 changes: 14 additions & 0 deletions lib/grape/active_model_serializers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# To add Grape support, require 'grape/active_model_serializers' in the base of your Grape endpoints
# Then add 'include Grape::ActiveModelSerializers' to enable the formatter and helpers
require 'active_model_serializers'
require 'grape/formatters/active_model_serializers'
require 'grape/helpers/active_model_serializers'

module Grape::ActiveModelSerializers
extend ActiveSupport::Concern

included do
formatter :json, Grape::Formatters::ActiveModelSerializers
helpers Grape::Helpers::ActiveModelSerializers
end
end
15 changes: 15 additions & 0 deletions lib/grape/formatters/active_model_serializers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# 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
def self.call(resource, env)
serializer_options = {}
serializer_options.merge!(env[:active_model_serializer_options]) if env[:active_model_serializer_options]
ActiveModel::SerializableResource.new(resource, serializer_options).to_json
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

naive question. Does grape require the formatter to return a JSON string?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grape can handle Hashes, Strings, Arrays, and Enumerables - I will add an update to return the serializable hash

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

really should be as_json (not serializable_hash). and possibly pass some options into it like include, etc. But I don't know what grape options have at this point

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently I was wrong about this - if I use as_json the response is a ruby hash

end
end
end
end
16 changes: 16 additions & 0 deletions lib/grape/helpers/active_model_serializers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Helpers can be included in your Grape endpoint as: helpers Grape::Helpers::ActiveModelSerializers
module Grape
module Helpers
module ActiveModelSerializers
# A convenience method for passing ActiveModelSerializers serializer options
#
# Example: To include relationships in the response: render(post, include: ['comments'])
#
# Example: To include pagination meta data: render(posts, meta: { page: posts.page, total_pages: posts.total_pages })
def render(resource, active_model_serializer_options = {})
env[:active_model_serializer_options] = active_model_serializer_options
resource
end
end
end
end
89 changes: 89 additions & 0 deletions test/grape_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
require 'test_helper'
require 'grape'
require 'grape/active_model_serializers'

class ActiveModelSerializers::GrapeTest < Minitest::Test
include Rack::Test::Methods

class GrapeTest < Grape::API
format :json
include Grape::ActiveModelSerializers

resources :grape do
get '/render' do
render ARModels::Post.new(title: 'Dummy Title', body: 'Lorem Ipsum')
end

get '/render_with_json_api' do
post = ARModels::Post.new(title: 'Dummy Title', body: 'Lorem Ipsum')
render post, meta: { page: 1, total_pages: 2 }, adapter: :json_api
end

get '/render_array_with_json_api' do
post = ARModels::Post.create(title: 'Dummy Title', body: 'Lorem Ipsum')
post.dup.save
render ARModels::Post.all, adapter: :json_api
end
end
end

def app
GrapeTest.new
end

def test_formatter_returns_json
get '/grape/render'

post = ARModels::Post.new(title: 'Dummy Title', body: 'Lorem Ipsum')
serializable_resource = serializable(post)

assert last_response.ok?
assert_equal serializable_resource.to_json, last_response.body
end

def test_render_helper_passes_through_options_correctly
get '/grape/render_with_json_api'

post = ARModels::Post.new(title: 'Dummy Title', body: 'Lorem Ipsum')
serializable_resource = serializable(post, serializer: ARModels::PostSerializer, adapter: :json_api, meta: { page: 1, total_pages: 2 })
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd just like to note that the other two tests here have an expected hash but this one generates the expected hash. I'm not sure if I like one style over the other or if there's a reason they're different. So, just noting it in case you have any thoughts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point - I've moved towards using serializable everywhere. I also added assert last_response.ok? to the one test that was missing it.


assert last_response.ok?
assert_equal serializable_resource.to_json, last_response.body
end

def test_formatter_handles_arrays
get '/grape/render_array_with_json_api'

expected = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

serializable? or I can merge it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How could I create an array to pass into the serializer?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you modified the routing as

+  def setup
+    @post = ARModels::Post.create(title: 'Dummy Title', body: 'Lorem Ipsum')
+     ARModels::Post.create(title: 'post 2', body: 'body 2')
+     @posts = ARModels::Post.all
+  end
# .. 
    resources :grape do
      get '/render' do
-        post = ARModels::Post.new(title: 'Dummy Title', body: 'Lorem Ipsum')
+        render @post
      end

      get '/render_with_json_api' do
-       post = ARModels::Post.new(title: 'Dummy Title', body: 'Lorem Ipsum')
-        render post, meta: { page: 1, total_pages: 2 }, adapter: :json_api
+        render @post, meta: { page: 1, total_pages: 2 }, adapter: :json_api
      end

      get '/render_array_with_json_api' do
-        render ARModels::Post.all, adapter: :json_api
+        render @posts, adapter: :json_api
      end
    end

Then your tests become testing

serializable_resource = serializable(@post)
serializable_resource = serializable(@post, serializer: ARModels::PostSerializer, adapter: :json_api, meta: { page: 1, total_pages: 2 })
serializable_resource = serializable(@posts, adapter: :json_api)

or expected = serializable_resource.to_json

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thoughts

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I never got back to you on this - work got in the way 😄

I tried to implement what you said, but unfortunately it doesn't work due to the fact that @post can't be associated from within GrapeTest class. I say LGTM.

'data' => [
{
id: '1',
type: 'ar_models_posts',
attributes: {
title: 'Dummy Title',
body: 'Lorem Ipsum'
},
relationships: {
comments: { data: [] },
author: { data: nil }
}
},
{
id: '2',
type: 'ar_models_posts',
attributes: {
title: 'Dummy Title',
body: 'Lorem Ipsum'
},
relationships: {
comments: { data: [] },
author: { data: nil }
}
}
]
}

assert last_response.ok?
assert_equal expected.to_json, last_response.body
end
end