okuramasafumi/alba

View on GitHub
lib/alba/conditional_attribute.rb

Summary

Maintainability
A
0 mins
Test Coverage
require_relative 'association'
require_relative 'constants'
require 'ostruct'

module Alba
  # Represents attribute with `if` option
  # @api private
  class ConditionalAttribute
    # @param body [Symbol, Proc, Alba::Association, Alba::TypedAttribute] real attribute wrapped with condition
    # @param condition [Symbol, Proc] condition to check
    def initialize(body:, condition:)
      @body = body
      @condition = condition
    end

    # Returns attribute body if condition passes
    #
    # @param resource [Alba::Resource]
    # @param object [Object] needed for collection, each object from collection
    # @return [Alba::REMOVE_KEY, Object] REMOVE_KEY if condition is unmet, fetched attribute otherwise
    def with_passing_condition(resource:, object: nil)
      return Alba::REMOVE_KEY unless condition_passes?(resource, object)

      fetched_attribute = yield(@body)
      return fetched_attribute unless with_two_arity_proc_condition

      return Alba::REMOVE_KEY unless resource.instance_exec(object, objectize(fetched_attribute), &@condition)

      fetched_attribute
    end

    private

    def condition_passes?(resource, object)
      if @condition.is_a?(Proc)
        arity = @condition.arity
        # We can return early to skip fetch_attribute if arity is 1
        # When arity is 2, we check the condition later
        return true if arity >= 2
        return false if arity <= 1 && !resource.instance_exec(object, &@condition)

        true
      else # Symbol
        resource.__send__(@condition)
      end
    end

    def with_two_arity_proc_condition
      @condition.is_a?(Proc) && @condition.arity >= 2
    end

    # OpenStruct is used as a simple solution for converting Hash or Array of Hash into an object
    # Using OpenStruct is not good in general, but in this case there's no other solution
    def objectize(fetched_attribute)
      return fetched_attribute unless @body.is_a?(Alba::Association)

      if fetched_attribute.is_a?(Array)
        fetched_attribute.map do |hash|
          OpenStruct.new(hash)
        end
      else
        OpenStruct.new(fetched_attribute)
      end
    end
  end
end