lib/alba/resource.rb in alba-2.0.1 vs lib/alba/resource.rb in alba-2.1.0

- old
+ new

@@ -39,11 +39,10 @@ # @param within [Object, nil, false, true] determines what associations to be serialized. If not set, it serializes all associations. def initialize(object, params: {}, within: WITHIN_DEFAULT) @object = object @params = params @within = within - @method_existence = {} # Cache for `respond_to?` result DSLS.each_key { |name| instance_variable_set("@#{name}", self.class.__send__(name)) } end # Serialize object into JSON string # @@ -147,20 +146,20 @@ k = collection? ? _key_for_collection : _key transforming_root_key? ? transform_key(k) : k end def _key_for_collection - if Alba.inferring + if Alba.inflector @_key_for_collection == true ? resource_name(pluralized: true) : @_key_for_collection.to_s else @_key_for_collection == true ? raise_root_key_inference_error : @_key_for_collection.to_s end end # @return [String] def _key - if Alba.inferring + if Alba.inflector @_key == true ? resource_name(pluralized: false) : @_key.to_s else @_key == true ? raise_root_key_inference_error : @_key.to_s end end @@ -172,100 +171,108 @@ underscore_name = inflector.underscore(name) pluralized ? inflector.pluralize(underscore_name) : underscore_name end def raise_root_key_inference_error - raise Alba::Error, 'You must call Alba.enable_inference! to set root_key to true for inferring root key.' + raise Alba::Error, 'You must set inflector when setting root key as true.' end def transforming_root_key? @_transforming_root_key end def converter - lambda do |object| - attributes_to_hash(object, {}) + lambda do |obj| + attributes_to_hash(obj, {}) end end def collection_converter - lambda do |object, a| + lambda do |obj, a| a << {} h = a.last - attributes_to_hash(object, h) + attributes_to_hash(obj, h) a end end - def attributes_to_hash(object, hash) + def attributes_to_hash(obj, hash) attributes.each do |key, attribute| - set_key_and_attribute_body_from(object, key, attribute, hash) + set_key_and_attribute_body_from(obj, key, attribute, hash) rescue ::Alba::Error, FrozenError, TypeError raise rescue StandardError => e - handle_error(e, object, key, attribute, hash) + handle_error(e, obj, key, attribute, hash) end hash end # This is default behavior for getting attributes for serialization # Override this method to filter certain attributes def attributes @_attributes end - def set_key_and_attribute_body_from(object, key, attribute, hash) + # Default implementation for selecting attributes + # Override this method to filter attributes based on key and value + def select(_key, _value) + true + end + + def set_key_and_attribute_body_from(obj, key, attribute, hash) key = transform_key(key) - value = fetch_attribute(object, key, attribute) + value = fetch_attribute(obj, key, attribute) + return unless select(key, value) + hash[key] = value unless value == Alba::REMOVE_KEY end - def handle_error(error, object, key, attribute, hash) + def handle_error(error, obj, key, attribute, hash) on_error = @_on_error || :raise case on_error # rubocop:disable Style/MissingElse when :raise, nil then raise(error) when :nullify then hash[key] = nil when :ignore then nil when Proc - key, value = on_error.call(error, object, key, attribute, self.class) + key, value = on_error.call(error, obj, key, attribute, self.class) hash[key] = value end end # @return [Symbol] def transform_key(key) # rubocop:disable Metrics/CyclomaticComplexity key = key.to_s return key if @_transform_type == :none || key.empty? # We can skip transformation inflector = Alba.inflector - raise Alba::Error, 'Inflector is nil. You can set inflector with `Alba.enable_inference!(with: :active_support)` for example.' unless inflector + raise Alba::Error, 'Inflector is nil. You must set inflector before transforming keys.' unless inflector case @_transform_type # rubocop:disable Style/MissingElse when :camel then inflector.camelize(key) when :lower_camel then inflector.camelize_lower(key) when :dash then inflector.dasherize(key) when :snake then inflector.underscore(key) end end - def fetch_attribute(object, key, attribute) # rubocop:disable Metrics/CyclomaticComplexity + def fetch_attribute(obj, key, attribute) # rubocop:disable Metrics/CyclomaticComplexity value = case attribute - when Symbol then fetch_attribute_from_object_and_resource(object, attribute) - when Proc then instance_exec(object, &attribute) - when Alba::Association then yield_if_within(attribute.name.to_sym) { |within| attribute.to_h(object, params: params, within: within) } - when TypedAttribute, NestedAttribute then attribute.value(object) - when ConditionalAttribute then attribute.with_passing_condition(resource: self, object: object) { |attr| fetch_attribute(object, key, attr) } + when Symbol then fetch_attribute_from_object_and_resource(obj, attribute) + when Proc then instance_exec(obj, &attribute) + when Alba::Association then yield_if_within(attribute.name.to_sym) { |within| attribute.to_h(obj, params: params, within: within) } + when TypedAttribute, NestedAttribute then attribute.value(obj) + when ConditionalAttribute then attribute.with_passing_condition(resource: self, object: obj) { |attr| fetch_attribute(obj, key, attr) } else raise ::Alba::Error, "Unsupported type of attribute: #{attribute.class}" end - value.nil? && nil_handler ? instance_exec(object, key, attribute, &nil_handler) : value + value.nil? && nil_handler ? instance_exec(obj, key, attribute, &nil_handler) : value end - def fetch_attribute_from_object_and_resource(object, attribute) - has_method = @method_existence[attribute] - has_method = @method_existence[attribute] = object.respond_to?(attribute) if has_method.nil? - has_method ? object.__send__(attribute) : __send__(attribute, object) + def fetch_attribute_from_object_and_resource(obj, attribute) + obj.__send__(attribute) + rescue NoMethodError + __send__(attribute, obj) end def nil_handler @_on_nil end @@ -311,10 +318,10 @@ # @param attrs [Array<String, Symbol>] # @param if [Proc] condition to decide if it should serialize these attributes # @param attrs_with_types [Hash<[Symbol, String], [Array<Symbol, Proc>, Symbol]>] # attributes with name in its key and type and optional type converter in its value # @return [void] - def attributes(*attrs, if: nil, **attrs_with_types) # rubocop:disable Naming/MethodParameterName + def attributes(*attrs, if: nil, **attrs_with_types) if_value = binding.local_variable_get(:if) assign_attributes(attrs, if_value) assign_attributes_with_types(attrs_with_types, if_value) end