eco-helpers 0.6.17 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +19 -0
  3. data/.yardopts +2 -2
  4. data/Gemfile +6 -0
  5. data/Rakefile +27 -0
  6. data/eco-helpers.gemspec +9 -6
  7. data/lib/eco/api.rb +2 -1
  8. data/lib/eco/api/common/people.rb +1 -1
  9. data/lib/eco/api/common/people/base_parser.rb +31 -1
  10. data/lib/eco/api/common/people/default_parsers.rb +5 -1
  11. data/lib/eco/api/common/people/default_parsers/csv_parser.rb +37 -0
  12. data/lib/eco/api/common/people/default_parsers/numeric_parser.rb +0 -1
  13. data/lib/eco/api/common/people/entries.rb +14 -18
  14. data/lib/eco/api/common/people/entry_factory.rb +97 -9
  15. data/lib/eco/api/common/people/person_entry.rb +147 -206
  16. data/lib/eco/api/common/people/person_entry_attribute_mapper.rb +212 -0
  17. data/lib/eco/api/common/people/person_factory.rb +10 -12
  18. data/lib/eco/api/common/people/person_parser.rb +97 -37
  19. data/lib/eco/api/common/session/base_session.rb +1 -2
  20. data/lib/eco/api/common/session/file_manager.rb +1 -1
  21. data/lib/eco/api/organization.rb +2 -1
  22. data/lib/eco/api/organization/people.rb +54 -22
  23. data/lib/eco/api/organization/person_schemas.rb +54 -0
  24. data/lib/eco/api/organization/policy_groups.rb +5 -9
  25. data/lib/eco/api/organization/{presets.rb → presets_factory.rb} +1 -1
  26. data/lib/eco/api/policies.rb +10 -0
  27. data/lib/eco/api/policies/base_policy.rb +14 -0
  28. data/lib/eco/api/policies/policy.rb +20 -0
  29. data/lib/eco/api/policies/used_policies.rb +37 -0
  30. data/lib/eco/api/session.rb +36 -34
  31. data/lib/eco/api/session/batch.rb +94 -44
  32. data/lib/eco/api/session/batch_job.rb +108 -48
  33. data/lib/eco/api/session/batch_jobs.rb +4 -5
  34. data/lib/eco/api/session/batch_status.rb +70 -11
  35. data/lib/eco/api/session/config.rb +22 -5
  36. data/lib/eco/api/session/config/files.rb +10 -1
  37. data/lib/eco/api/session/config/people.rb +18 -5
  38. data/lib/eco/api/session/config/policies.rb +29 -0
  39. data/lib/eco/api/session/config/use_cases.rb +3 -7
  40. data/lib/eco/api/session/job_groups.rb +9 -10
  41. data/lib/eco/api/usecases.rb +2 -1
  42. data/lib/eco/api/usecases/base_case.rb +7 -2
  43. data/lib/eco/api/usecases/default_cases/change_email_case.rb +4 -2
  44. data/lib/eco/api/usecases/default_cases/create_case.rb +2 -1
  45. data/lib/eco/api/usecases/default_cases/create_details_case.rb +3 -1
  46. data/lib/eco/api/usecases/default_cases/create_details_with_supervisor_case.rb +4 -2
  47. data/lib/eco/api/usecases/default_cases/hris_case.rb +20 -13
  48. data/lib/eco/api/usecases/default_cases/new_email_case.rb +3 -1
  49. data/lib/eco/api/usecases/default_cases/new_id_case.rb +4 -2
  50. data/lib/eco/api/usecases/default_cases/recover_db_case.rb +9 -5
  51. data/lib/eco/api/usecases/default_cases/remove_account_case.rb +4 -2
  52. data/lib/eco/api/usecases/default_cases/set_supervisor_case.rb +4 -2
  53. data/lib/eco/api/usecases/default_cases/to_csv_case.rb +2 -2
  54. data/lib/eco/api/usecases/default_cases/to_csv_detailed_case.rb +2 -2
  55. data/lib/eco/api/usecases/default_cases/update_case.rb +16 -2
  56. data/lib/eco/api/usecases/default_cases/update_details_case.rb +3 -1
  57. data/lib/eco/api/usecases/default_cases/upsert_case.rb +25 -3
  58. data/lib/eco/api/usecases/use_case.rb +23 -140
  59. data/lib/eco/api/usecases/use_case_chain.rb +95 -0
  60. data/lib/eco/api/usecases/use_case_io.rb +117 -0
  61. data/lib/eco/api/usecases/use_group.rb +25 -5
  62. data/lib/eco/common/base_cli_backup.rb +1 -0
  63. data/lib/eco/language/models.rb +1 -1
  64. data/lib/eco/language/models/collection.rb +42 -31
  65. data/lib/eco/language/models/parser_serializer.rb +68 -0
  66. data/lib/eco/version.rb +1 -1
  67. metadata +93 -38
  68. data/lib/eco/api/common/people/types.rb +0 -47
  69. data/lib/eco/api/usecases/case_data.rb +0 -13
  70. data/lib/eco/language/models/attribute_parser.rb +0 -38
  71. data/lib/eco/lexic/dictionary.rb +0 -33
  72. data/lib/eco/lexic/dictionary/dictionary.txt +0 -355484
  73. data/lib/eco/lexic/dictionary/tags.json +0 -38
@@ -3,135 +3,163 @@ module Eco
3
3
  module Common
4
4
  module People
5
5
  class PersonEntry
6
- @@cached_warnings = {}
7
-
8
- def initialize(data, parser:, mapper:, dependencies: {}, logger: ::Logger.new(IO::NULL))
9
- raise "Constructor needs a PersonParser. Given: #{parser}" if !parser.is_a?(Eco::API::Common::People::PersonParser)
10
- raise "Expecting Mapper object. Given: #{mapper}" if mapper && !mapper.is_a?(Eco::Data::Mapper)
11
-
12
- @source = data
13
- @parser = parser
14
- @mapper = mapper
15
- @deps = dependencies
16
- @logger = logger
6
+ # This class is meant to provide a common interface to access entries of source data that come in different formats.
7
+ # @note
8
+ # - if `data` is a `Person` object, its behaviour is `serialise`.
9
+ # - if `data` is **not** a `Person` object, it does a `parse`.
10
+ # - currently **in rework**, so there may be subtle differences that make it temporarily unstable (yet it is reliable).
11
+ # @param data [Hash, Person] `Person` object to be serialized or hashed entry (`CSV::Row` is accepted).
12
+ # @param person_parser [Common::People::PersonParser] parser/serializer of person attributes (it contains a set of attribute parsers).
13
+ # @param attr_map [Eco::Data::Mapper] mapper to translate attribute names from _external_ to _internal_ names and _vice versa_.
14
+ # @param dependencies [Hash] hash where _keys_ are internal attribute names. It is mostly used to deliver final dependencies to attribute parsers/serializers.
15
+ # @param logger [Common::Session::Logger, ::Logger] object to manage logs.
16
+ def initialize(data, person_parser:, attr_map:, dependencies: {}, logger: ::Logger.new(IO::NULL))
17
+ raise "Constructor needs a PersonParser. Given: #{parser}" if !person_parser.is_a?(Eco::API::Common::People::PersonParser)
18
+ raise "Expecting Mapper object. Given: #{attr_map}" if attr_map && !attr_map.is_a?(Eco::Data::Mapper)
19
+
20
+ @source = data
21
+ @person_parser = person_parser
22
+ @deps = dependencies
23
+ @logger = logger
24
+ @attr_map = attr_map
25
+ @emap = PersonEntryAttributeMapper.new(@source, person_parser: @person_parser, attr_map: @attr_map, logger: @logger)
17
26
 
18
27
  if parsing?
19
- # PARSING
20
- @raw_entry = data
21
- init_attr_trackers
22
- @aliased_entry = mapped_entry(@raw_entry)
23
- @int_row = internal_entry(@aliased_entry)
24
-
25
- # internally named attrs scoped by mapper and parser against the current row
26
- @int_attrs = @int_row.keys
27
- else
28
- # SERIALIZING
29
- @person = data
30
- @int_row = internal_entry(@person)
31
- @aliased_entry = mapped_entry(@int_row)
32
- @int_attrs = @parser.all_attrs
28
+ @external_entry = data
29
+ @serialized_entry = mapped_entry(@external_entry)
30
+ @internal_entry = internal_entry(@serialized_entry)
31
+ else # SERIALIZING
32
+ @person = data
33
+ @internal_entry = internal_entry(@person)
34
+ @serialized_entry = mapped_entry(@internal_entry)
35
+ #@external_entry = external_entry
33
36
  end
34
-
35
- @core_attrs = @parser.target_attrs_core(@int_attrs)
36
- @details_attrs = @parser.target_attrs_details(@int_attrs)
37
- @account_attrs = @parser.target_attrs_account(@int_attrs)
38
37
  end
39
38
 
39
+ # To know if currently the object is in parse or serialize mode.
40
+ # @return [Boolean] returns `true` if we are **parsing**, `false` otherwise.
40
41
  def parsing?
41
- !is_person?(@source)
42
+ !@source.is_a?(Ecoportal::API::Internal::Person)
42
43
  end
43
44
 
45
+ # To know if currently the object is in parse or serialize mode.
46
+ # @return [Boolean] returns `true` if we are **serializing**, `false` otherwise.
44
47
  def serializing?
45
48
  !parsing?
46
49
  end
47
50
 
48
- # SHORTCUTS
49
-
51
+ # @return [String, nil] the _internal id_ of this person if defined.
50
52
  def id
51
- @int_row["id"]
53
+ @internal_entry["id"]
52
54
  end
53
55
 
56
+ # @return [String, nil] the _external id_ of this person if defined.
54
57
  def external_id
55
- @int_row["external_id"]
58
+ @internal_entry["external_id"]
56
59
  end
57
60
 
61
+ # @return [String, nil] the _name_ of this person if defined.
58
62
  def name
59
- @int_row["name"]
63
+ @internal_entry["name"]
60
64
  end
61
65
 
66
+ # @return [String, nil] the _email_ of this person if defined.
62
67
  def email
63
- @int_row["email"]
68
+ @internal_entry["email"]
64
69
  end
65
70
 
71
+ # @return [String, nil] the _supervisor id_ of this person if defined.
66
72
  def supervisor_id
67
- @int_row["supervisor_id"]
73
+ @internal_entry["supervisor_id"]
68
74
  end
69
75
 
70
76
  def supervisor_id=(value)
71
- @int_row["supervisor_id"] = value
77
+ @internal_entry["supervisor_id"] = value
72
78
  end
73
79
 
80
+ # Provides a reference of this person.
81
+ # @return [String] string summary of this person identity.
74
82
  def to_s(options)
75
83
  options = into_a(options)
76
84
  case
77
85
  when options.include?(:identify)
78
86
  "'#{name}' ('#{external_id}': '#{email}')"
79
87
  else
80
- @int_row.each.map do |k, v|
88
+ @internal_entry.each.map do |k, v|
81
89
  "'#{k}': '#{v.to_json}'"
82
90
  end.join(" | ")
83
91
  end
84
92
  end
85
93
 
86
- # SETTERS
94
+ # Setter to fill in all the `core` properties of the `Person` that are present in the `Entry`.
95
+ # @note it only sets those core properties defined in the entry.
96
+ # Meaning that if an core property is not present in the entry, this will not be set on the target person.
97
+ # @param person [Person] the person we want to set the core values to.
98
+ # @param exclude [String, Array<String>] core attributes that should not be set/changed to the person.
87
99
  def set_core(person, exclude: nil)
88
- scoped_attrs = @core_attrs - into_a(exclude)
89
- @int_row.slice(*scoped_attrs).each do |attr, value|
100
+ scoped_attrs = @emap.core_attrs - into_a(exclude)
101
+ @internal_entry.slice(*scoped_attrs).each do |attr, value|
90
102
  set_to_core(person, attr, value)
91
103
  end
92
104
  end
93
105
 
106
+ # Setter to fill in all the schema `details` fields of the `Person` that are present in the `Entry`.
107
+ # @note it only sets those details properties defined in the entry.
108
+ # Meaning that if an details property is not present in the entry, this will not be set on the target person.
109
+ # @param person [Person] the person we want to set the schema fields' values to.
110
+ # @param exclude [String, Array<String>] schema field attributes that should not be set/changed to the person.
94
111
  def set_details(person, exclude: nil)
95
- person.add_details(@parser.schema) if !person.details || !person.details.schema_id
96
- scoped_attrs = @details_attrs - into_a(exclude)
97
- @int_row.slice(*scoped_attrs).each do |attr, value|
112
+ person.add_details(@person_parser.schema) if !person.details || !person.details.schema_id
113
+ scoped_attrs = @emap.details_attrs - into_a(exclude)
114
+ @internal_entry.slice(*scoped_attrs).each do |attr, value|
98
115
  set_to_details(person, attr, value)
99
116
  end
100
117
  end
101
118
 
119
+ # Setter to fill in the `account` properties of the `Person` that are present in the `Entry`.
120
+ # @note it only sets those account properties defined in the entry.
121
+ # Meaning that if an account property is not present in the entry, this will not be set on the target person.
122
+ # @param person [Person] the person we want to set the account values to.
123
+ # @param exclude [String, Array<String>] account properties that should not be set/changed to the person.
102
124
  def set_account(person, exclude: nil)
103
125
  person.account = {} if !person.account
104
126
  person.account.permissions_preset = nil unless person.account.permissions_preset = "custom"
105
- scoped_attrs = @account_attrs - into_a(exclude)
106
- @int_row.slice(*scoped_attrs).each do |attr, value|
127
+ scoped_attrs = @emap.account_attrs - into_a(exclude)
128
+ @internal_entry.slice(*scoped_attrs).each do |attr, value|
107
129
  set_to_account(person, attr, value)
108
130
  end
109
131
  end
110
132
 
111
- # GETTERS
112
- def entry(klass: nil)
113
- return klass.new(to_hash) if klass
114
- to_hash
133
+ # Entry represented in a `Hash` with **external** attribute names and values thereof.
134
+ # @note normally used to obtain a **serialized entry**.
135
+ # @return [Hash] with **external** names and values.
136
+ def to_hash
137
+ external_entry
115
138
  end
116
139
 
117
- def to_hash
118
- @aliased_entry.each.map do |int_attr, v|
119
- [to_external(int_attr), v || ""] if to_external(int_attr)
120
- end.compact.to_h
140
+ # Entry represented in a `Hash` with **external** attribute names and values thereof.
141
+ # @note normally used to obtain a **serialized entry**.
142
+ # @return [Hash] with **external** names and values.
143
+ def external_entry
144
+ @emap.all_attrs.each_with_object({}) do |attr, hash|
145
+ unless hash.key?(ext_attr = @emap.to_external(attr))
146
+ hash[ext_attr] = @serialized_entry[attr]
147
+ end
148
+ end
121
149
  end
122
150
 
123
151
  private
124
152
 
125
- def set_to_core (person, attr, value)
153
+ def set_to_core(person, attr, value)
126
154
  value = value&.downcase if attr == "email"
127
155
  person.send("#{attr}=", value&.strip)
128
156
  end
129
157
 
130
- def set_to_account (person, attr, value)
158
+ def set_to_account(person, attr, value)
131
159
  return if !person.account
132
160
  multiple = ["policy_group_ids", "filter_tags"].include?(attr)
133
161
  if multiple
134
- value = @parser.parse(:multiple, value)
162
+ value = @person_parser.parse(:multiple, value)
135
163
  value = value.map { |v| v&.upcase } if attr == "filter_tags"
136
164
  # preserve previous order
137
165
  current = into_a(person.account.send(attr))
@@ -141,85 +169,62 @@ module Eco
141
169
  person.account.send("#{attr}=", value)
142
170
  end
143
171
 
144
- def set_to_details (person, attr, value)
172
+ def set_to_details(person, attr, value)
145
173
  return if !person.details
146
- field = person.details.get_field(attr)
147
- fatal("Field '#{attr}' does not exist in details of schema: '#{person.details.schema_id}'") if !field
148
- value = @parser.parse(:multiple, value) if field.multiple
174
+ unless field = person.details.get_field(attr)
175
+ fatal("Field '#{attr}' does not exist in details of schema: '#{person.details.schema_id}'")
176
+ end
177
+ value = nil if value.to_s.empty?
178
+ value = @person_parser.parse(:multiple, value) if field.multiple
149
179
 
150
- if @parser.defined?(field.type.to_sym)
151
- value = @parser.parse(field.type.to_sym, value, deps: {"attr" => attr})
180
+ if @person_parser.defined?(field.type.to_sym)
181
+ value = @person_parser.parse(field.type.to_sym, value, deps: {"attr" => attr})
152
182
  end
153
183
 
154
184
  person.details[attr] = value
155
185
  end
156
186
 
157
- def get_from_core (person, attr)
187
+ def get_from_core (person, attr)
158
188
  person.send(attr)
159
189
  end
160
190
 
161
191
  def get_from_account (person, attr)
162
- return "-" if !person.account
192
+ return nil if !person.account
163
193
  multiple = ["policy_group_ids", "filter_tags"].include?(attr)
164
194
  value = person.account.send(attr)
165
- @parser.serialize(:multiple, value) if multiple
195
+ @person_parser.serialize(:multiple, value) if multiple
166
196
  end
167
197
 
168
- def get_from_details (person, attr)
169
- return "-" if !person.details || !person&.details&.schema_id
170
- field = person.details.get_field(attr)
171
- fatal("Field '#{attr}' does not exist in details of schema: '#{person.details.schema_id}'") if !field
198
+ def get_from_details(person, attr)
199
+ return nil if !person.details || !person&.details&.schema_id
200
+ unless field = person.details.get_field(attr)
201
+ fatal("Field '#{attr}' does not exist in details of schema: '#{person.details.schema_id}'")
202
+ end
172
203
  value = person.details[attr]
173
- value = @parser.serialize(:date, value) if field.type == "date"
174
- value = @parser.serialize(:multiple, value) if field.multiple
204
+ value = @person_parser.serialize(:date, value) if field.type == "date"
205
+ value = @person_parser.serialize(:multiple, value) if field.multiple
175
206
  value
176
207
  end
177
208
 
178
- # INIT
179
-
180
- # when parsing:
181
- def init_attr_trackers
182
- # internal <-> external attributes
183
- int_aliased = @parser.all_attrs.select { |attr| to_external(attr) }
184
- ext_alias = int_aliased.map { |attr| to_external(attr) }
185
-
186
- # virtual attrs (non native internal attr that require aliasing):
187
- ext_vi_aliased = attributes(@raw_entry).select do |attr|
188
- !ext_alias.include?(attr) && @mapper.external?(attr)
189
- end
190
-
191
- int_vi_aliased = ext_vi_aliased.map { |attr| @mapper.to_internal(attr) }
192
- @aliased_attrs = int_aliased + int_vi_aliased
193
-
194
- int_unlinked = @parser.undefined_attrs.select { |attr| !to_external(attr) }
195
- # those with parser or alias:
196
- int_linked = @parser.all_attrs - int_unlinked
197
-
198
- ext_aliased = ext_alias + ext_vi_aliased
199
- # those that are direct external to internal:
200
- ext_direct = attributes(@raw_entry) - ext_aliased
201
- # to avoid collisions between internal names:
202
- @direct_attrs = ext_direct - int_linked
203
- end
204
-
205
209
  # MAPPED ENTRY (when and where applicable)
206
- # return entry with internal names and external values
210
+ # To obtain an entry with internal names but external values.
211
+ # @param data [Hash] external or raw entry (when parsing) or internal or parsed entry (when serializing).
212
+ # @return [Hash] entry with **internal names** and **external values**.
207
213
  def mapped_entry(data)
208
214
  return aliased_entry(data) if parsing?
209
215
  serialized_entry(data)
210
216
  end
211
217
 
212
- # parse: here we aliase attribute names
213
- def aliased_entry(raw_data)
214
- aliased_hash = @aliased_attrs.map do |attr|
215
- [attr, raw_data[to_external(attr)]]
216
- end.to_h
217
-
218
- direct_hash = @direct_attrs.map do |attr|
219
- [attr, raw_data[attr]]
218
+ # Parsing helper that aliases attribute names (from internal to external names)
219
+ # @note **Parse**: here we aliase internal attribute names into external ones.
220
+ # @param ext_entry [Hash] entry in raw, with **external** names and values.
221
+ # @return [Hash] entry with **internal names** and **external values**.
222
+ def aliased_entry(ext_entry)
223
+ aliased_hash = @emap.aliased_attrs.map do |attr|
224
+ [attr, ext_entry[@emap.to_external(attr)]]
220
225
  end.to_h
221
226
 
222
- aliased_hash.merge!(direct_hash)
227
+ ext_entry.slice(*@emap.direct_attrs).merge(aliased_hash)
223
228
  end
224
229
 
225
230
  def hash_attr(attr, value)
@@ -227,106 +232,69 @@ module Eco
227
232
  { attr => value }
228
233
  end
229
234
 
230
- # serializing: run serializers (overrides existing keys)
235
+ # Serializing helper that serializes values (from internal to external values).
236
+ # @note **Serializing**:
237
+ # 1. here we tranform internal into external **values**.
238
+ # 2. when running the serializers, it overrides existing keys.
239
+ # @param unserialized_entry [Hash] entry with **internal** names and values.
240
+ # @return [Hash] entry with **internal names** and **external values**.
231
241
  def serialized_entry(unserialized_entry)
232
- serial_attrs = @parser.defined_attrs.reduce({}) do |serial_hash, attr|
242
+ serial_attrs = @person_parser.defined_attrs.reduce({}) do |serial_hash, attr|
233
243
  deps = @deps[attr] || {}
234
- serial_attr = @parser.serialize(attr, @person, deps: deps)
244
+ serial_attr = @person_parser.serialize(attr, @person, deps: deps)
235
245
  serial_hash.merge(hash_attr(attr, serial_attr))
236
246
  end
237
247
  unserialized_entry.merge(serial_attrs)
238
248
  end
239
249
 
240
- # returns entry with internal names and values
250
+ # To obtain an entry with internal names but external values.
251
+ # @param data [Hash, Ecoportal::API::V1::Person] alised_entry (when parsing) or person (when serializing).
252
+ # @return [Hash] the `internal entry` with the **internal** attributes names and values.
241
253
  def internal_entry(data)
242
254
  return parsed_entry(data) if parsing?
243
255
  unserialized_entry(data)
244
256
  end
245
257
 
246
- # parse: to internal entry (parse values)
258
+ # Parsing helper that just **parses the values** that have a parser/serializer defined.
259
+ # @param aliased_entry [Hash] the entry with the _internal attribute_ names but the _external values_.
260
+ # @return [Hash] the `internal entry` with the **internal** attributes names and values.
247
261
  def parsed_entry(aliased_entry)
248
- parsed = @parser.defined_attrs.map do |attr|
249
- value = @parser.parse(attr, aliased_entry)
262
+ parsed = @person_parser.defined_attrs.map do |attr|
263
+ value = @person_parser.parse(attr, aliased_entry)
250
264
  [attr, value]
251
265
  end.to_h
252
266
  aliased_entry.merge(parsed)
253
267
  end
254
268
 
255
- # serializing: to internal entry (create the internal entry out of a person)
269
+ # Serializing helper that just creates the _internal entry_ out of a `Person` object.
270
+ # @note
271
+ # - when unnesting attributes, the overriding precedence for collisions is
272
+ # - `core` -> _overrides_ -> `account` -> _overrides_ -> `details`
273
+ # - to keep things consistent, the `internal entry` hash has keys in this order:
274
+ # - `core`, `account`, `details`.
275
+ # @param person [Ecoportal::API::V1::Person] the `Person` object to transform into an _internal entry_.
276
+ # @return [Hash] the `internal entry` with the **internal** attributes names and values.
256
277
  def unserialized_entry(person)
257
- core_hash = @parser.target_attrs_core.reduce({}) do |hash, attr|
278
+ core_hash = @person_parser.target_attrs_core.reduce({}) do |hash, attr|
258
279
  value = get_from_core(person, attr)
259
280
  hash.merge(hash_attr(attr, value))
260
281
  end
261
282
 
262
- details_hash = @parser.target_attrs_details.reduce({}) do |hash, attr|
283
+ details_hash = @person_parser.target_attrs_details.reduce({}) do |hash, attr|
263
284
  value = get_from_details(person, attr)
264
285
  hash.merge(hash_attr(attr, value))
265
286
  end
266
287
 
267
- account_hash = @parser.target_attrs_account.reduce({}) do |hash, attr|
288
+ account_hash = @person_parser.target_attrs_account.reduce({}) do |hash, attr|
268
289
  value = get_from_account(person, attr)
269
290
  hash.merge(hash_attr(attr, value))
270
291
  end
271
292
 
272
- details_hash.merge(account_hash).merge(core_hash)
273
- #core_hash.merge(account_hash).merge(details_hash)
274
- end
275
-
276
-
277
- def to_internal(value)
278
- return value if !@mapper
279
- attr = value
280
- case value
281
- when Array
282
- return value.map do |v|
283
- to_internal(v)
284
- end.compact
285
- when String
286
- case
287
- when @mapper.external?(value)
288
- attr = @mapper.to_internal(value)
289
- when @mapper.external?(value.strip)
290
- unless cached_warning("external", "spaces", value)
291
- logger.warn("The external person field name '#{value}' contains additional spaces in the reference file")
292
- end
293
- attr = @mapper.to_internal(value.strip)
294
- when @mapper.internal?(value) || @mapper.internal?(value.strip) || @mapper.internal?(value.strip.downcase)
295
- unless cached_warning("external", "reversed", value)
296
- logger.warn("The mapper [external, internal] attribute names may be declared reversedly for EXTERNAL attribute: '#{value}'")
297
- end
298
- end
299
- end
300
-
301
- return nil unless @parser.all_attrs.include?(attr)
302
- end
303
-
304
- def to_external(value)
305
- return value if !@mapper
306
- attr = value
307
- case value
308
- when Array
309
- return value.map do |v|
310
- to_external(v)
311
- end.compact
312
- when String
313
- case
314
- when @mapper.internal?(value)
315
- attr = @mapper.to_external(value)
316
- when @mapper.internal?(value.strip)
317
- unless cached_warning("internal", "spaces", value)
318
- logger.warn("The internal person field name '#{value}' contains additional spaces in the reference file")
319
- end
320
- attr = @mapper.to_external(value.strip)
321
- when @mapper.external?(value) || @mapper.external?(value.strip) || @mapper.external?(value.strip.downcase)
322
- unless cached_warning("internal", "reversed", value)
323
- logger.warn("The mapper [external, internal] attribute names may be declared reversedly for INTERNAL attribute: '#{value}'")
324
- end
325
- end
326
- end
327
-
328
- return nil unless !@raw_entry || attributes(@raw_entry).include?(attr)
329
- attr
293
+ # merge by core overriding account and details
294
+ rh = details_hash.merge(account_hash).merge(core_hash)
295
+ # resort hash keys
296
+ sorted_keys = core_hash.keys | account_hash.keys | details_hash.keys
297
+ sorted_keys.reduce({}) {|h,k| h[k] = rh[k]; h}
330
298
  end
331
299
 
332
300
  # LOGGER
@@ -334,16 +302,6 @@ module Eco
334
302
  @logger || ::Logger.new(IO::NULL)
335
303
  end
336
304
 
337
- def cached_warning(*args)
338
- unless exists = !!@@cached_warnings.dig(*args)
339
- args.reduce(@@cached_warnings) do |cache, level|
340
- cache[level] = {} if !cache.key?(level)
341
- cache[level]
342
- end
343
- end
344
- exists
345
- end
346
-
347
305
  def fatal(msg)
348
306
  logger.fatal(msg)
349
307
  exit
@@ -356,23 +314,6 @@ module Eco
356
314
  value
357
315
  end
358
316
 
359
- def attributes(value)
360
- case value
361
- when CSV::Row
362
- value&.headers
363
- when Hash
364
- value&.keys
365
- when PersonEntry
366
- @parser.target_attrs_core
367
- else
368
- []
369
- end
370
- end
371
-
372
- def is_person?(value)
373
- value.is_a?(Ecoportal::API::Internal::Person)
374
- end
375
-
376
317
  end
377
318
  end
378
319
  end