diff --git a/CHANGELOG.md b/CHANGELOG.md index 06534b3de..304707bbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ Breaking changes: Features: +- [#1618](https://github.com/rails-api/active_model_serializers/issues/1618) Get collection root key for + empty collection from explicit serializer option, when possible. (@bf4) - [#1574](https://github.com/rails-api/active_model_serializers/pull/1574) Provide key translation. (@remear) - [#1494](https://github.com/rails-api/active_model_serializers/pull/1494) Make serializers serializalbe (using the Attributes adapter by default). (@bf4) diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index d78b873fb..79478abb7 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -182,7 +182,7 @@ def as_json(adapter_opts = nil) # Used by adapter as resource root. def json_key - root || object.class.model_name.to_s.underscore + root || _type || object.class.model_name.to_s.underscore end def read_attribute_for_serialization(attr) diff --git a/lib/active_model/serializer/collection_serializer.rb b/lib/active_model/serializer/collection_serializer.rb index ebaded3d7..f026ebfe8 100644 --- a/lib/active_model/serializer/collection_serializer.rb +++ b/lib/active_model/serializer/collection_serializer.rb @@ -8,15 +8,10 @@ class CollectionSerializer attr_reader :object, :root def initialize(resources, options = {}) - @root = options[:root] - @object = resources - + @object = resources + @options = options + @root = options[:root] serializer_context_class = options.fetch(:serializer_context_class, ActiveModel::Serializer) - - if resources.blank? && options[:serializer] - @each_serializer = options[:serializer] - end - @serializers = resources.map do |resource| serializer_class = options.fetch(:serializer) { serializer_context_class.serializer_for(resource) } @@ -32,9 +27,28 @@ def success? true end + # TODO: unify naming of root, json_key, and _type. Right now, a serializer's + # json_key comes from the root option or the object's model name, by default. + # But, if a dev defines a custom `json_key` method with an explicit value, + # we have no simple way to know that it is safe to call that instance method. + # (which is really a class property at this point, anyhow). + # rubocop:disable Metrics/CyclomaticComplexity + # Disabling cop since it's good to highlight the complexity of this method by + # including all the logic right here. def json_key - root || derived_root || guess_root || default_root + return root if root + # 1. get from options[:serializer] for empty resource collection + key = object.empty? && + (explicit_serializer_class = options[:serializer]) && + explicit_serializer_class._type + # 2. get from first serializer instance in collection + key ||= (serializer = serializers.first) && serializer.json_key + # 3. get from collection name, if a named collection + key ||= object.respond_to?(:name) ? object.name && object.name.underscore : nil + # 4. key may be nil for empty collection and no serializer option + key && key.pluralize end + # rubocop:enable Metrics/CyclomaticComplexity def paginated? object.respond_to?(:current_page) && @@ -44,21 +58,7 @@ def paginated? protected - attr_reader :serializers - - private - - def derived_root - serializers.first.try(:json_key).try(:pluralize) - end - - def default_root - object.try(:name).try(:underscore).try(:pluralize) - end - - def guess_root - @each_serializer.try(:allocate).try(:json_key).try(:pluralize) - end + attr_reader :serializers, :options end end end diff --git a/test/collection_serializer_test.rb b/test/collection_serializer_test.rb index 833867b03..5e5267e4e 100644 --- a/test/collection_serializer_test.rb +++ b/test/collection_serializer_test.rb @@ -3,6 +3,10 @@ module ActiveModel class Serializer class CollectionSerializerTest < ActiveSupport::TestCase + MessagesSerializer = Class.new(ActiveModel::Serializer) do + type 'messages' + end + def setup @comment = Comment.new @post = Post.new diff --git a/test/fixtures/poro.rb b/test/fixtures/poro.rb index 7fc59ffa5..c7fb831c8 100644 --- a/test/fixtures/poro.rb +++ b/test/fixtures/poro.rb @@ -183,12 +183,6 @@ def json_key end end -MessagesSerializer = Class.new(ActiveModel::Serializer) do - def json_key - 'messages' - end -end - AlternateBlogSerializer = Class.new(ActiveModel::Serializer) do attribute :id attribute :name, key: :title