diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dd7a819..113c1ec1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ Fixes: - [#1358](https://github.com/rails-api/active_model_serializers/pull/1358) Handle serializer file paths with spaces (@rwstauner, @bf4) Misc: +- [#1370](https://github.com/rails-api/active_model_serializers/pull/1370) Simplify attributes handling via a mixin (@beauby) - [#1233](https://github.com/rails-api/active_model_serializers/pull/1233) Top-level meta and meta_key options no longer handled at serializer level (@beauby) - [#1232](https://github.com/rails-api/active_model_serializers/pull/1232) fields option no longer handled at serializer level (@beauby) - [#1178](https://github.com/rails-api/active_model_serializers/pull/1178) env CAPTURE_STDERR=false lets devs see hard failures (@bf4) diff --git a/lib/active_model/serializer/attribute.rb b/lib/active_model/serializer/attribute.rb new file mode 100644 index 00000000..5c9893ca --- /dev/null +++ b/lib/active_model/serializer/attribute.rb @@ -0,0 +1,13 @@ +module ActiveModel + class Serializer + Attribute = Struct.new(:name, :block) do + def value(serializer) + if block + serializer.instance_eval(&block) + else + serializer.read_attribute_for_serialization(name) + end + end + end + end +end diff --git a/lib/active_model/serializer/attributes.rb b/lib/active_model/serializer/attributes.rb index 81f6e49a..f57ab205 100644 --- a/lib/active_model/serializer/attributes.rb +++ b/lib/active_model/serializer/attributes.rb @@ -1,55 +1,24 @@ module ActiveModel class Serializer module Attributes - # @api private - class Attribute - delegate :call, to: :reader - - attr_reader :name, :reader - - def initialize(name) - @name = name - @reader = :no_reader - end - - def self.build(name, block) - if block - AttributeBlock.new(name, block) - else - AttributeReader.new(name) - end - end - end - # @api private - class AttributeReader < Attribute - def initialize(name) - super(name) - @reader = ->(instance) { instance.read_attribute_for_serialization(name) } - end - end - # @api private - class AttributeBlock < Attribute - def initialize(name, block) - super(name) - @reader = ->(instance) { instance.instance_eval(&block) } - end - end - extend ActiveSupport::Concern included do with_options instance_writer: false, instance_reader: false do |serializer| - serializer.class_attribute :_attribute_mappings # @api private : maps attribute key names to names to names of implementing methods, @see #attribute - self._attribute_mappings ||= {} + serializer.class_attribute :_attributes_data # @api private + self._attributes_data ||= {} end + extend ActiveSupport::Autoload + autoload :Attribute + # Return the +attributes+ of +object+ as presented # by the serializer. def attributes(requested_attrs = nil, reload = false) @attributes = nil if reload - @attributes ||= self.class._attribute_mappings.each_with_object({}) do |(key, attribute_mapping), hash| + @attributes ||= self.class._attributes_data.each_with_object({}) do |(key, attr), hash| next unless requested_attrs.nil? || requested_attrs.include?(key) - hash[key] = attribute_mapping.call(self) + hash[key] = attr.value(self) end end end @@ -57,7 +26,7 @@ module ActiveModel module ClassMethods def inherited(base) super - base._attribute_mappings = _attribute_mappings.dup + base._attributes_data = _attributes_data.dup end # @example @@ -85,14 +54,14 @@ module ActiveModel # end def attribute(attr, options = {}, &block) key = options.fetch(:key, attr) - _attribute_mappings[key] = Attribute.build(attr, block) + _attributes_data[key] = Attribute.new(attr, block) end # @api private - # names of attribute methods + # keys of attributes # @see Serializer::attribute def _attributes - _attribute_mappings.keys + _attributes_data.keys end # @api private @@ -100,10 +69,10 @@ module ActiveModel # @see Serializer::attribute # @see Adapter::FragmentCache#fragment_serializer def _attributes_keys - _attribute_mappings - .each_with_object({}) do |(key, attribute_mapping), hash| - next if key == attribute_mapping.name - hash[attribute_mapping.name] = { key: key } + _attributes_data + .each_with_object({}) do |(key, attr), hash| + next if key == attr.name + hash[attr.name] = { key: key } end end end