eco-helpers 3.0.25 → 3.0.27
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +39 -1
- data/eco-helpers.gemspec +1 -1
- data/lib/eco/api/common/loaders/base.rb +13 -1
- data/lib/eco/api/common/loaders/case_base.rb +1 -1
- data/lib/eco/api/common/loaders/error_handler.rb +1 -1
- data/lib/eco/api/common/loaders/parser.rb +2 -2
- data/lib/eco/api/common/loaders/policy.rb +1 -1
- data/lib/eco/api/common/loaders/use_case.rb +1 -1
- data/lib/eco/api/common/people/default_parsers/archived_parser.rb +19 -0
- data/lib/eco/api/common/people/default_parsers/boolean_parser.rb +4 -4
- data/lib/eco/api/common/people/default_parsers/date_parser.rb +3 -3
- data/lib/eco/api/common/people/default_parsers/freemium_parser.rb +6 -6
- data/lib/eco/api/common/people/default_parsers/login_providers_parser.rb +3 -3
- data/lib/eco/api/common/people/default_parsers/multi_parser.rb +4 -4
- data/lib/eco/api/common/people/default_parsers/policy_groups_parser.rb +9 -6
- data/lib/eco/api/common/people/default_parsers/send_invites_parser.rb +6 -5
- data/lib/eco/api/common/people/default_parsers/xls_parser.rb +2 -2
- data/lib/eco/api/common/people/default_parsers.rb +1 -0
- data/lib/eco/api/common/people/entries.rb +16 -15
- data/lib/eco/api/common/people/person_entry.rb +53 -37
- data/lib/eco/api/common/people/person_parser.rb +8 -6
- data/lib/eco/api/common/session/logger/channels.rb +1 -1
- data/lib/eco/api/common/session/logger.rb +2 -2
- data/lib/eco/api/common/session/mailer/aws_provider.rb +3 -2
- data/lib/eco/api/common/session/mailer/provider_base.rb +6 -2
- data/lib/eco/api/common/session/mailer/sendgrid_provider.rb +9 -9
- data/lib/eco/api/common/session/mailer.rb +8 -3
- data/lib/eco/api/common/session/sftp.rb +2 -2
- data/lib/eco/api/common/version_patches/object.rb +2 -1
- data/lib/eco/api/custom/mailer.rb +1 -1
- data/lib/eco/api/error/handlers.rb +3 -3
- data/lib/eco/api/error.rb +17 -17
- data/lib/eco/api/microcases/people/apply_changes/set_account/account_excluded.rb +3 -11
- data/lib/eco/api/microcases/people/apply_changes/set_core/core_excluded.rb +4 -1
- data/lib/eco/api/microcases/people/manage/search.rb +3 -1
- data/lib/eco/api/organization/people.rb +1 -0
- data/lib/eco/api/policies.rb +2 -2
- data/lib/eco/api/session/batch/job.rb +2 -2
- data/lib/eco/api/session/batch/launcher.rb +4 -5
- data/lib/eco/api/session/batch/searcher.rb +4 -4
- data/lib/eco/api/session/config/api.rb +2 -2
- data/lib/eco/api/session/config/apis/enviro_spaces.rb +3 -3
- data/lib/eco/api/session/config/apis/space_helpers.rb +4 -4
- data/lib/eco/api/session/config/apis.rb +1 -1
- data/lib/eco/api/session/config/post_launch.rb +7 -4
- data/lib/eco/api/session/config/sftp.rb +1 -1
- data/lib/eco/api/session/config/tagtree.rb +1 -1
- data/lib/eco/api/session/config/workflow.rb +4 -4
- data/lib/eco/api/session/config.rb +25 -24
- data/lib/eco/api/session.rb +4 -4
- data/lib/eco/api/usecases/base_io/validations.rb +4 -3
- data/lib/eco/api/usecases/cli/option.rb +5 -2
- data/lib/eco/api/usecases/default/people/amend/clear_abilities_case.rb +2 -2
- data/lib/eco/api/usecases/default/people/amend/restore_db_case.rb +16 -9
- data/lib/eco/api/usecases/default/people/treat/analyse_people_case.rb +20 -20
- data/lib/eco/api/usecases/default.rb +5 -5
- data/lib/eco/api/usecases/default_cases/to_csv_case.rb +8 -8
- data/lib/eco/api/usecases/default_cases/upsert_case.rb +4 -4
- data/lib/eco/api/usecases/graphql/helpers/base.rb +1 -1
- data/lib/eco/api/usecases/graphql/helpers/location/base/tree_tracking.rb +5 -1
- data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/commandable.rb +2 -1
- data/lib/eco/api/usecases/graphql/helpers/location/command/result.rb +3 -3
- data/lib/eco/api/usecases/graphql/helpers/location/command.rb +3 -0
- data/lib/eco/api/usecases/graphql/samples/location/command/dsl.rb +2 -2
- data/lib/eco/api/usecases/graphql/samples/location/command/service/tree_update.rb +9 -3
- data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/inputable.rb +5 -4
- data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/parsing.rb +5 -2
- data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff.rb +3 -3
- data/lib/eco/api/usecases/graphql/utils/sftp.rb +1 -1
- data/lib/eco/api/usecases/lib/{file_pattern.rb → files/file_pattern.rb} +5 -1
- data/lib/eco/api/usecases/lib/{sftp.rb → files/sftp.rb} +27 -16
- data/lib/eco/api/usecases/lib/files.rb +7 -0
- data/lib/eco/api/usecases/lib.rb +1 -2
- data/lib/eco/api/usecases/ooze_cases/export_register_case.rb +5 -5
- data/lib/eco/api/usecases/ooze_samples/helpers/exportable_ooze.rb +14 -11
- data/lib/eco/api/usecases/ooze_samples/helpers_migration/copying.rb +14 -23
- data/lib/eco/api/usecases/ooze_samples/helpers_migration/typed_fields_pairing.rb +49 -27
- data/lib/eco/api/usecases/ooze_samples/ooze_base_case.rb +9 -9
- data/lib/eco/api/usecases/ooze_samples/ooze_run_base_case.rb +1 -1
- data/lib/eco/api/usecases/ooze_samples/register_export_case.rb +25 -17
- data/lib/eco/api/usecases/ooze_samples/register_migration_case.rb +41 -24
- data/lib/eco/api/usecases/ooze_samples/register_update_case.rb +16 -15
- data/lib/eco/api/usecases/samples/drivers/cli/sftp_cli.rb +15 -15
- data/lib/eco/api/usecases/samples/drivers/cli/url_pull_cli.rb +5 -5
- data/lib/eco/api/usecases/samples/drivers/sftp_sample.rb +2 -2
- data/lib/eco/api/usecases/use_case.rb +6 -6
- data/lib/eco/api/usecases/use_case_chain/chaining.rb +6 -6
- data/lib/eco/api/usecases/use_case_chain.rb +4 -4
- data/lib/eco/api/usecases/use_case_io.rb +2 -1
- data/lib/eco/api/usecases.rb +9 -9
- data/lib/eco/cli/config/options_set.rb +4 -4
- data/lib/eco/cli/config/use_cases.rb +3 -3
- data/lib/eco/cli/scripting/argument.rb +1 -1
- data/lib/eco/cli_default/input.rb +9 -9
- data/lib/eco/cli_default/options.rb +125 -100
- data/lib/eco/cli_default/people.rb +3 -3
- data/lib/eco/cli_default/usecases.rb +83 -83
- data/lib/eco/cli_default/workflow.rb +7 -7
- data/lib/eco/data/files/helpers.rb +3 -3
- data/lib/eco/data/fuzzy_match/result.rb +69 -26
- data/lib/eco/data/fuzzy_match/results.rb +10 -10
- data/lib/eco/data/fuzzy_match/score.rb +13 -8
- data/lib/eco/data/fuzzy_match.rb +65 -48
- data/lib/eco/data/locations/node_base/treeify.rb +13 -11
- data/lib/eco/data/mapper.rb +4 -4
- data/lib/eco/language/auxiliar_logger.rb +4 -4
- data/lib/eco/language/delegation/const_delegator.rb +64 -0
- data/lib/eco/language/delegation/const_lookup_hooks.rb +81 -0
- data/lib/eco/language/delegation/delegated_class.rb +84 -0
- data/lib/eco/language/delegation.rb +8 -0
- data/lib/eco/language/klass/when_inherited.rb +1 -0
- data/lib/eco/language/methods/delegate_missing.rb +1 -0
- data/lib/eco/language/models/class_helpers.rb +25 -23
- data/lib/eco/language/models/collection.rb +12 -2
- data/lib/eco/language.rb +1 -0
- data/lib/eco/version.rb +1 -1
- metadata +12 -6
data/lib/eco/data/fuzzy_match.rb
CHANGED
@@ -23,7 +23,7 @@ module Eco
|
|
23
23
|
include CharsPositionScore
|
24
24
|
include NGramsScore
|
25
25
|
|
26
|
-
def jaro_winkler(
|
26
|
+
def jaro_winkler(str_1, str_2, **options)
|
27
27
|
return 0 if !str1 || !str2
|
28
28
|
options = {
|
29
29
|
ignore_case: true,
|
@@ -31,26 +31,26 @@ module Eco
|
|
31
31
|
}.merge(options)
|
32
32
|
|
33
33
|
require 'jaro_winkler'
|
34
|
-
JaroWinkler.distance(
|
34
|
+
JaroWinkler.distance(str_1, str_2, **options)
|
35
35
|
end
|
36
36
|
|
37
37
|
end
|
38
38
|
|
39
39
|
module InstanceMethods
|
40
|
-
FUZZY_MATCH_OPTIONS = [
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
]
|
40
|
+
FUZZY_MATCH_OPTIONS = %i[
|
41
|
+
identities groupings stop_words read
|
42
|
+
must_match_grouping must_match_at_least_one_word
|
43
|
+
gather_last_result threshold
|
44
|
+
].freeze
|
45
45
|
|
46
|
-
JARO_OPTIONS = [
|
47
|
-
NGRAMS_OPTIONS = [
|
48
|
-
POSITION_OPTIONS = [
|
49
|
-
RESULTS_OPTIONS = [
|
46
|
+
JARO_OPTIONS = %i[ignore_case weight].freeze
|
47
|
+
NGRAMS_OPTIONS = %i[range].freeze
|
48
|
+
POSITION_OPTIONS = %i[max_distance].freeze
|
49
|
+
RESULTS_OPTIONS = %i[order threshold].freeze
|
50
50
|
|
51
51
|
include StopWords
|
52
52
|
|
53
|
-
|
53
|
+
attr_writer :fuzzy_options
|
54
54
|
|
55
55
|
def fuzzy_options
|
56
56
|
@fuzzy_options ||= {}
|
@@ -58,60 +58,71 @@ module Eco
|
|
58
58
|
|
59
59
|
def fuzzy_match(haystack_data = nil, **options)
|
60
60
|
if instance_variable_defined?(:@fuzzy_match) && !haystack_data
|
61
|
-
return @fuzzy_match if fuzzy_match_options == fuzzy_match_options(options)
|
61
|
+
return @fuzzy_match if fuzzy_match_options == fuzzy_match_options(options) # rubocop:disable Style/SoleNestedConditional
|
62
62
|
end
|
63
|
+
|
63
64
|
@fuzzy_options = options
|
64
65
|
|
65
66
|
# make it run with a native C extension (for better performance: ~130 % increase of performance)
|
66
67
|
require 'fuzzy_match'
|
67
68
|
require 'amatch'
|
69
|
+
|
68
70
|
::FuzzyMatch.engine = :amatch
|
69
71
|
@fuzzy_match = ::FuzzyMatch.new(haystack(haystack_data), fuzzy_match_options)
|
70
72
|
end
|
71
73
|
|
72
|
-
#
|
74
|
+
# @todo integration for options[:unique_words] => to ensure repeated words do not bring down
|
75
|
+
# the score are cut by threshold
|
73
76
|
# @note
|
74
77
|
# - When the `haystack` elements are **non** `String` objects, it excludes the needle itself from the results
|
75
78
|
# @param needle [String, Object] object is allowed when `fuzzy_options` includes `read:` key.
|
76
79
|
# @param needle_str [String, nil] the actual value of needle_str to be used.
|
77
80
|
# @param haystack [Enumerable] the items to find `needle` among.
|
78
81
|
# @return [Eco::Data::FuzzyMatch::Results]
|
79
|
-
def find_all_with_score(needle, needle_str: nil, haystack: nil, **options)
|
82
|
+
def find_all_with_score(needle, needle_str: nil, haystack: nil, **options) # rubocop:disable Metrics/AbcSize
|
80
83
|
base_match = fuzzy_match(haystack, **options)
|
81
84
|
match_results = base_match.find_all_with_score(needle_str || needle)
|
82
85
|
needle_str ||= item_string(needle)
|
83
86
|
results = match_results.each_with_object([]) do |fuzzy_results, results|
|
84
87
|
item, dice, lev = fuzzy_results
|
85
|
-
|
86
|
-
item_str = item_string(item)
|
87
|
-
|
88
|
-
if item_str.to_s.strip.empty? || needle_str.to_s.strip.empty?
|
89
|
-
dice = lev = jaro_res = ngram_res = ngram_res = wngram_res = pos_res = 0
|
90
|
-
end
|
88
|
+
next if item == needle
|
91
89
|
|
92
|
-
|
93
|
-
ngram_res ||= ngram(needle_str, item_str)
|
94
|
-
wngram_res ||= words_ngram(needle_str, item_str)
|
95
|
-
pos_res ||= position(needle_str, item_str)
|
90
|
+
item_str = item_string(item)
|
96
91
|
|
97
|
-
|
92
|
+
if item_str.to_s.strip.empty? || needle_str.to_s.strip.empty?
|
93
|
+
dice = lev = jaro_res = ngram_res = ngram_res = wngram_res = pos_res = 0
|
98
94
|
end
|
95
|
+
|
96
|
+
jaro_res ||= jaro(needle_str, item_str)
|
97
|
+
ngram_res ||= ngram(needle_str, item_str)
|
98
|
+
wngram_res ||= words_ngram(needle_str, item_str)
|
99
|
+
pos_res ||= position(needle_str, item_str)
|
100
|
+
|
101
|
+
results << Result.new(item, item_str, needle_str, dice, lev, jaro_res, ngram_res, wngram_res, pos_res)
|
99
102
|
end
|
103
|
+
|
100
104
|
Results.new(needle, needle_str, results).tap do |res|
|
101
105
|
res.order = fuzzy_options[:order] if fuzzy_options[:order]
|
102
106
|
res.threshold = fuzzy_options[:threshold] if fuzzy_options[:threshold]
|
103
107
|
end.relevant_results
|
104
108
|
end
|
105
109
|
|
106
|
-
def recalculate_results(results, needle_str: nil, **options)
|
107
|
-
|
110
|
+
def recalculate_results(results, needle_str: nil, **options) # rubocop:disable Metrics/AbcSize
|
111
|
+
msg = "You should provide a block |needle_str, item_str, needle, item|"
|
112
|
+
raise msg unless block_given?
|
113
|
+
|
108
114
|
new_results = results.each_with_object([]) do |result, new_results|
|
109
|
-
nstr, istr = yield(
|
115
|
+
nstr, istr = yield(
|
116
|
+
needle_str || results.value,
|
117
|
+
result.value,
|
118
|
+
results.needle,
|
119
|
+
result.match
|
120
|
+
)
|
110
121
|
|
111
122
|
if istr.to_s.strip.empty?
|
112
123
|
dice = lev = jaro_res = ngram_res = ngram_res = wngram_res = pos_res = 1
|
113
124
|
elsif nstr.to_s.strip.empty?
|
114
|
-
unless istr = needle_str
|
125
|
+
unless (istr = needle_str)
|
115
126
|
dice = lev = jaro_res = ngram_res = ngram_res = wngram_res = pos_res = 0
|
116
127
|
end
|
117
128
|
end
|
@@ -119,7 +130,7 @@ module Eco
|
|
119
130
|
require 'fuzzy_match'
|
120
131
|
require 'amatch'
|
121
132
|
res = ::FuzzyMatch.score_class.new(nstr, istr) unless dice && lev
|
122
|
-
|
133
|
+
|
123
134
|
dice ||= res&.dices_coefficient_similar || 0
|
124
135
|
lev ||= res&.levenshtein_similar || 0
|
125
136
|
jaro_res ||= jaro(nstr, istr)
|
@@ -129,6 +140,7 @@ module Eco
|
|
129
140
|
|
130
141
|
new_results << Result.new(*result.values_at(:match, :value, :needle_str), dice, lev, jaro_res, ngram_res, wngram_res, pos_res)
|
131
142
|
end
|
143
|
+
|
132
144
|
Results.new(results.needle, results.value, new_results).tap do |res|
|
133
145
|
res.order = options[:order] if options[:order]
|
134
146
|
res.threshold = options[:threshold] if options[:threshold]
|
@@ -137,24 +149,24 @@ module Eco
|
|
137
149
|
|
138
150
|
private
|
139
151
|
|
140
|
-
def jaro(
|
152
|
+
def jaro(str_1, str_2)
|
141
153
|
options = fuzzy_options.slice(*JARO_OPTIONS)
|
142
|
-
self.class.jaro_winkler(
|
154
|
+
self.class.jaro_winkler(str_1, str_2, **options)
|
143
155
|
end
|
144
156
|
|
145
|
-
def ngram(
|
157
|
+
def ngram(str_1, str_2)
|
146
158
|
options = { range: 3..5 }.merge(fuzzy_options.slice(*NGRAMS_OPTIONS))
|
147
|
-
self.class.ngrams_score(
|
159
|
+
self.class.ngrams_score(str_1, str_2, **options).ratio
|
148
160
|
end
|
149
161
|
|
150
|
-
def words_ngram(
|
162
|
+
def words_ngram(str_1, str_2)
|
151
163
|
options = { range: 3..7 }.merge(fuzzy_options.slice(*NGRAMS_OPTIONS))
|
152
|
-
self.class.words_ngrams_score(
|
164
|
+
self.class.words_ngrams_score(str_1, str_2, **options).ratio
|
153
165
|
end
|
154
166
|
|
155
|
-
def position(
|
167
|
+
def position(str_1, str_2)
|
156
168
|
options = fuzzy_options.slice(*POSITION_OPTIONS)
|
157
|
-
self.class.chars_position_score(
|
169
|
+
self.class.chars_position_score(str_1, str_2, **options).ratio
|
158
170
|
end
|
159
171
|
|
160
172
|
# @note
|
@@ -162,12 +174,18 @@ module Eco
|
|
162
174
|
# @param data [Enumerable, nil]
|
163
175
|
# @return [Array<Object>] the non-repeated values of `data`
|
164
176
|
def haystack(data = nil)
|
165
|
-
data = self if
|
166
|
-
|
167
|
-
|
177
|
+
data = self if is_a?(Enumerable) && !data
|
178
|
+
|
179
|
+
msg = "'data' should be an Enumerable. Given: #{data.class}"
|
180
|
+
raise ArgumentError, msg unless data.is_a?(Enumerable)
|
181
|
+
|
182
|
+
data = is_a?(Hash) ? values.flatten : to_a.flatten
|
183
|
+
|
168
184
|
data.uniq.compact.tap do |items|
|
169
|
-
if !fuzzy_read_method && found = items.find {|item| !item.is_a?(String)}
|
170
|
-
|
185
|
+
if !fuzzy_read_method && (found = items.find {|item| !item.is_a?(String)})
|
186
|
+
msg = "To use non String objects as 'haystack' you should provide `read:` or `options[:read]`. "
|
187
|
+
msg << "Given element: #{found.class}"
|
188
|
+
raise ArgumentError, msg
|
171
189
|
end
|
172
190
|
end
|
173
191
|
end
|
@@ -175,12 +193,13 @@ module Eco
|
|
175
193
|
def item_string(item, attr = fuzzy_read_method)
|
176
194
|
return item if !item || item.is_a?(String) || !attr
|
177
195
|
return attr.call(item) if attr.is_a?(Proc)
|
196
|
+
|
178
197
|
attr = attr.to_sym
|
179
|
-
|
198
|
+
item.send(attr) if item.respond_to?(attr, true)
|
180
199
|
end
|
181
200
|
|
182
201
|
def fuzzy_match_options(options = nil)
|
183
|
-
options
|
202
|
+
options ||= fuzzy_options
|
184
203
|
options.slice(*FUZZY_MATCH_OPTIONS).merge({
|
185
204
|
stop_words: PREPOSITIONS + PRONOUNS + ARTICLES
|
186
205
|
})
|
@@ -189,13 +208,11 @@ module Eco
|
|
189
208
|
def fuzzy_read_method
|
190
209
|
fuzzy_match_options[:read]
|
191
210
|
end
|
192
|
-
|
193
211
|
end
|
194
212
|
|
195
213
|
class << self
|
196
214
|
include FuzzyMatch::ClassMethods
|
197
215
|
end
|
198
|
-
|
199
216
|
end
|
200
217
|
end
|
201
218
|
end
|
@@ -16,7 +16,7 @@ module Eco::Data::Locations::NodeBase
|
|
16
16
|
raise ArgumentError, msg unless node.is_a?(Eco::Data::Locations::NodeBase)
|
17
17
|
|
18
18
|
node.node_hash.tap do |json|
|
19
|
-
json.merge!({
|
19
|
+
json.merge!({'parent_id' => parent_id}) unless parent_id == :unused
|
20
20
|
json.merge!(yield(node, json)) if block_given?
|
21
21
|
end
|
22
22
|
end
|
@@ -90,7 +90,7 @@ module Eco::Data::Locations::NodeBase
|
|
90
90
|
|
91
91
|
node_hash = serialize_node(child, parent_id: node_id, &block)
|
92
92
|
results << node_hash
|
93
|
-
node_hash[
|
93
|
+
node_hash['nodes'] = get_children(
|
94
94
|
child.id, parents,
|
95
95
|
parent: child, done_ids: done_ids,
|
96
96
|
level: level + 1, skipped: skipped,
|
@@ -105,7 +105,7 @@ module Eco::Data::Locations::NodeBase
|
|
105
105
|
end
|
106
106
|
|
107
107
|
def parent_msg(parent)
|
108
|
-
parent ? "child of '#{parent.id}'" :
|
108
|
+
parent ? "child of '#{parent.id}'" : 'top level'
|
109
109
|
end
|
110
110
|
|
111
111
|
def level_msg(level)
|
@@ -113,7 +113,7 @@ module Eco::Data::Locations::NodeBase
|
|
113
113
|
end
|
114
114
|
|
115
115
|
def indent(level)
|
116
|
-
|
116
|
+
' ' * level
|
117
117
|
end
|
118
118
|
|
119
119
|
# Method to ensure the results are consistent
|
@@ -122,7 +122,7 @@ module Eco::Data::Locations::NodeBase
|
|
122
122
|
# because otherwise would be part of `done_ids` anyway.
|
123
123
|
# @param unlinked_trees [Array<Hash>] by excluding those done and skipped,
|
124
124
|
# it will treeify the unlinked nodes (the exclusion applies to `parants_hash`)
|
125
|
-
def check_results( # rubocop:disable Metrics/AbcSize
|
125
|
+
def check_results( # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
126
126
|
_tree, nodes, parents,
|
127
127
|
done_ids: {}, skipped: [], unlinked_trees: [],
|
128
128
|
warns: [], &block
|
@@ -138,9 +138,9 @@ module Eco::Data::Locations::NodeBase
|
|
138
138
|
|
139
139
|
# The reason of missing nodes in the output tree is unknown!
|
140
140
|
if skipped.empty? && unlinked_parent_ids.empty?
|
141
|
-
msg
|
142
|
-
msg <<
|
143
|
-
msg <<
|
141
|
+
msg = []
|
142
|
+
msg << 'BUG in this library (open issue with maintainers).'
|
143
|
+
msg << 'There were no skipped nodes nor missin referred parents, and yet:'
|
144
144
|
msg << " * the tree nodes count: #{done_ids.count} ..."
|
145
145
|
msg << " * doesn't match the original nodes count: #{nodes.count}"
|
146
146
|
raise msg.join("\n")
|
@@ -181,7 +181,7 @@ module Eco::Data::Locations::NodeBase
|
|
181
181
|
str_skipped = (residual_skipped.count < 15 ? " => #{residual_skipped.map(&:id).join(', ')}" : '')
|
182
182
|
|
183
183
|
msg = []
|
184
|
-
msg <<
|
184
|
+
msg << 'After treeifying via the unlinked_parents:'
|
185
185
|
msg << " * total_nodes: #{nodes.count}"
|
186
186
|
msg << " * tracked_nodes: #{tracked_nodes.count}"
|
187
187
|
msg << " * untracked_nodes: #{untracked_nodes.count}"
|
@@ -230,14 +230,16 @@ module Eco::Data::Locations::NodeBase
|
|
230
230
|
node_hash = serialize_node(child, parent_id: node_id, &block) unless src_plain
|
231
231
|
descendants = get_tree_nodes_raw(child.id, parents, src_plain: src_plain, &block).tap do |desc|
|
232
232
|
next unless (nil_count = desc.count(nil)).positive?
|
233
|
+
|
233
234
|
log(:debug) {
|
234
235
|
"get_tree_nodes_raw gave #{nil_count} nil values for nodes of #{child.id}"
|
235
236
|
}
|
236
237
|
end
|
238
|
+
|
237
239
|
next results.concat(descendants) if src_plain
|
238
240
|
|
239
241
|
results << node_hash.merge({
|
240
|
-
|
242
|
+
'nodes' => descendants.compact
|
241
243
|
})
|
242
244
|
end
|
243
245
|
end
|
@@ -256,7 +258,7 @@ module Eco::Data::Locations::NodeBase
|
|
256
258
|
|
257
259
|
# With given a skipped `node` (repeated `id`), it gives different warnings,
|
258
260
|
# provided that the context in which the double-up `id` happened is identified.
|
259
|
-
def report_skipped_node( # rubocop:disable Metrics/AbcSize
|
261
|
+
def report_skipped_node( # rubocop:disable Metrics/AbcSize, Metrics/ParameterLists, Metrics/MethodLength
|
260
262
|
node, parent,
|
261
263
|
done_ids, level,
|
262
264
|
level_ids, parents,
|
data/lib/eco/data/mapper.rb
CHANGED
@@ -2,7 +2,7 @@ module Eco
|
|
2
2
|
module Data
|
3
3
|
class Mapper
|
4
4
|
# it expects [[v1a, v1b], [v2a, v2b] ...]
|
5
|
-
def initialize
|
5
|
+
def initialize(array_of_arrays = [], internal: :last, insensitive: false)
|
6
6
|
@internal_order = internal
|
7
7
|
@source = array_of_arrays
|
8
8
|
@insensitive = insensitive
|
@@ -23,7 +23,7 @@ module Eco
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
@by_internal = @source.reverse.
|
26
|
+
@by_internal = @source.reverse.to_h(&:reverse).tap do |h_data|
|
27
27
|
next unless insensitive?
|
28
28
|
|
29
29
|
h_data.dup.each do |key, value|
|
@@ -57,7 +57,7 @@ module Eco
|
|
57
57
|
src_dup.map(&:reverse)
|
58
58
|
end
|
59
59
|
|
60
|
-
def +(array_of_arrays)
|
60
|
+
def +(array_of_arrays) # rubocop:disable Naming/BinaryOperatorParameterName
|
61
61
|
self.class.new(array_of_arrays + to_a, internal: @internal_order)
|
62
62
|
end
|
63
63
|
|
@@ -70,7 +70,7 @@ module Eco
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def internal?(value)
|
73
|
-
return true
|
73
|
+
return true unless @source
|
74
74
|
|
75
75
|
value = value.downcase if insensitive?
|
76
76
|
@by_internal.key?(value)
|
@@ -9,8 +9,8 @@ module Eco
|
|
9
9
|
def logger
|
10
10
|
if instance_variable_defined?(:@session) && !@session.nil?
|
11
11
|
@session.logger
|
12
|
-
elsif respond_to?(:session)
|
13
|
-
session.logger
|
12
|
+
elsif respond_to?(:session, true)
|
13
|
+
send(:session).logger
|
14
14
|
elsif Object.const_defined?(:ASSETS)
|
15
15
|
ASSETS.session.logger
|
16
16
|
elsif defined?(super)
|
@@ -31,8 +31,8 @@ module Eco
|
|
31
31
|
levels = levels.compact.uniq.map(&:to_sym)
|
32
32
|
levels.unshift(:info) if levels.include?(:general) && levels.length == 1
|
33
33
|
|
34
|
-
levels.each do |level|
|
35
|
-
next unless logger.respond_to?(:level)
|
34
|
+
levels.uniq.each do |level|
|
35
|
+
next unless logger.respond_to?(:level, true)
|
36
36
|
|
37
37
|
logger.send(level, &block)
|
38
38
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Eco::Language::Delegation
|
2
|
+
# Module that is to be used with ruby `Delegator` or related helpers,
|
3
|
+
# such as `SimpleDelegator` or `Forwardable`.
|
4
|
+
# @note it includes `DelegatedClass`
|
5
|
+
# @note it looks up the constant via `delegated_class`. This is because
|
6
|
+
# on `Delegator` approaches, the intance object (`self`) points to the
|
7
|
+
# delegated object, which doesn't allow to refer to `self.class` to
|
8
|
+
# implement a delegation of constants that other way.
|
9
|
+
# For example, `self.class::CONSTANT` will not point to the delegator
|
10
|
+
# class constant, but to the constant of the delegated object's class.
|
11
|
+
module ConstDelegator
|
12
|
+
class << self
|
13
|
+
private
|
14
|
+
|
15
|
+
def included(base)
|
16
|
+
super
|
17
|
+
|
18
|
+
base.send :include, Eco::Language::DelegatedClass
|
19
|
+
base.extend(ClassMethods)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
def const_defined?(str, inherit = true, delegated: true)
|
25
|
+
return super(str, inherit) if super(str, inherit) || !delegated
|
26
|
+
return true if delegated_class&.const_defined?(str, inherit)
|
27
|
+
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
def const_get(str)
|
32
|
+
return super if const_defined?(str, delegated: false)
|
33
|
+
return delegated_class.const_get(str) if delegated_class&.const_defined?(str)
|
34
|
+
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
def const_missing(str)
|
39
|
+
return super unless const_defined?(str)
|
40
|
+
|
41
|
+
const_get(str)
|
42
|
+
end
|
43
|
+
|
44
|
+
def constants(inherited = true, delegated: true)
|
45
|
+
super(inherited).dup.tap do |consts|
|
46
|
+
next unless delegated && delegated_class
|
47
|
+
|
48
|
+
delegated_class.constants(inherited).each do |const|
|
49
|
+
consts.unshift(const)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def remove_const(str)
|
57
|
+
return super unless const_defined?(str)
|
58
|
+
return super if const_defined?(str, delegated: false)
|
59
|
+
|
60
|
+
raise NameError, "#{str} not defined (is delegated to #{delegated_class})"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Eco::Language::Delegation
|
2
|
+
# This approach only makes sense to be used in `Delegator` and related helpers.
|
3
|
+
# @note it includes `ConstDelegator` (which includes `DelegatedClass`)
|
4
|
+
# @note for each not inherited constant that isn't part of the delegated object's
|
5
|
+
# class, it will create a undercore/snake named instance method, to ease
|
6
|
+
# constant lookup througout chained/wrapped delegators.
|
7
|
+
module ConstLookupHooks
|
8
|
+
class << self
|
9
|
+
private
|
10
|
+
|
11
|
+
def included(base)
|
12
|
+
super
|
13
|
+
|
14
|
+
base.send :include, Eco::Language::Delegation::ConstDelegator
|
15
|
+
|
16
|
+
base.extend(ClassMethods)
|
17
|
+
base.send :install_constant_lookup_hooks
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module ClassMethods
|
22
|
+
private
|
23
|
+
|
24
|
+
def inherited(subclass)
|
25
|
+
super
|
26
|
+
|
27
|
+
subclass.send :install_constant_lookup_hooks
|
28
|
+
end
|
29
|
+
|
30
|
+
def const_added(sym)
|
31
|
+
install_constant_lookup_hook(const: sym)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Drawback is this will also create methods for modules and classes hanging
|
35
|
+
# from `base` namespace. Unfortunatelly, we need to go with them, because
|
36
|
+
# it might happen a constant points to one of them to implement some behaviour.
|
37
|
+
def install_constant_lookup_hooks(base = self)
|
38
|
+
# Only method-hook constants that are not inherited,
|
39
|
+
# nor delegated to the decorated class.
|
40
|
+
# This constraint ensure that we don't overpopulate with hook methods,
|
41
|
+
# and that the lookup respects the order in which the decorators are
|
42
|
+
# wrapped (i.e. Sugar.new(Milk.new(Coffe.new)) < Sugar(Milk(Coffee)) ).
|
43
|
+
base.constants(false, delegated: false).each do |const|
|
44
|
+
install_constant_lookup_hook(base, const: const)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def install_constant_lookup_hook(base = self, const:)
|
49
|
+
meth_name = underscore(const).to_sym
|
50
|
+
return if instance_method?(base, name: meth_name, include_private: true)
|
51
|
+
|
52
|
+
method_def = proc do |*args, **kargs, &block|
|
53
|
+
return super(*args, **kargs, &block) if defined?(super)
|
54
|
+
|
55
|
+
base.const_get(const)
|
56
|
+
end
|
57
|
+
|
58
|
+
base.define_method(meth_name, &method_def)
|
59
|
+
end
|
60
|
+
|
61
|
+
# @see https://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-underscore
|
62
|
+
def underscore(sym)
|
63
|
+
return super if defined?(super)
|
64
|
+
return sym.to_s.dup unless /[A-Z-]|::/.match?(sym)
|
65
|
+
|
66
|
+
word = sym.to_s.gsub('::', '/')
|
67
|
+
word.gsub!(/(?<=[A-Z])(?=[A-Z][a-z])|(?<=[a-z\d])(?=[A-Z])/, '_')
|
68
|
+
word.tr!('-', '_')
|
69
|
+
word.downcase!
|
70
|
+
word
|
71
|
+
end
|
72
|
+
|
73
|
+
def instance_method?(base = self, name:, include_private: false)
|
74
|
+
return true if base.method_defined?(name)
|
75
|
+
return false unless include_private
|
76
|
+
|
77
|
+
base.private_instance_methods.include?(name.to_sym)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Eco::Language::Delegation
|
2
|
+
module DelegatedClass
|
3
|
+
class << self
|
4
|
+
private
|
5
|
+
|
6
|
+
def included(base)
|
7
|
+
super
|
8
|
+
|
9
|
+
base.extend(ClassMethods)
|
10
|
+
base.send :patch_class_compare
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
def delegated_class(klass = nil)
|
16
|
+
return @delegated_class if klass.nil?
|
17
|
+
|
18
|
+
@delegated_class = klass
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def inherited(subclass)
|
24
|
+
super
|
25
|
+
|
26
|
+
subclass.instance_variable_set(:@delegated_class, @delegated_class)
|
27
|
+
end
|
28
|
+
|
29
|
+
def patch_class_compare(base = self)
|
30
|
+
%i[< <= ==].each do |op|
|
31
|
+
base.singleton_class.define_method(op) do |other|
|
32
|
+
return super(other) unless delegated_class
|
33
|
+
return true if super(other)
|
34
|
+
|
35
|
+
delegated_class.send(op, other)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Instance methods
|
42
|
+
|
43
|
+
# It allows to chain delegators, when the first parameter is an
|
44
|
+
# instance of `delegated_class`.
|
45
|
+
# @note it also allows to create an instance of `delegated_class`
|
46
|
+
# on initialization.
|
47
|
+
def initialize(*args, **kargs, &block)
|
48
|
+
obj = args.first
|
49
|
+
obj = new(*args, **kargs, &block) unless of_kind?(obj)
|
50
|
+
|
51
|
+
super(obj)
|
52
|
+
end
|
53
|
+
|
54
|
+
def delegated_class
|
55
|
+
self.class.delegated_class
|
56
|
+
end
|
57
|
+
|
58
|
+
def new(...)
|
59
|
+
delegated_class.new(...)
|
60
|
+
end
|
61
|
+
|
62
|
+
def of_kind?(obj)
|
63
|
+
return true if obj.is_a?(delegated_class)
|
64
|
+
return false unless obj.is_a(Delegator)
|
65
|
+
return true if obj.__get_obj__.is_a?(delegated_class)
|
66
|
+
|
67
|
+
false
|
68
|
+
end
|
69
|
+
|
70
|
+
def is_a?(base)
|
71
|
+
return true if super
|
72
|
+
return false unless delegated_class
|
73
|
+
return true if delegated_class <= base
|
74
|
+
|
75
|
+
false
|
76
|
+
end
|
77
|
+
|
78
|
+
def instance_of?(base)
|
79
|
+
return true if base == delegated_class
|
80
|
+
|
81
|
+
super
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|