diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c8f5547d..ab24cea0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/active_model_serializers.gemspec b/active_model_serializers.gemspec index 3ccd9654e..71fdda85e 100644 --- a/active_model_serializers.gemspec +++ b/active_model_serializers.gemspec @@ -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 diff --git a/docs/README.md b/docs/README.md index 51c8c85fb..1a0df26e5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -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) | | Grape | 0.9.x | https://github.com/jrhe/grape-active_model_serializers/ | | Sinatra | 0.9.x | https://github.com/SauloSilva/sinatra-active-model-serializers/ diff --git a/docs/integrations/grape.md b/docs/integrations/grape.md new file mode 100644 index 000000000..7c855ebf1 --- /dev/null +++ b/docs/integrations/grape.md @@ -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. diff --git a/lib/grape/active_model_serializers.rb b/lib/grape/active_model_serializers.rb new file mode 100644 index 000000000..eb7fd1ac3 --- /dev/null +++ b/lib/grape/active_model_serializers.rb @@ -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 diff --git a/lib/grape/formatters/active_model_serializers.rb b/lib/grape/formatters/active_model_serializers.rb new file mode 100644 index 000000000..3cac1318b --- /dev/null +++ b/lib/grape/formatters/active_model_serializers.rb @@ -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 + end + end + end +end diff --git a/lib/grape/helpers/active_model_serializers.rb b/lib/grape/helpers/active_model_serializers.rb new file mode 100644 index 000000000..370c52d81 --- /dev/null +++ b/lib/grape/helpers/active_model_serializers.rb @@ -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 diff --git a/test/grape_test.rb b/test/grape_test.rb new file mode 100644 index 000000000..746e246e0 --- /dev/null +++ b/test/grape_test.rb @@ -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 }) + + 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 = { + '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