diff --git a/active_model_serializers.gemspec b/active_model_serializers.gemspec index 1e438e2cb..34893f575 100644 --- a/active_model_serializers.gemspec +++ b/active_model_serializers.gemspec @@ -47,4 +47,5 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'bundler', '~> 1.6' spec.add_development_dependency 'timecop', '~> 0.7' + spec.add_development_dependency 'grape', '~> 0.13.0' end diff --git a/docs/README.md b/docs/README.md index a1e9d908e..04c8f88b0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -16,6 +16,7 @@ This is the documentation of AMS, it's focused on the **0.10.x version.** - [How to add pagination links](howto/add_pagination_links.md) - [Using AMS Outside Of Controllers](howto/outside_controller_use.md) - [How to use JSON API with Ember](howto/ember-and-json-api.md) +- [Grape](howto/grape.md) ## Getting Help diff --git a/docs/howto/grape.md b/docs/howto/grape.md new file mode 100644 index 000000000..128059f6f --- /dev/null +++ b/docs/howto/grape.md @@ -0,0 +1,27 @@ +# Integration with Grape + +To add Grape support, `require 'grape/active_model_serializers'` in the base of your Grape endpoints like so: + +```ruby +module Example + class Dummy < Grape::API + require 'grape/active_model_serializers' + mount Example::V1::Base + end +end +``` + +Then add the formatter and helpers to the endpoints (a `Defaults` module is used here which would be included in all endpoints): + +```ruby +module Example::V1::Defaults + extend ActiveSupport::Concern + + included do + formatter :json, Grape::Formatters::ActiveModelSerializers + helpers Grape::Helpers::ActiveModelSerializers + end + +end +``` + diff --git a/lib/grape/active_model_serializers.rb b/lib/grape/active_model_serializers.rb new file mode 100644 index 000000000..e0fd8676b --- /dev/null +++ b/lib/grape/active_model_serializers.rb @@ -0,0 +1,6 @@ +# To add grape support, require 'grape/active_model_serializers' in the base of your grape endpoints +# Then add 'formatter :json, Grape::Formatters::ActiveModelSerializers' to the endpoints +# Then add 'helpers Grape::Helpers::ActiveModelSerializers' to the endpoints +require 'active_model_serializers' +require 'grape/formatters/active_model_serializers' +require 'grape/helpers/active_model_serializers' diff --git a/lib/grape/formatters/active_model_serializers.rb b/lib/grape/formatters/active_model_serializers.rb new file mode 100644 index 000000000..bcd0cc9a1 --- /dev/null +++ b/lib/grape/formatters/active_model_serializers.rb @@ -0,0 +1,16 @@ +# 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) + return resource.to_json unless resource.is_a?(Array) || resource.respond_to?(:model_name) + 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..a33f38498 --- /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 ActiveModelSerializer 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..620f80e5b --- /dev/null +++ b/test/grape_test.rb @@ -0,0 +1,50 @@ +require 'test_helper' +require 'securerandom' +require 'grape' +require 'grape/active_model_serializers' + +class ActiveModelSerializers::GrapeTest < Minitest::Test + include Rack::Test::Methods + + class GrapeTest < Grape::API + format :json + formatter :json, Grape::Formatters::ActiveModelSerializers + helpers Grape::Helpers::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 + end + end + + def app + GrapeTest.new + end + + def test_formatter_returns_json + get '/grape/render' + assert last_response.ok? + assert JSON.parse(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') + serializer = ARModels::PostSerializer.new(post) + adapter = ActiveModel::Serializer::Adapter::JsonApi.new(serializer, meta: { page: 1, total_pages: 2 }) + + assert last_response.ok? + assert_equal adapter.to_json, last_response.body + end +end