lib/eco/api/common/people/person_entry.rb in eco-helpers-1.5.1 vs lib/eco/api/common/people/person_entry.rb in eco-helpers-1.5.2

- old
+ new

@@ -7,11 +7,11 @@ # This class is meant to provide 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, Person] `Person` object to be serialized or hashed entry (`CSV::Row` is accepted). + # @param data [Hash, Ecoportal::API::V1::Person] `Person` object to be serialized or hashed entry (`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 dependencies [Hash] hash where _keys_ are internal attribute names. It is mostly used to deliver final dependencies to attribute parsers/serializers. # @param logger [Common::Session::Logger, ::Logger] object to manage logs. def initialize(data, person_parser:, attr_map:, dependencies: {}, logger: ::Logger.new(IO::NULL)) @@ -24,21 +24,47 @@ @logger = logger @attr_map = attr_map @emap = PersonEntryAttributeMapper.new(@source, person_parser: @person_parser, attr_map: @attr_map, logger: @logger) if parsing? - @external_entry = data - @serialized_entry = _mapped_entry(@external_entry) - @internal_entry = _internal_entry(@serialized_entry) + @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 - @internal_entry = _internal_entry(@person) - @serialized_entry = _mapped_entry(@internal_entry) - #@external_entry = external_entry + @final_entry = __final_entry(@person) + @internal_entry = __internal_entry(@final_entry) + @mapped_entry = __mapped_entry(@internal_entry) + @external_entry = __external_entry(@mapped_entry) end end + # Generates a new entry + # @return [PersonEntry] + def new(data) + self.class.new(data, person_parser: @person_parser, attr_map: @attr_map, dependencies: @deps, logger: @logger) + end + + # @note completely serialized entry. + # @return [Hash] entry `Hash` with **external** attribute names, and values and types thereof. + def external_entry + @external_entry + end + + # @note just one step away from being completely parsed (only types parsing pending). + # @return [Hash] entry `Hash` with **internal** attribute names and values, but **external** types. + def internal_entry + @internal_entry + end + + # @note values ready to be set to a person. + # @return [Hash] entry `Hash` with **internal** attribute names, values and types. + def final_entry + @final_entry + end + # To know if currently the object is in parse or serialize mode. # @return [Boolean] returns `true` if we are **parsing**, `false` otherwise. def parsing? !@source.is_a?(Ecoportal::API::Internal::Person) end @@ -47,347 +73,403 @@ # @return [Boolean] returns `true` if we are **serializing**, `false` otherwise. def serializing? !parsing? end + # @note `Eco::API::Common::People::EntryFactory#entries` adds this `idx` + # @return [Integer] the entry number in the input file + def idx + final_entry["idx"] + end + # @return [String, nil] the _internal id_ of this person if defined. def id - @internal_entry["id"] + final_entry["id"] end def id? - @internal_entry.key?("id") + final_entry.key?("id") end # @return [String, nil] the _external id_ of this person if defined. def external_id - @internal_entry["external_id"] + final_entry["external_id"] end def external_id? - @internal_entry.key?("external_id") + final_entry.key?("external_id") end # @return [String, nil] the _name_ of this person if defined. def name - @internal_entry["name"] + final_entry["name"] end def name? - @internal_entry.key?("name") + final_entry.key?("name") end # @return [String, nil] the _email_ of this person if defined. def email - @internal_entry["email"] + final_entry["email"] end def email? - @internal_entry.key?("email") + final_entry.key?("email") end # @return [String, nil] the _supervisor id_ of this person if defined. def supervisor_id - @internal_entry["supervisor_id"] + final_entry["supervisor_id"] end def supervisor_id=(value) - @internal_entry["supervisor_id"] = value + final_entry["supervisor_id"] = value end def supervisor_id? - @internal_entry.key?("supervisor_id") + final_entry.key?("supervisor_id") end def filter_tags - @internal_entry["filter_tags"] + final_entry["filter_tags"] || [] end def filter_tags? - @internal_entry.key?("filter_tags") + final_entry.key?("filter_tags") end + def policy_group_ids + final_entry["policy_group_ids"] || [] + end + def policy_group_ids? - @internal_entry.key?("policy_group_ids") + final_entry.key?("policy_group_ids") end def default_tag? - @internal_entry.key?("default_tag") + final_entry.key?("default_tag") end def default_tag - @internal_entry["default_tag"] + final_entry["default_tag"] end # Provides a reference of this person. # @return [String] string summary of this person identity. def to_s(options) options = into_a(options) case when options.include?(:identify) - "'#{name}' ('#{external_id}': '#{email}')" + str_id = id ? "id: '#{id}'; " : "" + "(row: #{idx}) '#{name}' (#{str_id}ext_id: '#{external_id}'; email: '#{email}')" else - @internal_entry.each.map do |k, v| + final_entry.each.map do |k, v| "'#{k}': '#{v.to_json}'" end.join(" | ") end end # Setter to fill in all the `core` properties of the `Person` that are present in the `Entry`. # @note it only sets those core properties defined in the entry. # Meaning that if an core property is not present in the entry, this will not be set on the target person. - # @param person [Person] the person we want to set the core values to. + # @param person [Ecoportal::API::V1::Person] the person we want to set the core values to. # @param exclude [String, Array<String>] core attributes that should not be set/changed to the person. def set_core(person, exclude: nil) scoped_attrs = @emap.core_attrs - into_a(exclude) - @internal_entry.slice(*scoped_attrs).each do |attr, value| - _set_to_core(person, attr, value) + @final_entry.slice(*scoped_attrs).each do |attr, value| + set_part(person, attr, value) end end - # Setter to fill in all the schema `details` fields of the `Person` that are present in the `Entry`. - # @note it only sets those details properties defined in the entry. - # Meaning that if an details property is not present in the entry, this will not be set on the target person. - # @param person [Person] the person we want to set the schema fields' values to. - # @param exclude [String, Array<String>] schema field attributes that should not be set/changed to the person. - def set_details(person, exclude: nil) - person.add_details(@person_parser.schema) if !person.details || !person.details.schema_id - scoped_attrs = @emap.details_attrs - into_a(exclude) - @internal_entry.slice(*scoped_attrs).each do |attr, value| - _set_to_details(person, attr, value) - end - end - # Setter to fill in the `account` properties of the `Person` that are present in the `Entry`. # @note it only sets those account properties defined in the entry. # Meaning that if an account property is not present in the entry, this will not be set on the target person. - # @param person [Person] the person we want to set the account values to. + # @param person [Ecoportal::API::Internal::Person] the person we want to set the account values to. # @param exclude [String, Array<String>] account properties that should not be set/changed to the person. def set_account(person, exclude: nil) person.account = {} if !person.account person.account.permissions_preset = nil unless person.account.permissions_preset = "custom" scoped_attrs = @emap.account_attrs - into_a(exclude) - @internal_entry.slice(*scoped_attrs).each do |attr, value| - _set_to_account(person, attr, value) + @final_entry.slice(*scoped_attrs).each do |attr, value| + set_part(person.account, attr, value) end end - # Entry represented in a `Hash` with **external** attribute names and values thereof. - # @note normally used to obtain a **serialized entry**. - # @return [Hash] with **external** names and values. - def to_hash - external_entry + # Setter to fill in all the schema `details` fields of the `Person` that are present in the `Entry`. + # @note it only sets those details properties defined in the entry. + # Meaning that if an details property is not present in the entry, this will not be set on the target person. + # @param person [Ecoportal::API::V1::Person] the person we want to set the schema fields' values to. + # @param exclude [String, Array<String>] schema field attributes that should not be set/changed to the person. + def set_details(person, exclude: nil) + person.add_details(@person_parser.schema) if !person.details || !person.details.schema_id + scoped_attrs = @emap.details_attrs - into_a(exclude) + @final_entry.slice(*scoped_attrs).each do |attr, value| + set_part(person.details, attr, value) + end end - # Entry represented in a `Hash` with **external** attribute names and values thereof. - # @note normally used to obtain a **serialized entry**. - # @return [Hash] with **external** names and values. - def external_entry - @emap.all_attrs.each_with_object({}) do |attr, hash| - unless hash.key?(ext_attr = @emap.to_external(attr)) - hash[ext_attr] = @serialized_entry[attr] - end - end + private + + # @return [Hash] entry in raw: that with **external** names, values and types. + def __external_entry(data) + return data if parsing? + _external_serializing(data) end - def internal_entry - @internal_entry + # @return [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 doc - return @person.doc if instance_variable_defined?(:@person) && @person + # @return [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 - core_attrs = @emap.core_attrs - details_attrs = @emap.details_attrs - account_attrs = @emap.account_attrs + # @return [Hash] that with **internal** names, values and types. + def __final_entry(data) + return _final_parsing(data) if parsing? + _final_serializing(data) + end - internal_entry.slice(*core_attrs).tap do |core_hash| - unless details_attrs.empty? - schema_id = @person_parser.schema.id - details_fields = @person_parser.schema.doc["fields"].each_with_object([]) do |fld, flds| - if details_attrs.include?(fld.alt_id) - flds << fld.merge("value" => internal_entry[fld.alt_id]).slice("id", "alt_id", "type", "name", "shared", "multiple", "value") - end - end - core_hash.merge!({ - "details" => { - "schema_id" => schema_id, - "fields" => details_fields - } - }) + # Serializing helper that maps internal attributes to external attribute names + # @note **Serialize**: here we unaliase internal attribute names into external ones. + # @param mapped_entry [Hash] that with **internal** names but **external** values and types. + # @return [Hash] with **external** names, values and types. + def _external_serializing(mapped_entry) + target_attrs = @emap.all_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 - - unless account_attrs.empty? - account_hash = internal_entry.slice(*account_attrs) - core_hash.merge!({ - "account" => account_hash - }) - end end + merge_missing_attrs(external_entry, mapped_entry.slice(*rest_keys)) end - private - - def _set_to_core(person, attr, value) - value = value&.downcase if attr == "email" - multiple = ["filter_tags"].include?(attr) - if multiple - value = @person_parser.parse(:multiple, value) - value = value.map { |v| v&.upcase } if attr == "filter_tags" - # preserve previous order - current = into_a(person.send(attr)) - value = (current & value) + (value - current) - else - value = value&.strip + # Parsing helper that aliases attribute names (from internal to external names) + # @note **Parse**: here we aliase external attribute names into internal ones. + # @param external_entry [Hash] entry in raw, with **external** names and values. + # @return [Hash] entry with **internal** names, but still **external** values and types. + 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 - person.send("#{attr}=", value) + # Serializing helper that **serializes values** that have a parser/serializer defined. + # @note **Serializing**: + # 1. here we tranform internal into external **values**. + # 2. when running the serializers, it overrides existing keys. + # @param internal_entry [Hash] entry with **internal** names and values, but **external** types. + # @return [Hash] entry with **internal** names and **external** values and types. + def _mapped_serializing(internal_entry) + internal_entry.merge(_serialize_values(internal_entry, :internal)) end - def _set_to_account(person, attr, value) - return if !person.account - multiple = ["policy_group_ids", "login_provider_ids"].include?(attr) - if multiple - value = @person_parser.parse(:multiple, value) - # preserve previous order - current = into_a(person.account.send(attr)) - value = (current & value) + (value - current) - end + # Parsing helper that just **parses the values** that have a parser/serializer defined. + # @note this entry will still miss the type parsing (i.e. to `Array` if `multiple`) + # @param mapped_entry [Hash] the entry with the _internal attribute_ names but the _external values_. + # @return [Hash] the `internal entry` with the **internal** attributes names and values. + def _internal_parsing(mapped_entry) + mapped_entry.merge(_parse_values(mapped_entry, :internal)) + end - person.account.send("#{attr}=", value) + # Serializing helper that just creates the _internal entry_ out of a _parsed entry_ (serializes the type). + # @param final_entry [Hash] the entry with all _internal_ (attributes, values and types) + # @return [Hash] the `internal entry` with the **internal** attributes names and values, but external 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 _set_to_details(person, attr, value) - return if !person.details - unless field = person.details.get_field(attr) - fatal("Field '#{attr}' does not exist in details of schema: '#{person.details.schema_id}'") + # Parsing helper where attributes with custom parsers are already parsed, but + # it finishes to parse the types (i.e. to `Array` if `multiple`) + # @param internal_entry [Hash] the entry with the **internal** _attribute_ names and values but the **external** types. + # @return [Hash] the `parsed entry` with the **internal** final attributes names, values and types. + def _final_parsing(internal_entry) + core_account = @emap.account_attrs + @emap.core_attrs + core_account_hash = internal_entry.slice(*core_account).each_with_object({}) do |(attr, value), hash| + hash[attr] = _parse_type(attr, value) end - value = nil if value.to_s.empty? - 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}) + details_hash = internal_entry.slice(*@emap.details_attrs).each_with_object({}) do |(attr, value), hash| + hash[attr] = _parse_type(attr, value, schema: @person_parser.schema) end - person.details[attr] = value + 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 _get_from_core (person, attr) - person.send(attr) + # Serializing helper that just creates the _parsed entry_ out of a `Person` object. + # @note + # - when unnesting attributes, the overriding precedence for collisions is + # - `core` -> _overrides_ -> `account` -> _overrides_ -> `details` + # - to keep things consistent, the `internal entry` hash has keys in this order: + # - `core`, `account`, `details`. + # @param person [Ecoportal::API::V1::Person] the `Person` object to transform into a _parsed entry_. + # @return [Hash] the `parsed entry` with the **internal** attributes names and internal typed values. + 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 + final_entry.merge(_serialize_values(person, :person)) + end end - def _get_from_account (person, attr) - return nil if !person.account - multiple = ["policy_group_ids", "filter_tags", "login_provider_ids"].include?(attr) - value = person.account.send(attr) - value = @person_parser.serialize(:multiple, value) if multiple - value + # 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 _get_from_details(person, attr) - return nil if !person.details || !person&.details&.schema_id - unless field = person.details.get_field(attr) - fatal("Field '#{attr}' does not exist in details of schema: '#{person.details.schema_id}'") + 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 - value = person.details[attr] - value = @person_parser.serialize(:date, value) if field.type == "date" - value = @person_parser.serialize(:multiple, value) if field.multiple - value end - # MAPPED ENTRY (when and where applicable) - # To obtain an entry with internal names but external values. - # @param data [Hash] external or raw entry (when parsing) or internal or parsed entry (when serializing). - # @return [Hash] entry with **internal names** and **external values**. - def _mapped_entry(data) - return _aliased_entry(data) if parsing? - _serialized_entry(data) + # Transforms each **typed** value into its `String` version + def _serialize_type(attr, value, schema: nil) + case + when !!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 + when ["policy_group_ids", "filter_tags", "login_provider_ids", "starred_ids"].include?(attr) + @person_parser.serialize(:multiple, value) + when ["freemium", "accept_eula"].include?(attr) + @person_parser.serialize(:boolean, value) + when ["subordinates"].include?(attr) + @person_parser.serialize(:number, value) + else + value + end end - # Parsing helper that aliases attribute names (from internal to external names) - # @note **Parse**: here we aliase internal attribute names into external ones. - # @param ext_entry [Hash] entry in raw, with **external** names and values. - # @return [Hash] entry with **internal names** and **external values**. - def _aliased_entry(ext_entry) - aliased_hash = @emap.aliased_attrs.map do |attr| - [attr, ext_entry[@emap.to_external(attr)]] - end.to_h + # Transforms each `String` value into its **typed** version + def _parse_type(attr, value, schema: nil) + value = value.strip if value.is_a?(String) + value = nil if value.to_s.strip.empty? + case + when !!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 - ext_entry.slice(*@emap.direct_attrs).merge(aliased_hash) + if @person_parser.defined?(field.type.to_sym) + value = @person_parser.parse(field.type.to_sym, value, deps: {"attr" => attr}) + end + value + when attr == "email" + value = value.strip.downcase if value + value + when ["policy_group_ids", "filter_tags", "login_provider_ids", "starred_ids"].include?(attr) + value = @person_parser.parse(:multiple, value) + value = (attr == "filter_tags")? value.compact.map(&:upcase) : value + value + when ["freemium", "accept_eula"].include?(attr) + @person_parser.parse(:boolean, value) + when ["subordinates"].include?(attr) + @person_parser.parse(:number, value) + else + value + end end - def hash_attr(attr, value) - return value if value.is_a?(Hash) - { attr => value } + # Merges multiple hashes giving overriding perference to the first ones. + # @return [Hash] with well sorted keys, as they came in the order of the input hashes. + def merging(*hashes) + sorted_keys = hashes.map {|h| h.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 - # Serializing helper that serializes values (from internal to external values). - # @note **Serializing**: - # 1. here we tranform internal into external **values**. - # 2. when running the serializers, it overrides existing keys. - # @param unserialized_entry [Hash] entry with **internal** names and values. - # @return [Hash] entry with **internal names** and **external values**. - def _serialized_entry(unserialized_entry) - serial_attrs = @person_parser.defined_attrs.reduce({}) do |serial_hash, attr| - deps = @deps[attr] || {} - serial_attr = @person_parser.serialize(attr, @person, deps: deps) - serial_hash.merge(hash_attr(attr, serial_attr)) - end - unserialized_entry.merge(serial_attrs).tap do |hash| - if hash.key?("filter_tags") && hash["filter_tags"].is_a?(Array) - hash["filter_tags"] = @person_parser.serialize(:multiple, hash["filter_tags"]) - end - end + # 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 - # To obtain an entry with internal names but external values. - # @param data [Hash, Ecoportal::API::V1::Person] alised_entry (when parsing) or person (when serializing). - # @return [Hash] the `internal entry` with the **internal** attributes names and values. - def _internal_entry(data) - return _parsed_entry(data) if parsing? - _unserialized_entry(data) + def into_a(value) + value = [] if value == nil + value = [].push(value) unless value.is_a?(Array) + value end - # Parsing helper that just **parses the values** that have a parser/serializer defined. - # @param aliased_entry [Hash] the entry with the _internal attribute_ names but the _external values_. - # @return [Hash] the `internal entry` with the **internal** attributes names and values. - def _parsed_entry(aliased_entry) - parsed = @person_parser.active_attrs(aliased_entry).each_with_object({}) do |attr, hash| - hash[attr] = @person_parser.parse(attr, aliased_entry.merge(hash)) + 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 - aliased_entry.merge(parsed) end - # Serializing helper that just creates the _internal entry_ out of a `Person` object. - # @note - # - when unnesting attributes, the overriding precedence for collisions is - # - `core` -> _overrides_ -> `account` -> _overrides_ -> `details` - # - to keep things consistent, the `internal entry` hash has keys in this order: - # - `core`, `account`, `details`. - # @param person [Ecoportal::API::V1::Person] the `Person` object to transform into an _internal entry_. - # @return [Hash] the `internal entry` with the **internal** attributes names and values. - def _unserialized_entry(person) - core_hash = @person_parser.target_attrs_core.reduce({}) do |hash, attr| - value = _get_from_core(person, attr) - hash.merge(hash_attr(attr, value)) + def set_part(obj, attr, value) + return unless obj + begin + 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] = value + else + obj.send("#{attr}=", value) + end + rescue Exception => e + raise e.append_message " -- Entry #{to_s(:identify)}" end + end - details_hash = @person_parser.target_attrs_details.reduce({}) do |hash, attr| - value = _get_from_details(person, attr) - hash.merge(hash_attr(attr, value)) - end - - account_hash = @person_parser.target_attrs_account.reduce({}) do |hash, attr| - value = _get_from_account(person, attr) - hash.merge(hash_attr(attr, value)) - end - - # merge by core overriding account and details - rh = details_hash.merge(account_hash).merge(core_hash) - # resort hash keys - sorted_keys = core_hash.keys | account_hash.keys | details_hash.keys - sorted_keys.reduce({}) {|h,k| h[k] = rh[k]; h} + # @return [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 # LOGGER def logger @logger || ::Logger.new(IO::NULL) @@ -396,14 +478,24 @@ def fatal(msg) logger.fatal(msg) raise msg end - # HELPERS - def into_a(value) - value = [] if value == nil - value = [].push(value) unless value.is_a?(Array) - value + # Function to debug faste + def print_models + print_it = Proc.new do |name, model| + puts "#{name}:" + pp model + puts "*" * 30 + end + + fin = Proc.new {|x_| print_it.call("final_entry", @final_entry) } + int = Proc.new {|x_| print_it.call("internal_entry", @internal_entry) } + mad = Proc.new {|x_| print_it.call("mapped_entry", @mapped_entry) } + ext = Proc.new {|x_| print_it.call("external_entry", @external_entry) } + + call_order = parsing? ? [ext, mad, int, fin] : [fin, int, mad, ext] + call_order.each {|proc| proc.call} end end end end