class Eco::API::Common::People::PersonEntryAttributeMapper
@attr_reader direct_attrs [Array<String>] only those internal attributes present in the person entry that do not have an internal/external name mapping.
def account_attrs(data = nil)
-
(Array- account attributes that are present in the person entry.)
def account_attrs(data = nil) return @account_attrs unless data || !@account_attrs @person_parser.target_attrs_account(internal_attrs(data)).tap do |account_attrs| @account_attrs ||= account_attrs end end
def aliased_attrs
-
(Array- only those internal attributes)
def aliased_attrs return @aliased_attrs if @aliased_attrs if parsing? init_attr_trackers else @aliased_attrs = @attr_map.list(:internal) end @aliased_attrs end
def all_model_attrs(data = nil)
-
(Array- all the attrs that are present in the person entry.)
def all_model_attrs(data = nil) core_attrs(data) | account_attrs(data) | details_attrs(data) end
def attributes(value)
def attributes(value) case value when CSV::Row value&.headers when Hash value&.keys when PersonEntry @person_parser.target_attrs_core else [] end end
def cached_warning(*args)
def cached_warning(*args) unless exists = !!@@cached_warnings.dig(*args) args.reduce(@@cached_warnings) do |cache, level| cache[level] = {} unless cache.key?(level) cache[level] end end exists end
def core_attrs(data = nil)
-
(Array- core attributes that are present in the person entry.)
def core_attrs(data = nil) return @core_attrs unless data || !@core_attrs @person_parser.target_attrs_core(internal_attrs(data)).tap do |core_attrs| @core_attrs ||= core_attrs end end
def debug(var, msg)
def debug(var, msg) return unless DEBUG puts "\n • #{msg}:" pp var end
def details_attrs(data = nil)
-
(Array- schema details attributes that are present in the person entry.)
def details_attrs(data = nil) return @details_attrs unless data || !@details_attrs @person_parser.target_attrs_details(internal_attrs(data)).tap do |details_attrs| @details_attrs ||= details_attrs end end
def external_attr?(attr)
def external_attr?(attr) return false unless @attr_map @attr_map.external?(attr) end
def fatal(msg)
def fatal(msg) logger.fatal(msg) raise msg end
def init_attr_trackers # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
when parsing:
def init_attr_trackers # rubocop:disable Metrics/AbcSize, Metrics/MethodLength # (def) all internal attributes we can expect def_all_attrs = @person_parser.all_model_attrs # (def) internal attrs with no aliasing nor parser definition (expected to be direct) def_unlinked = @person_parser.undefined_model_attrs.reject do |attr| to_external(attr) end debug( def_unlinked, "(def_unlinked) expected to be direct (not parser defined nor aliased)" ) # (data) data attributes (actual attributes of the entry) data_attrs = attributes(@external_entry) # (data) attributes of the data that COULD come directly as internal attribute names # => WARNING: this includes ext direct attrs that may be aliased to other int attrs data_direct_attrs_raw = data_attrs & def_all_attrs # (data) direct (int) ext data attrs mapped data_direct_mapped = data_direct_attrs_raw.select { |attr| external_attr?(attr) } # (data) direct (int) ext data attrs mapped to themselves data_direct_self_mapped = data_direct_attrs_raw.select { |attr| self_mapped_attr?(attr) } # (data) direct (int) ext data attrs mapped only to another attr data_direct_renamed = data_direct_mapped - data_direct_self_mapped # (data) attributes of the data that come directly as internal attribute names data_direct_attrs = data_direct_attrs_raw - data_direct_renamed debug( data_direct_attrs, "(data_direct_attrs) attributes of the data that come directly as internal attribute names (data_direct_attrs_raw - data_direct_renamed)" # rubocop:disable Layout/LineLength ) # (def) configured as alised (int <-> ext attrs) + accept/include int attrs as ext attrs def_int_aliased_raw = def_all_attrs.select { |attr| to_external(attr) } # (def) aliasable int attrs of the input data (excludes int attrs direct as ext attrs that got renamed) def_int_aliased = def_int_aliased_raw - data_direct_renamed # (def) ext attrs of the data's aliasable int attrs (def_int_aliased) def_ext_alias = def_int_aliased.map { |attr| to_external(attr) } # (def) those ext attrs that map to multiple int attrs # def_ext_multi_alias = def_ext_alias.detect { |attr| def_ext_alias.count(attr) > 1 } # (def) those ext attrs that are direct, mapt to themselves and some other # (data) virtual attrs (external alias of non native internal attr in data): data_vi_ext_alias = data_attrs.select do |attr| !def_ext_alias.include?(attr) && @attr_map&.external?(attr) end # (data) virtual internal attrs (the internal names of those virtual attrs) data_vi_int_aliased = data_vi_ext_alias.map do |attr| # to_internal(attr) can't be used here, becauase virtual fields would get filtered out, # as they are not recognized by @parser.all_model_attrs.include?(attr) @attr_map.to_internal(attr) end.compact # (data) int attrs that come aliased in the current data # => modify aliased based on those that came directly as internal attrs in the entry data_def_int_aliased = (def_int_aliased - data_direct_attrs) | data_direct_self_mapped data_def_ext_alias = data_def_int_aliased.map { |attr| to_external(attr) } # (data) ext attrs of the data that come aliased data_ext_alias = data_def_ext_alias & data_attrs # (data) all internal attributes that come aliased, with given the entry aliased_attrs = data_def_int_aliased | data_vi_int_aliased # render in the order that defines the model @aliased_attrs = (def_all_attrs & aliased_attrs) | (aliased_attrs - def_all_attrs) debug( data_vi_int_aliased, "(data_vi_int_aliased) virtual internal attrs (internal names of those virtual attrs)" ) debug( def_int_aliased, "(def_int_aliased) aliasable int attrs of the input data (excludes int attrs direct as ext attrs that got renamed)" # rubocop:disable Layout/LineLength ) debug( def_ext_alias, "(def_ext_alias) ext attrs of the data's aliasable int attrs (def_int_aliased)" ) debug( data_def_int_aliased, "(data_def_int_aliased) int attrs that come aliased in the current data ((def_int_aliased - data_direct_attrs) | data_direct_self_mapped)" # rubocop:disable Layout/LineLength ) debug( data_ext_alias, "(data_ext_alias) ext attrs of the data that come aliased (data_def_ext_alias & data_attrs)" ) debug( aliased_attrs, "(aliased_attrs) all internal attributes that come aliased, with given the entry (data_def_int_aliased | data_vi_int_aliased)" # rubocop:disable Layout/LineLength ) # (data) all those ext attrs present that will require aliasing #data_ext_alias_all = data_def_ext_alias + data_vi_ext_alias data_ext_alias_all = data_ext_alias | data_vi_ext_alias debug( data_ext_alias_all, "(data_ext_alias_all) all those ext attrs present that will require aliasing (data_ext_alias | data_vi_ext_alias)" # rubocop:disable Layout/LineLength ) # those that are direct external to internal: data_ext_direct = data_attrs - data_ext_alias_all # (data) attributes that do not require aliasing # to avoid collisions between internal names: direct_attrs = data_ext_direct # render in the order that defines the model @direct_attrs = (def_all_attrs & direct_attrs) | (direct_attrs - def_all_attrs) # (data) attributes that are being aliased internal_attrs = @aliased_attrs | @direct_attrs # render in the order that defines the model @internal_attrs = (def_all_attrs & internal_attrs) | (internal_attrs - def_all_attrs) debug( @direct_attrs, "(@direct_attrs) attributes that do not require aliasing (data_attrs - data_ext_alias_all)" ) debug( @internal_attrs, "(@internal_attrs) aliased_attrs | @direct_attrs" ) end
def initialize(data, person_parser:, attr_map:, logger: ::Logger.new(IO::NULL))
-
logger(Common::Session::Logger, ::Logger) -- object to manage logs. -
attr_map(Eco::Data::Mapper) -- mapper to translate attribute names -
person_parser(Common::People::PersonParser) -- parser/serializer -
data(Hash, Ecoportal::API::V1::Person) -- `Person` object to
Other tags:
- Note: -
def initialize(data, person_parser:, attr_map:, logger: ::Logger.new(IO::NULL)) msg = "Constructor needs a PersonParser. Given: #{person_parser.class}" raise ArgumentError, msg unless person_parser.is_a?(Eco::API::Common::People::PersonParser) msg = "Expecting Mapper object. Given: #{attr_map.class}" raise ArgumentError, msg if attr_map && !attr_map.is_a?(Eco::Data::Mapper) @source = data @person_parser = person_parser @attr_map = attr_map @logger = logger if parsing? @external_entry = data else # SERIALIZING @person = data end end
def internal_attrs(data = nil)
-
(Array- all the internally named attributes that the person entry has.)
def internal_attrs(data = nil) return @internal_attrs unless data || !@internal_attrs if parsing? init_attr_trackers unless @internal_attrs return data.keys & @person_parser.all_model_attrs if data else @internal_attrs = @person_parser.all_model_attrs end @internal_attrs end
def logger
def logger @logger || ::Logger.new(IO::NULL) end
def parsing?
-
(Boolean)- returns `true` if we are **parsing**, `false` otherwise.
def parsing? !@source.is_a?(Ecoportal::API::V1::Person) end
def self_mapped_attr?(attr)
def self_mapped_attr?(attr) return false unless @attr_map @attr_map.self_mapped?(attr) end
def serializing?
-
(Boolean)- returns `true` if we are **serializing**, `false` otherwise.
def serializing? !parsing? end
def to_external(value)
-
(String, nil, Array- String, nil, Array
Parameters:
-
value(String, Array) -- value(s) to be translated or aliased into external ones.
Other tags:
- Note: -
def to_external(value) return value unless @attr_map attr = value case value when Array return value.map do |v| to_external(v) end.compact when String if @attr_map.internal?(value) attr = @attr_map.to_external(value) elsif @attr_map.internal?(value.strip) unless cached_warning("internal", "spaces", value) msg = "The internal person field name '#{value}' contains " msg << "additional spaces in the reference file" logger.warn(msg) end attr = @attr_map.to_external(value.strip) elsif [value, value.strip, value.strip.downcase].any? {|val| @attr_map.external?(val)} unless cached_warning("internal", "reversed", value) msg = "The mapper [external, internal] attribute names " msg << "may be declared reversedly for " msg << "INTERNAL attribute: '#{value}'" logger.info(msg) end end end return nil unless !@external_entry || attributes(@external_entry).include?(attr) attr end
def to_internal(value)
-
(String, nil, Array- String, nil, Array
Parameters:
-
value(String, Array) -- value(s) to be translated into internal names.
Other tags:
- Note: -
def to_internal(value) # TODO: check PersonEntry#to_internal and #to_external in init_attr_trackers # => when attr_map is avoided, it doesn't work as it should return value unless @attr_map attr = value case value when Array return value.map do |v| to_internal(v) end.compact when String if @attr_map.external?(value) attr = @attr_map.to_internal(value) elsif @attr_map.external?(value.strip) unless cached_warning("external", "spaces", value) msg = "The external person field name '#{value}' contains " msg << "additional spaces in the reference file" logger.warn(msg) end attr = @attr_map.to_internal(value.strip) elsif [value, value.strip, value.strip.downcase].any? {|val| @attr_map.internal?(val)} unless cached_warning("external", "reversed", value) msg = "The mapper [external, internal] attribute names " msg << "may be declared reversedly for EXTERNAL attribute: '#{value}'" logger.info(msg) end end end return nil unless @person_parser.all_model_attrs.include?(attr) end