From 5560b49098b486d29ce4c00bd2ada965ee1c67e1 Mon Sep 17 00:00:00 2001 From: Gary Gordon Date: Tue, 4 Nov 2014 15:36:52 -0500 Subject: [PATCH] Allow overriding the adapter with render option Make it easy to use multiple adapters in an app. use "adapter: false" to not use ams make a test override config.adapter --- lib/action_controller/serialization.rb | 23 +++++----- lib/active_model/serializer.rb | 3 +- lib/active_model/serializer/adapter.rb | 10 +++++ .../adapter_selector_test.rb | 41 +++++++++++++++++ .../action_controller/json_api_linked_test.rb | 44 +++++-------------- test/action_controller/serialization_test.rb | 6 +-- test/adapter_test.rb | 20 +++++++++ test/serializers/configuration_test.rb | 2 +- 8 files changed, 99 insertions(+), 50 deletions(-) create mode 100644 test/action_controller/adapter_selector_test.rb diff --git a/lib/action_controller/serialization.rb b/lib/action_controller/serialization.rb index d157e2f9..edb5bc0e 100644 --- a/lib/action_controller/serialization.rb +++ b/lib/action_controller/serialization.rb @@ -6,29 +6,32 @@ module ActionController include ActionController::Renderers - ADAPTER_OPTION_KEYS = [:include, :root] + ADAPTER_OPTION_KEYS = [:include, :root, :adapter] - def get_serializer(resource, options) - @_serializer ||= options.delete(:serializer) + def get_serializer(resource) + @_serializer ||= @_serializer_opts.delete(:serializer) @_serializer ||= ActiveModel::Serializer.serializer_for(resource) - if options.key?(:each_serializer) - options[:serializer] = options.delete(:each_serializer) + if @_serializer_opts.key?(:each_serializer) + @_serializer_opts[:serializer] = @_serializer_opts.delete(:each_serializer) end @_serializer end + def use_adapter? + !(@_adapter_opts.key?(:adapter) && !@_adapter_opts[:adapter]) + end + [:_render_option_json, :_render_with_renderer_json].each do |renderer_method| define_method renderer_method do |resource, options| - - adapter_opts, serializer_opts = + @_adapter_opts, @_serializer_opts = options.partition { |k, _| ADAPTER_OPTION_KEYS.include? k }.map { |h| Hash[h] } - if (serializer = get_serializer(resource, serializer_opts)) + if use_adapter? && (serializer = get_serializer(resource)) # omg hax - object = serializer.new(resource, serializer_opts) - adapter = ActiveModel::Serializer.adapter.new(object, adapter_opts) + object = serializer.new(resource, @_serializer_opts) + adapter = ActiveModel::Serializer::Adapter.create(object, @_adapter_opts) super(adapter, options) else super(resource, options) diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index 9f9c91db..edf4e024 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -90,8 +90,7 @@ module ActiveModel def self.adapter adapter_class = case config.adapter when Symbol - class_name = "ActiveModel::Serializer::Adapter::#{config.adapter.to_s.classify}" - class_name.safe_constantize + ActiveModel::Serializer::Adapter.adapter_class(config.adapter) when Class config.adapter end diff --git a/lib/active_model/serializer/adapter.rb b/lib/active_model/serializer/adapter.rb index 84649285..bf546097 100644 --- a/lib/active_model/serializer/adapter.rb +++ b/lib/active_model/serializer/adapter.rb @@ -20,6 +20,16 @@ module ActiveModel def as_json(options = {}) serializable_hash(options) end + + def self.create(resource, options = {}) + override = options.delete(:adapter) + klass = override ? adapter_class(override) : ActiveModel::Serializer.adapter + klass.new(resource, options) + end + + def self.adapter_class(adapter) + "ActiveModel::Serializer::Adapter::#{adapter.to_s.classify}".safe_constantize + end end end end diff --git a/test/action_controller/adapter_selector_test.rb b/test/action_controller/adapter_selector_test.rb new file mode 100644 index 00000000..96d7dd52 --- /dev/null +++ b/test/action_controller/adapter_selector_test.rb @@ -0,0 +1,41 @@ +require 'test_helper' + +module ActionController + module Serialization + class AdapterSelectorTest < ActionController::TestCase + class MyController < ActionController::Base + def render_using_default_adapter + @profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) + render json: @profile + end + + def render_using_adapter_override + @profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) + render json: @profile, adapter: :json_api + end + + def render_skipping_adapter + @profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) + render json: @profile, adapter: false + end + end + + tests MyController + + def test_render_using_default_adapter + get :render_using_default_adapter + assert_equal '{"name":"Name 1","description":"Description 1"}', response.body + end + + def test_render_using_adapter_override + get :render_using_adapter_override + assert_equal '{"profiles":{"name":"Name 1","description":"Description 1"}}', response.body + end + + def test_render_skipping_adapter + get :render_skipping_adapter + assert_equal '{"attributes":{"name":"Name 1","description":"Description 1","comments":"Comments 1"}}', response.body + end + end + end +end diff --git a/test/action_controller/json_api_linked_test.rb b/test/action_controller/json_api_linked_test.rb index d0138538..dca55e67 100644 --- a/test/action_controller/json_api_linked_test.rb +++ b/test/action_controller/json_api_linked_test.rb @@ -28,54 +28,34 @@ module ActionController @second_comment.author = nil end - def with_json_api_adapter - old_adapter = ActiveModel::Serializer.config.adapter - ActiveModel::Serializer.config.adapter = :json_api - yield - ensure - ActiveModel::Serializer.config.adapter = old_adapter - end - def render_resource_without_include - with_json_api_adapter do - setup_post - render json: @post - end + setup_post + render json: @post, adapter: :json_api end def render_resource_with_include - with_json_api_adapter do - setup_post - render json: @post, include: 'author' - end + setup_post + render json: @post, include: 'author', adapter: :json_api end def render_resource_with_nested_include - with_json_api_adapter do - setup_post - render json: @post, include: 'comments.author' - end + setup_post + render json: @post, include: 'comments.author', adapter: :json_api end def render_resource_with_nested_has_many_include - with_json_api_adapter do - setup_post - render json: @post, include: 'author,author.roles' - end + setup_post + render json: @post, include: 'author,author.roles', adapter: :json_api end def render_collection_without_include - with_json_api_adapter do - setup_post - render json: [@post] - end + setup_post + render json: [@post], adapter: :json_api end def render_collection_with_include - with_json_api_adapter do - setup_post - render json: [@post], include: 'author,comments' - end + setup_post + render json: [@post], include: 'author,comments', adapter: :json_api end end diff --git a/test/action_controller/serialization_test.rb b/test/action_controller/serialization_test.rb index fbbb48f9..a4c20a54 100644 --- a/test/action_controller/serialization_test.rb +++ b/test/action_controller/serialization_test.rb @@ -25,13 +25,9 @@ module ActionController end def render_using_custom_root_in_adapter_with_a_default - old_adapter = ActiveModel::Serializer.config.adapter # JSON-API adapter sets root by default - ActiveModel::Serializer.config.adapter = ActiveModel::Serializer::Adapter::JsonApi @profile = Profile.new({ name: 'Name 1', description: 'Description 1', comments: 'Comments 1' }) - render json: @profile, root: "profile" - ensure - ActiveModel::Serializer.config.adapter = old_adapter + render json: @profile, root: "profile", adapter: :json_api end def render_array_using_implicit_serializer diff --git a/test/adapter_test.rb b/test/adapter_test.rb index 737e0c4e..d76559d5 100644 --- a/test/adapter_test.rb +++ b/test/adapter_test.rb @@ -18,6 +18,26 @@ module ActiveModel def test_serializer assert_equal @serializer, @adapter.serializer end + + def test_adapter_class_for_known_adapter + klass = ActiveModel::Serializer::Adapter.adapter_class(:json_api) + assert_equal ActiveModel::Serializer::Adapter::JsonApi, klass + end + + def test_adapter_class_for_unknown_adapter + klass = ActiveModel::Serializer::Adapter.adapter_class(:json_simple) + assert_nil klass + end + + def test_create_adapter + adapter = ActiveModel::Serializer::Adapter.create(@serializer) + assert_equal ActiveModel::Serializer::Adapter::Json, adapter.class + end + + def test_create_adapter_with_override + adapter = ActiveModel::Serializer::Adapter.create(@serializer, { adapter: :json_api}) + assert_equal ActiveModel::Serializer::Adapter::JsonApi, adapter.class + end end end end diff --git a/test/serializers/configuration_test.rb b/test/serializers/configuration_test.rb index eec00436..9c6c5fea 100644 --- a/test/serializers/configuration_test.rb +++ b/test/serializers/configuration_test.rb @@ -7,7 +7,7 @@ module ActiveModel assert_equal ActiveModel::Serializer::ArraySerializer, ActiveModel::Serializer.config.array_serializer end - def test_adapter + def test_default_adapter assert_equal :json, ActiveModel::Serializer.config.adapter end end