diff --git a/lib/action_controller/serialization.rb b/lib/action_controller/serialization.rb index d157e2f9d..edb5bc0e0 100644 --- a/lib/action_controller/serialization.rb +++ b/lib/action_controller/serialization.rb @@ -6,29 +6,32 @@ module Serialization 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 dbc2060d9..51834830f 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -83,8 +83,7 @@ def self.serializer_for(resource) 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 846492857..bf546097e 100644 --- a/lib/active_model/serializer/adapter.rb +++ b/lib/active_model/serializer/adapter.rb @@ -20,6 +20,16 @@ def serializable_hash(options = {}) 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 000000000..96d7dd524 --- /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 d01385384..dca55e67b 100644 --- a/test/action_controller/json_api_linked_test.rb +++ b/test/action_controller/json_api_linked_test.rb @@ -28,54 +28,34 @@ def setup_post @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 fbbb48f98..e8ac8f490 100644 --- a/test/action_controller/serialization_test.rb +++ b/test/action_controller/serialization_test.rb @@ -15,23 +15,15 @@ def render_using_custom_root end def render_using_default_adapter_root - 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 - ensure - ActiveModel::Serializer.config.adapter = old_adapter + render json: @profile, adapter: :json_api 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 737e0c4e7..d76559d55 100644 --- a/test/adapter_test.rb +++ b/test/adapter_test.rb @@ -18,6 +18,26 @@ def test_serializable_hash_is_abstract_method 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 eec004364..9c6c5feab 100644 --- a/test/serializers/configuration_test.rb +++ b/test/serializers/configuration_test.rb @@ -7,7 +7,7 @@ def test_array_serializer 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