class Eco::API::Common::People::PersonEntry

def __external_entry(data)

Returns:
  • (Hash) - entry in raw: that with **external** names, values and types.
def __external_entry(data)
  return data if parsing?
  _external_serializing(data)
end

def __final_entry(data)

Returns:
  • (Hash) - that with **internal** names, values and types.
def __final_entry(data)
  return _final_parsing(data) if parsing?
  _final_serializing(data)
end

def __internal_entry(data)

Returns:
  • (Hash) - that with **internal** names and values, but **external** values and types.
def __internal_entry(data)
  return _internal_parsing(data) if parsing?
  _internal_serializing(data)
end

def __mapped_entry(data)

Returns:
  • (Hash) - that with **internal** names but **external** values and types.
def __mapped_entry(data)
  return _mapped_parsing(data) if parsing?
  _mapped_serializing(data)
end

def _external_serializing(mapped_entry)

Returns:
  • (Hash) - with **external** names, values and types.

Parameters:
  • mapped_entry (Hash) -- that with **internal** names but **external** values and types.

Other tags:
    Note: - **Serialize**: here we unaliase internal attribute names into external ones.
def _external_serializing(mapped_entry)
  target_attrs    = @emap.all_model_attrs | @emap.aliased_attrs
  rest_keys       = mapped_entry.keys - target_attrs
  target_attrs   -= ["send_invites"]
  external_entry  = target_attrs.each_with_object({}) do |attr, hash|
    unless hash.key?(ext_attr = @emap.to_external(attr))
      hash[ext_attr] = mapped_entry[attr]
    end
  end
  merge_missing_attrs(external_entry, mapped_entry.slice(*rest_keys))
end

def _final_parsing(internal_entry)

Returns:
  • (Hash) - the `parsed entry` with the **internal** final attributes names, values and types.

Parameters:
  • internal_entry (Hash) -- the entry with the **internal** _attribute_ names and values
def _final_parsing(internal_entry)
  core_account_attrs = @emap.account_attrs(internal_entry) + @emap.core_attrs(internal_entry)
  core_account_hash  = internal_entry.slice(*core_account_attrs).each_with_object({}) do |(attr, value), hash|
    hash[attr] = _parse_type(attr, value)
  end
  details_attrs     = @emap.details_attrs(internal_entry)
  details_hash      = internal_entry.slice(*details_attrs).each_with_object({}) do |(attr, value), hash|
    hash[attr] = _parse_type(attr, value, schema: @person_parser.schema)
  end
  merging(core_account_hash, details_hash) do |final_entry|
    final_entry = merge_missing_attrs(final_entry, internal_entry)
    final_entry.merge(_parse_values(final_entry, :final))
  end
end

def _final_serializing(person)

Returns:
  • (Hash) - the `parsed entry` with the **internal** attributes names and internal typed values.

Parameters:
  • person (Ecoportal::API::V1::Person) -- the `Person` object to transform into a _parsed entry_.

Other tags:
    Note: -
def _final_serializing(person)
  core_hash = @person_parser.target_attrs_core.reduce({}) do |hash, attr|
    hash.merge(hash_attr(attr, get_part(person, attr)))
  end
  account_hash = @person_parser.target_attrs_account.reduce({}) do |hash, attr|
    hash.merge(hash_attr(attr, get_part(person.account, attr)))
  end
  details_hash = @person_parser.target_attrs_details.reduce({}) do |hash, attr|
    hash.merge(hash_attr(attr, get_part(person.details, attr)))
  end
  merging(core_hash, account_hash, details_hash) do |final_entry|
    final_entry["Has account?"] = !person.account.nil?
    final_entry.merge(_serialize_values(person, :person))
  end
end

def _internal_parsing(mapped_entry)

Returns:
  • (Hash) - the `internal entry` with the **internal** attributes names and values.

Parameters:
  • mapped_entry (Hash) -- the entry with the _internal attribute_ names but the _external values_.

Other tags:
    Note: - this entry will still miss the type parsing (i.e. to `Array` if `multiple`)
def _internal_parsing(mapped_entry)
  mapped_entry.merge(_parse_values(mapped_entry, :internal))
end

def _internal_serializing(final_entry)

Returns:
  • (Hash) - the `internal entry` with the **internal** attributes names and values, but external types.

Parameters:
  • final_entry (Hash) -- the entry with all _internal_ (attributes, values and types)
def _internal_serializing(final_entry)
  final_entry        = final_entry.merge(_serialize_values(final_entry, :final))
  core_account       = @person_parser.target_attrs_account + @person_parser.target_attrs_core
  core_account_hash  = core_account.reduce({}) do |hash, attr|
    hash.merge(hash_attr(attr, _serialize_type(attr, final_entry[attr])))
  end
  details_hash = @person_parser.target_attrs_details.reduce({}) do |hash, attr|
    hash.merge(hash_attr(attr, _serialize_type(attr, final_entry[attr], schema: @person_parser.schema)))
  end
  merging(core_account_hash, details_hash) do |internal_entry|
    merge_missing_attrs(internal_entry, final_entry)
  end
end

def _mapped_parsing(external_entry)

Returns:
  • (Hash) - entry with **internal** names, but still **external** values and types.

Parameters:
  • external_entry (Hash) -- entry in raw, with **external** names and values.

Other tags:
    Note: - **Parse**: here we aliase external attribute names into internal ones.
def _mapped_parsing(external_entry)
  mapped_hash = @emap.aliased_attrs.each_with_object({}) do |attr, hash|
    hash[attr] = external_entry[@emap.to_external(attr)]
  end
  external_entry.slice(*@emap.direct_attrs).merge(mapped_hash)
end

def _mapped_serializing(internal_entry)

Returns:
  • (Hash) - entry with **internal** names and **external** values and types.

Parameters:
  • internal_entry (Hash) -- entry with **internal** names and values, but **external** types.

Other tags:
    Note: - **Serializing**:
def _mapped_serializing(internal_entry)
  mapped_hash = internal_entry.merge(_serialize_values(internal_entry, :internal))
  model_attrs = @person_parser.all_model_attrs - ["send_invites"]
  aux_hash    = mapped_hash.slice(*model_attrs)
  merge_missing_attrs(aux_hash, mapped_hash)
end

def _parse_type(attr, value, schema: nil) # rubocop:disable Metrics/AbcSize

rubocop:disable Metrics/AbcSize
Transforms each `String` value into its **typed** version
def _parse_type(attr, value, schema: nil) # rubocop:disable Metrics/AbcSize

  value = value.strip if value.is_a?(String)
  value = nil         if value.to_s.strip.empty?
  if !!schema
    unless (field = schema[attr])
      fatal("Field '#{attr}' does not exist in details of schema: '#{schema.name}'")
    end
    value = @person_parser.parse(:multiple, value) if field.multiple
    if @person_parser.defined?(field.type.to_sym)
      value = @person_parser.parse(field.type.to_sym, value, deps: {"attr" => attr})
    end
    value
  elsif attr == "email"
    value = value.strip.downcase if value
    value
  elsif %w[policy_group_ids filter_tags login_provider_ids starred_ids].include?(attr)
    value = @person_parser.parse(:multiple, value)
    value = value.compact.map(&:upcase) if attr == "filter_tags"
    value
  elsif %w[freemium accept_eula].include?(attr)
    @person_parser.parse(:boolean, value)
  elsif ["subordinates"].include?(attr)
    @person_parser.parse(:number, value)
  else
    value
  end
end

def _parse_values(entry, phase = :internal)

def _parse_values(entry, phase = :internal)
  @person_parser.active_attrs(entry, phase).each_with_object({}) do |attr, hash|
    parsed_attr = @person_parser.parse(attr, entry.merge(hash), phase)
    hash.merge!(hash_attr(attr, parsed_attr))
  end
end

def _serialize_type(attr, value, schema: nil)

Transforms each **typed** value into its `String` version
def _serialize_type(attr, value, schema: nil)
  if !!schema
    unless (field = schema[attr])
      fatal("Field '#{attr}' does not exist in details of schema: '#{schema.name}'")
    end
    value = @person_parser.serialize(:multiple, value) if field.multiple
    if @person_parser.defined?(field.type.to_sym)
      value = @person_parser.serialize(field.type.to_sym, value, deps: {"attr" => attr})
    end
    value
  elsif %w[policy_group_ids filter_tags login_provider_ids starred_ids].include?(attr)
    @person_parser.serialize(:multiple, value)
  elsif %w[freemium accept_eula].include?(attr)
    @person_parser.serialize(:boolean, value)
  elsif ["subordinates"].include?(attr)
    @person_parser.serialize(:number, value)
  else
    value
  end
end

def _serialize_values(entry, phase = :person)

HELPERS
def _serialize_values(entry, phase = :person)
  @person_parser.active_attrs(entry, phase, process: :serialize).each_with_object({}) do |attr, hash|
    data        = entry.is_a?(Hash)? entry.merge(hash) : entry
    serial_attr = @person_parser.serialize(attr, data, phase, deps: @deps[attr] || {})
    hash.merge!(hash_attr(attr, serial_attr))
  end
end

def default_tag

def default_tag
  final_entry["default_tag"]
end

def default_tag?

def default_tag?
  final_entry.key?("default_tag")
end

def email

Returns:
  • (String, nil) - the _email_ of this person if defined.
def email
  final_entry["email"]
end

def email?

def email?
  final_entry.key?("email")
end

def external_entry # rubocop:disable Style/TrivialAccessors

Returns:
  • (Hash) - entry `Hash` with **external** attribute names, and values and types thereof.

Other tags:
    Note: - completely serialized entry.
def external_entry # rubocop:disable Style/TrivialAccessors

  @external_entry
end

def external_id

Returns:
  • (String, nil) - the _external id_ of this person if defined.
def external_id
  final_entry["external_id"]
end

def external_id?

def external_id?
  final_entry.key?("external_id")
end

def fatal(msg)

def fatal(msg)
  logger.fatal(msg)
  raise msg
end

def filter_tags

def filter_tags
  final_entry["filter_tags"] || []
end

def filter_tags?

def filter_tags?
  final_entry.key?("filter_tags")
end

def final_entry # rubocop:disable Style/TrivialAccessors

Returns:
  • (Hash) - entry `Hash` with **internal** attribute names, values and types.

Other tags:
    Note: - values ready to be set to a person.
def final_entry # rubocop:disable Style/TrivialAccessors

  @final_entry
end

def get_part(obj, attr)

def get_part(obj, attr)
  return unless obj
  case obj
  when Ecoportal::API::V1::PersonDetails
    #unless field = obj.get_field(attr)

    #  fatal("Field '#{attr}' does not exist in details of schema: '#{obj.schema_id}'")

    #end

    obj[attr]
  else
    obj.send(attr)
  end
end

def hash_attr(attr, value)

Returns:
  • (Hash) - `value` if it was a `Hash`, and `{ attr => value}` otherwise
def hash_attr(attr, value)
  return value if value.is_a?(Hash)
  { attr => value }
end

def id

Returns:
  • (String, nil) - the _internal id_ of this person if defined.
def id
  final_entry["id"]
end

def id?

def id?
  final_entry.key?("id")
end

def identify

Returns:
  • (String) - string summary of this person identity.
def identify
  str_id = id ? "id: '#{id}'; " : ""
  "(row: #{idx}) '#{name}' (#{str_id}ext_id: '#{external_id}'; email: '#{email}')"
end

def idx

Returns:
  • (Integer) - the entry number in the input file

Other tags:
    Note: - `Eco::API::Common::People::EntryFactory#entries` adds this `idx` (i.e. row number)
def idx
  final_entry["idx"]
end

def initialize(data, person_parser:, attr_map:, dependencies: {}, logger: ::Logger.new(IO::NULL))

Parameters:
  • logger (Common::Session::Logger, ::Logger) -- object to managelogs.
  • dependencies (Hash) -- hash where _keys_ are internal attribute
  • attr_map (Eco::Data::Mapper) -- mapper to translate attribute
  • person_parser (Common::People::PersonParser) -- parser/serializer
  • data (Hash, Ecoportal::API::V1::Person) -- `Person` object

Other tags:
    Note: -
def initialize(data, person_parser:, attr_map:, dependencies: {}, logger: ::Logger.new(IO::NULL))
  msg = "Constructor needs a PersonParser. Given: #{person_parser.class}"
  raise msg unless person_parser.is_a?(Eco::API::Common::People::PersonParser)
  msg = "Expecting Mapper object. Given: #{attr_map.class}"
  raise msg if attr_map && !attr_map.is_a?(Eco::Data::Mapper)
  @source        = data
  @person_parser = person_parser
  @deps          = dependencies
  @logger        = logger
  @attr_map      = attr_map
  @emap          = PersonEntryAttributeMapper.new(
    @source,
    person_parser: @person_parser,
    attr_map:      @attr_map,
    logger:        @logger
  )
  if parsing?
    @external_entry     = __external_entry(data)
    @mapped_entry       = __mapped_entry(@external_entry)
    @internal_entry     = __internal_entry(@mapped_entry)
    @final_entry        = __final_entry(@internal_entry)
  else  # SERIALIZING

    @person             = data
    @final_entry        = __final_entry(@person)
    @internal_entry     = __internal_entry(@final_entry)
    @mapped_entry       = __mapped_entry(@internal_entry)
    @external_entry     = __external_entry(@mapped_entry)
  end
  (print_models; exit(1)) if DEBUG # rubocop:disable Style/Semicolon

end

def internal_entry # rubocop:disable Style/TrivialAccessors

Returns:
  • (Hash) - entry `Hash` with **internal** attribute names and values, but **external** types.

Other tags:
    Note: - just one step away from being completely parsed (only types parsing pending).
def internal_entry # rubocop:disable Style/TrivialAccessors

  @internal_entry
end

def into_a(value)

def into_a(value)
  value = [] if value.nil?
  value = [].push(value) unless value.is_a?(Array)
  value
end

def logger

LOGGER
def logger
  @logger || ::Logger.new(IO::NULL)
end

def mapped_entry # rubocop:disable Style/TrivialAccessors

Returns:
  • (Hash) - entry `Hash` with **internal** attribute names, but **external** types and values.
def mapped_entry # rubocop:disable Style/TrivialAccessors

  @mapped_entry
end

def merge_missing_attrs(dest_entry, source_entry)

Adds to `dest_entry` the `keys` it misses from `source_entry`
def merge_missing_attrs(dest_entry, source_entry)
  keys_rest = source_entry.keys - dest_entry.keys
  dest_entry.merge(source_entry.slice(*keys_rest))
end

def merging(*hashes)

Returns:
  • (Hash) - with well sorted keys, as they came in the order of the input hashes.
def merging(*hashes)
  sorted_keys = hashes.map(&:keys).flatten.uniq
  rev_hash    = hashes.reverse.each_with_object({}) {|h, out| out.merge!(h)}
  merged      = sorted_keys.each_with_object({}) do |k, h|
    h[k] = rev_hash[k]
  end
  merged = yield(merged) if block_given?
  merged
end

def name

Returns:
  • (String, nil) - the _name_ of this person if defined.
def name
  final_entry["name"]
end

def name?

def name?
  final_entry.key?("name")
end

def new(data)

Returns:
  • (PersonEntry) -
def new(data)
  self.class.new(
    data,
    person_parser: @person_parser,
    attr_map:      @attr_map,
    dependencies:  @deps,
    logger:        @logger
  )
end

def parsing?

Returns:
  • (Boolean) - returns `true` if we are **parsing**, `false` otherwise.
def parsing?
  !@source.is_a?(Ecoportal::API::Internal::Person)
end

def policy_group_ids

def policy_group_ids
  final_entry["policy_group_ids"] || []
end

def policy_group_ids?

def policy_group_ids?
  final_entry.key?("policy_group_ids")
end

def print_models

Function to debug faste
def print_models
  print_it = proc do |name, model|
    puts "#{name}:"
    pp model
    puts "*" * 30
  end
  fin = proc { print_it.call("final_entry",    @final_entry)    }
  int = proc { print_it.call("internal_entry", @internal_entry) }
  mad = proc { print_it.call("mapped_entry",   @mapped_entry)   }
  ext = proc { print_it.call("external_entry", @external_entry) }
  call_order = parsing? ? [ext, mad, int, fin] : [fin, int, mad, ext]
  call_order.each(&:call)
end

def serializing?

Returns:
  • (Boolean) - returns `true` if we are **serializing**, `false` otherwise.
def serializing?
  !parsing?
end

def set_account(person, exclude: nil)

Parameters:
  • exclude (String, Array) -- account properties that should not be set/changed to the person.
  • person (Ecoportal::API::Internal::Person) -- the person we want to set the account values to.

Other tags:
    Note: - it only sets those account properties defined in the entry.
def set_account(person, exclude: nil)
  person.account = {} unless person.account
  scoped_attrs = @emap.account_attrs(@final_entry) - into_a(exclude)
  @final_entry.slice(*scoped_attrs).each do |attr, value|
    set_part(person.account, attr, value)
  end
end

def set_core(person, exclude: nil)

Parameters:
  • exclude (String, Array) -- core attributes that should not be set/changed to the person.
  • person (Ecoportal::API::V1::Person) -- the person we want to set the core values to.

Other tags:
    Note: -
def set_core(person, exclude: nil)
  scoped_attrs = @emap.core_attrs(@final_entry) - into_a(exclude)
  @final_entry.slice(*scoped_attrs).each do |attr, value|
    set_part(person, attr, value)
  rescue StandardError => e
    raise unless attr == "email"
    logger.error("#{e} - setting blank email instead.")
    set_part(person, attr, nil)
  end
end

def set_details(person, exclude: nil)

Parameters:
  • exclude (String, Array) -- schema field attributes that should not be set/changed to the person.
  • person (Ecoportal::API::V1::Person) -- the person we want to set the schema fields' values to.

Other tags:
    Note: - it only sets those details properties defined in the entry.
def set_details(person, exclude: nil)
  person.add_details(@person_parser.schema) if !person.details || !person.details.schema_id
  scoped_attrs = @emap.details_attrs(@final_entry) - into_a(exclude)
  @final_entry.slice(*scoped_attrs).each do |attr, value|
    set_part(person.details, attr, value)
  end
end

def set_part(obj, attr, value)

def set_part(obj, attr, value)
  return unless obj
  case obj
  when Ecoportal::API::V1::PersonDetails
    msg = "Field '#{attr}' does not exist in details of schema: '#{obj.schema_id}'"
    fatal msg unless obj.get_field(attr)
    obj[attr] = value
  else
    obj.send("#{attr}=", value)
  end
rescue StandardError => e
  # add more info to the error

  raise e.append_message " -- Entry #{to_s(:identify)}"
end

def supervisor_id

Returns:
  • (String, nil) - the _supervisor id_ of this person if defined.
def supervisor_id
  final_entry["supervisor_id"]
end

def supervisor_id=(value)

def supervisor_id=(value)
  final_entry["supervisor_id"] = value
end

def supervisor_id?

def supervisor_id?
  final_entry.key?("supervisor_id")
end

def to_s(options)

Returns:
  • (String) - string summary of this person identity.
def to_s(options)
  options = into_a(options)
  if options.include?(:identify)
    identify
  else
    final_entry.each.map do |k, v|
      "'#{k}': '#{v.to_json}'"
    end.join(" | ")
  end
end