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.
Attributes
Public Class Methods
Helper class tied to `PersonEntry` that allows to track which attributes of a person entry are present and how they should be mapped between internal and external names if applicable. This class is meant to help in providing a common interface to access entries of source data that come in different formats. @note
- if `data` is a `Person` object, its behaviour is `serialise`. - if `data` is **not** a `Person` object, it does a `parse`. - currently **in rework**, so there may be subtle differences that make it temporarily unstable (yet it is reliable).
@param data [Hash, Ecoportal::API::V1::Person] `Person` object to be serialized or hashed entry to be parsed (note: `CSV::Row` is accepted). @param person_parser [Common::People::PersonParser] parser/serializer of person attributes (it contains a set of attribute parsers). @param attr_map [Eco::Data::Mapper] mapper to translate attribute names from external to internal names and _vice versa_. @param logger [Common::Session::Logger, ::Logger] object to manage logs.
# File lib/eco/api/common/people/person_entry_attribute_mapper.rb, line 23 def initialize(data, person_parser:, attr_map:, logger: ::Logger.new(IO::NULL)) raise "Constructor needs a PersonParser. Given: #{parser}" if !person_parser.is_a?(Eco::API::Common::People::PersonParser) raise "Expecting Mapper object. Given: #{attr_map}" 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
Public Instance Methods
@return [Array<String>] account attributes that are present in the person entry.
# File lib/eco/api/common/people/person_entry_attribute_mapper.rb, line 87 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
@return [Array<String>] all the attrs that are present in the person entry.
# File lib/eco/api/common/people/person_entry_attribute_mapper.rb, line 66 def all_model_attrs(data = nil) core_attrs(data) | account_attrs(data) | details_attrs(data) end
@return [Array<String>] core attributes that are present in the person entry.
# File lib/eco/api/common/people/person_entry_attribute_mapper.rb, line 71 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
@return [Array<String>] schema details attributes that are present in the person entry.
# File lib/eco/api/common/people/person_entry_attribute_mapper.rb, line 79 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
@return [Array<String>] all the internally named attributes that the person entry has.
# File lib/eco/api/common/people/person_entry_attribute_mapper.rb, line 51 def internal_attrs(data = nil) return @internal_attrs unless data || !@internal_attrs if parsing? init_attr_trackers unless @internal_attrs if data return data.keys & @person_parser.all_model_attrs end else @internal_attrs = @person_parser.all_model_attrs end @internal_attrs end
To know if currently the object is in parse or serialize mode. @return [Boolean] returns `true` if we are parsing, `false` otherwise.
# File lib/eco/api/common/people/person_entry_attribute_mapper.rb, line 96 def parsing? !@source.is_a?(Ecoportal::API::V1::Person) end
To know if currently the object is in parse or serialize mode. @return [Boolean] returns `true` if we are serializing, `false` otherwise.
# File lib/eco/api/common/people/person_entry_attribute_mapper.rb, line 102 def serializing? !parsing? end
Serializing helper also used to do a reverse mapping when parsing:
- as there could be _internal attributes_ that shared _external attributes_, - when parsing, you use this helper to recognize the source _external attribute_ of each _internal_ one.
If there no `mapper` defined for the object, it mirrors `value`. If there is a `mapper` defined for the object:
1. if the `value` exists as _internal-, translates it into an _external_ one. 2. if it doesn't exist, returns `nil`.
@note
1. the **scope of attributes** is based on all the attributes defined in the current entry. 2. the attributes recognized by the person parser are those of of the `Person` model (where details attributes depend on the `schema`).
@param value [String, Array<String>] value(s) to be translated or aliased into external ones. @return [String, nil, Array<String] the external name(s) of `value`.
# File lib/eco/api/common/people/person_entry_attribute_mapper.rb, line 156 def to_external(value) return value if !@attr_map attr = value case value when Array return value.map do |v| to_external(v) end.compact when String case when @attr_map.internal?(value) attr = @attr_map.to_external(value) when @attr_map.internal?(value.strip) unless cached_warning("internal", "spaces", value) logger.warn("The internal person field name '#{value}' contains additional spaces in the reference file") end attr = @attr_map.to_external(value.strip) when @attr_map.external?(value) || @attr_map.external?(value.strip) || @attr_map.external?(value.strip.downcase) unless cached_warning("internal", "reversed", value) logger.warn("The mapper [external, internal] attribute names may be declared reversedly for INTERNAL attribute: '#{value}'") end end end return nil unless !@external_entry || attributes(@external_entry).include?(attr) attr end
If there no `mapper` defined for the object, it mirrors `value`. If there is a `mapper` defined for the object:
1. if the `value` exists as external, translates it into an internal one. 2. if it doesn't exist, returns `nil`.
@note
1. the **scope of attributes** is based on all the attributes recognized by the person parser. 2. the attributes recognized by the person parser are those of of the `Person` model (where details attributes depend on the `schema`).
@param value [String, Array<String>] value(s) to be translated into internal names. @return [String, nil, Array<String] the internal name(s) of `value`.
# File lib/eco/api/common/people/person_entry_attribute_mapper.rb, line 115 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 if !@attr_map attr = value case value when Array return value.map do |v| to_internal(v) end.compact when String case when @attr_map.external?(value) attr = @attr_map.to_internal(value) when @attr_map.external?(value.strip) unless cached_warning("external", "spaces", value) logger.warn("The external person field name '#{value}' contains additional spaces in the reference file") end attr = @attr_map.to_internal(value.strip) when @attr_map.internal?(value) || @attr_map.internal?(value.strip) || @attr_map.internal?(value.strip.downcase) unless cached_warning("external", "reversed", value) logger.warn("The mapper [external, internal] attribute names may be declared reversedly for EXTERNAL attribute: '#{value}'") end end end return nil unless @person_parser.all_model_attrs.include?(attr) end
Private Instance Methods
# File lib/eco/api/common/people/person_entry_attribute_mapper.rb, line 237 def attributes(value) case value when CSV::Row value&.headers when Hash value&.keys when PersonEntry @person_parser.target_attrs_core else [] end end
# File lib/eco/api/common/people/person_entry_attribute_mapper.rb, line 255 def cached_warning(*args) unless exists = !!@@cached_warnings.dig(*args) args.reduce(@@cached_warnings) do |cache, level| cache[level] = {} if !cache.key?(level) cache[level] end end exists end
# File lib/eco/api/common/people/person_entry_attribute_mapper.rb, line 265 def fatal(msg) logger.fatal(msg) raise msg end
when parsing:
# File lib/eco/api/common/people/person_entry_attribute_mapper.rb, line 187 def init_attr_trackers # (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.select { |attr| !to_external(attr) } # (def) those with parser or alias: def_linked = def_all_attrs - def_unlinked # (data) data attributes (actual attributes of the entry) data_attrs = attributes(@external_entry) # (data) attributes of the data that come directly as internal attribute names data_direct_attrs = data_attrs & def_all_attrs # (def) configured as alised (internal <-> external attributes) def_int_aliased = def_all_attrs.select { |attr| to_external(attr) } def_ext_alias = def_int_aliased.map { |attr| to_external(attr) } # (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) attrs that could 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_def_ext_alias = data_def_int_aliased.map { |attr| to_external(attr) } # (data) actual attrs of the data that come aliased data_ext_alias = data_def_ext_alias & data_attrs # (data) all internal attributes that could come aliased, with given the entry @aliased_attrs = data_def_int_aliased + data_vi_int_aliased # (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 # 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 - def_linked @direct_attrs = data_ext_direct @internal_attrs = @aliased_attrs | @direct_attrs end
LOGGER
# File lib/eco/api/common/people/person_entry_attribute_mapper.rb, line 251 def logger @logger || ::Logger.new(IO::NULL) end