lib/serega/attribute_normalizer.rb
# frozen_string_literal: true
class Serega
#
# Prepares provided attribute options
#
class SeregaAttributeNormalizer
#
# AttributeNormalizer instance methods
#
module AttributeNormalizerInstanceMethods
# Attribute initial params
# @return [Hash] Attribute initial params
attr_reader :init_name, :init_opts, :init_block
#
# Instantiates attribute options normalizer
#
# @param initials [Hash] new attribute options
#
# @return [SeregaAttributeNormalizer] Instantiated attribute options normalizer
#
def initialize(initials)
@init_name = initials[:name]
@init_opts = initials[:opts]
@init_block = initials[:block]
end
#
# Symbolized initial attribute name
#
# @return [Symbol] Attribute normalized name
#
def name
@name ||= prepare_name
end
#
# Symbolized initial attribute method name
#
# @return [Symbol] Attribute normalized method name
#
def method_name
@method_name ||= prepare_method_name
end
#
# Combines all options to return single block that will be used to find
# attribute value during serialization
#
# @return [#call] Attribute normalized callable value block
#
def value_block
@value_block ||= prepare_value_block
end
#
# Shows if attribute is specified to be hidden
#
# @return [Boolean, nil] if attribute must be hidden by default
#
def hide
return @hide if instance_variable_defined?(:@hide)
@hide = prepare_hide
end
#
# Shows if attribute is specified to be a one-to-many relationship
#
# @return [Boolean, nil] if attribute is specified to be a one-to-many relationship
#
def many
return @many if instance_variable_defined?(:@many)
@many = prepare_many
end
#
# Shows specified attribute serializer
# @return [Serega, String, #callable, nil] specified serializer
#
def serializer
return @serializer if instance_variable_defined?(:@serializer)
@serializer = prepare_serializer
end
#
# Shows the default attribute value. It is a value that replaces found nils.
#
# When custom :default is not specified, we set empty array as default when `many: true` specified
#
# @return [Object] Attribute default value
#
def default
return @default if instance_variable_defined?(:@default)
@default = prepare_default
end
private
def prepare_name
init_name.to_sym
end
#
# Patched in:
# - plugin :formatters (wraps resulted block in formatter block and formats :const values)
#
def prepare_value_block
value_block =
prepare_init_block ||
prepare_value_option_block ||
prepare_const_block ||
prepare_delegate_block ||
prepare_keyword_block
prepare_value_block_with_default(value_block)
end
#
# Patched in:
# - plugin :preloads (returns true by default if config option auto_hide_attribute_with_preloads is enabled)
# - plugin :batch (returns true by default if auto_hide option was set and attribute has batch loader)
#
def prepare_hide
init_opts[:hide]
end
def prepare_many
init_opts[:many]
end
def prepare_serializer
init_opts[:serializer]
end
def prepare_method_name
(init_opts[:method] || init_name).to_sym
end
def prepare_const_block
return unless init_opts.key?(:const)
const = init_opts[:const]
proc { const }
end
def prepare_keyword_block
key_method_name = method_name
proc do |object|
object.public_send(key_method_name)
end
end
def prepare_init_block
prepare_callable_proc(init_block)
end
def prepare_value_option_block
prepare_callable_proc(init_opts[:value])
end
def prepare_callable_proc(callable)
return unless callable
params_count = SeregaUtils::ParamsCount.call(callable, max_count: 2)
case params_count
when 0 then proc { |obj, _ctx| callable.call }
when 1 then proc { |obj, _ctx| callable.call(obj) }
else callable
end
end
def prepare_value_block_with_default(callable)
default_value = default
return callable if default_value.nil?
proc { |obj, ctx|
res = callable.call(obj, ctx)
res.nil? ? default_value : res
}
end
def prepare_default
init_opts.fetch(:default) { many ? FROZEN_EMPTY_ARRAY : nil }
end
def prepare_delegate_block
delegate = init_opts[:delegate]
return unless delegate
key_method_name = delegate[:method] || method_name
delegate_to = delegate[:to]
allow_nil = delegate.fetch(:allow_nil) { self.class.serializer_class.config.delegate_default_allow_nil }
if allow_nil
proc do |object|
object.public_send(delegate_to)&.public_send(key_method_name)
end
else
proc do |object|
object.public_send(delegate_to).public_send(key_method_name)
end
end
end
end
extend Serega::SeregaHelpers::SerializerClassHelper
include AttributeNormalizerInstanceMethods
end
end