From 4315930a55761fa5b17e2e7b4468adf75fbac1d8 Mon Sep 17 00:00:00 2001 From: Darren Cheng Date: Tue, 30 Aug 2016 14:43:57 -0700 Subject: [PATCH 1/2] Pass adapter options through calls to render. --- CHANGELOG.md | 3 +- README.md | 19 +++- .../endpoint_extension.rb | 6 +- .../options_builder.rb | 5 + .../options_builder_spec.rb | 96 +++++++++++++++++++ .../serializer_resolver_spec.rb | 1 - 6 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 spec/grape/active_model_serializers/options_builder_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index fb3ca1e..9d6cba9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,9 @@ ### 1.5.1 (Next) -* [#65](https://github.com/ruby-grape/grape-active_model_serializers/pull/65): Added Danger, PR linter - [@dblock](https://github.com/dblock). * Your contribution here. +* [#65](https://github.com/ruby-grape/grape-active_model_serializers/pull/65): Added Danger, PR linter - [@dblock](https://github.com/dblock). +* [#63](https://github.com/ruby-grape/grape-active_model_serializers/pull/63): Pass adapter options through render - [@drn](https://github.com/drn). ### 1.5.0 (August 24, 2016) diff --git a/README.md b/README.md index 77305fb..4fbcf46 100644 --- a/README.md +++ b/README.md @@ -150,10 +150,10 @@ Or as follows. ActiveModelSerializer will fetch automatically the right serializer to render. -### Manually specifying serializer options +### Manually specifying serializer / adapter options ```ruby -# Serializer options can be specified on routes or namespaces. +# Serializer and adapter options can be specified on routes or namespaces. namespace 'foo', serializer: BarSerializer do get "/" do # will use "bar" serializer @@ -171,6 +171,21 @@ namespace 'foo', serializer: BarSerializer do end ``` +```ruby +# Serializer and adapter options can also be specified in the body of the route +resource :users do + get '/:id' do + if conditional + # uses UserSerializer and configured default adapter automatically + user + else + # uses specified serializer and adapter + render current_user, serializer: ErrorSerializer, adapter: :attributes + end + end +end +``` + ### Custom Metadata ```ruby diff --git a/lib/grape-active_model_serializers/endpoint_extension.rb b/lib/grape-active_model_serializers/endpoint_extension.rb index 444fa6b..3fa89db 100644 --- a/lib/grape-active_model_serializers/endpoint_extension.rb +++ b/lib/grape-active_model_serializers/endpoint_extension.rb @@ -37,8 +37,10 @@ def serialization_scope end end - def render(resources, meta = {}) - env['ams_meta'] = meta + def render(resources, extra_options = {}) + extra_options.symbolize_keys! + env['ams_meta'] = extra_options.slice(:meta, :meta_key) + env['ams_adapter_options'] = extra_options.except(:meta, :meta_key) resources end diff --git a/lib/grape-active_model_serializers/options_builder.rb b/lib/grape-active_model_serializers/options_builder.rb index 040d784..109c0a3 100644 --- a/lib/grape-active_model_serializers/options_builder.rb +++ b/lib/grape-active_model_serializers/options_builder.rb @@ -12,6 +12,7 @@ def options options[:scope] = endpoint unless options.key?(:scope) options.merge!(default_root_options) unless options.key?(:root) options.merge!(meta_options) + options.merge!(adapter_options) options ) end @@ -63,6 +64,10 @@ def meta_options options[:meta_key] = meta_key if meta && meta_key options end + + def adapter_options + env['ams_adapter_options'] || {} + end end end end diff --git a/spec/grape/active_model_serializers/options_builder_spec.rb b/spec/grape/active_model_serializers/options_builder_spec.rb new file mode 100644 index 0000000..f218d3b --- /dev/null +++ b/spec/grape/active_model_serializers/options_builder_spec.rb @@ -0,0 +1,96 @@ +require 'spec_helper' + +describe Grape::ActiveModelSerializers::OptionsBuilder do + let(:resolver) { described_class.new(resource, env) } + let(:resource) { User.new } + let(:env) { { 'api.endpoint' => UsersApi.endpoints.first } } + + context '#options' do + let(:options) { resolver.options } + + context 'meta options' do + let(:env) { super().merge('ams_meta' => meta_options) } + let(:meta_options) { + { + meta: meta, + meta_key: meta_key + } + } + let(:meta) { { sample: 'metadata' } } + let(:meta_key) { 'specified_key' } + + context 'meta option set' do + context 'meta_key set' do + it 'includes meta' do + expect(options[:meta]).to eq(meta) + end + + it 'includes meta_key' do + expect(options[:meta_key]).to eq(meta_key) + end + end + + context 'meta_key unset' do + let(:meta_key) { nil } + + it 'includes meta' do + expect(options[:meta]).to eq(meta) + end + + it 'does not include meta_key' do + expect(options.keys).not_to include(:meta_key) + end + end + end + + context 'meta option unset' do + let(:meta) { nil } + + context 'meta_key set' do + it 'does not include meta' do + expect(options.keys).not_to include(:meta) + end + + it 'does not include meta_key' do + expect(options.keys).not_to include(:meta_key) + end + end + + context 'meta_key unset' do + let(:meta_key) { nil } + + it 'does not include meta' do + expect(options.keys).not_to include(:meta) + end + + it 'does not include meta_key' do + expect(options.keys).not_to include(:meta_key) + end + end + end + end + + context 'adapter options' do + let(:env) { super().merge('ams_adapter_options' => adapter_options) } + let(:adapter_options) { {} } + + context 'adapter' do + let(:adapter_options) { { adapter: adapter } } + let(:adapter) { :attributes } + + it 'includes adapter as top-level option' do + expect(options[:adapter]).to eq(adapter) + end + end + + context 'serializer' do + let(:adapter_options) { { serializer: serializer } } + let(:serializer) { V2::UserSerializer } + + it 'includes serializer as top-level option' do + expect(options[:serializer]).to eq(serializer) + end + end + end + end +end diff --git a/spec/grape/active_model_serializers/serializer_resolver_spec.rb b/spec/grape/active_model_serializers/serializer_resolver_spec.rb index 4972551..da10876 100644 --- a/spec/grape/active_model_serializers/serializer_resolver_spec.rb +++ b/spec/grape/active_model_serializers/serializer_resolver_spec.rb @@ -1,4 +1,3 @@ -require 'pry' require 'spec_helper' # asserts serializer resolution order: From 0963774ecc8d994184eb2822aafbcd1d672d1c38 Mon Sep 17 00:00:00 2001 From: Darren Cheng Date: Thu, 8 Sep 2016 12:57:14 -0700 Subject: [PATCH 2/2] Whitelist adapter options and support extra options. --- README.md | 17 ++++++++- .../endpoint_extension.rb | 11 ++++-- .../options_builder.rb | 15 +++++--- .../options_builder_spec.rb | 35 ++++++++++++++++++- 4 files changed, 69 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 4fbcf46..c3f8232 100644 --- a/README.md +++ b/README.md @@ -177,7 +177,7 @@ resource :users do get '/:id' do if conditional # uses UserSerializer and configured default adapter automatically - user + current_user else # uses specified serializer and adapter render current_user, serializer: ErrorSerializer, adapter: :attributes @@ -186,6 +186,21 @@ resource :users do end ``` +```ruby +# Adhoc serializer options can be specified in the body of the route +resource :users do + get '/:id' do + render current_user, extra: { adhoc_name_option: 'value' } + end +end + +class UserSerializer + def name + instance_options[:adhoc_name_option] # accessible in instance_options + end +end +``` + ### Custom Metadata ```ruby diff --git a/lib/grape-active_model_serializers/endpoint_extension.rb b/lib/grape-active_model_serializers/endpoint_extension.rb index 3fa89db..7c21fdd 100644 --- a/lib/grape-active_model_serializers/endpoint_extension.rb +++ b/lib/grape-active_model_serializers/endpoint_extension.rb @@ -38,9 +38,14 @@ def serialization_scope end def render(resources, extra_options = {}) - extra_options.symbolize_keys! - env['ams_meta'] = extra_options.slice(:meta, :meta_key) - env['ams_adapter_options'] = extra_options.except(:meta, :meta_key) + options = extra_options.symbolize_keys + env['ams_meta'] = options.slice( + :meta, :meta_key + ) + env['ams_adapter'] = options.slice( + :adapter, :serializer, :each_serializer + ) + env['ams_extra'] = options[:extra] resources end diff --git a/lib/grape-active_model_serializers/options_builder.rb b/lib/grape-active_model_serializers/options_builder.rb index 109c0a3..7221746 100644 --- a/lib/grape-active_model_serializers/options_builder.rb +++ b/lib/grape-active_model_serializers/options_builder.rb @@ -13,6 +13,7 @@ def options options.merge!(default_root_options) unless options.key?(:root) options.merge!(meta_options) options.merge!(adapter_options) + options.merge!(extra_options) options ) end @@ -57,16 +58,22 @@ def innermost_scope def meta_options options = {} - ams_meta = env['ams_meta'] || {} - meta = ams_meta[:meta] - meta_key = ams_meta[:meta_key] + meta_options = env['ams_meta'] || {} + meta = meta_options[:meta] + meta_key = meta_options[:meta_key] options[:meta] = meta if meta options[:meta_key] = meta_key if meta && meta_key options end def adapter_options - env['ams_adapter_options'] || {} + env['ams_adapter'] || {} + end + + def extra_options + options = env['ams_extra'] || {} + return options if options.is_a?(Hash) + raise 'Extra options must be a hash' end end end diff --git a/spec/grape/active_model_serializers/options_builder_spec.rb b/spec/grape/active_model_serializers/options_builder_spec.rb index f218d3b..af47c43 100644 --- a/spec/grape/active_model_serializers/options_builder_spec.rb +++ b/spec/grape/active_model_serializers/options_builder_spec.rb @@ -71,7 +71,7 @@ end context 'adapter options' do - let(:env) { super().merge('ams_adapter_options' => adapter_options) } + let(:env) { super().merge('ams_adapter' => adapter_options) } let(:adapter_options) { {} } context 'adapter' do @@ -91,6 +91,39 @@ expect(options[:serializer]).to eq(serializer) end end + + context 'each_serializer' do + let(:adapter_options) { { each_serializer: each_serializer } } + let(:each_serializer) { V2::UserSerializer } + + it 'includes each_serializer as top-level option' do + expect(options[:each_serializer]).to eq(each_serializer) + end + end + end + + context 'extra options' do + let(:env) { super().merge('ams_extra' => extra_options) } + + context 'hash' do + let(:extra_options) { { extra: 'info' } } + + it 'includes extra options in top-level options' do + expect(options.keys).to include(:extra) + end + end + + context 'not a hash' do + let(:extra_options) { 'extra' } + + it 'raises an exception' do + expect { + options + }.to raise_exception( + 'Extra options must be a hash' + ) + end + end end end end