eco-helpers 3.0.37 → 3.1.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -0
- data/CHANGELOG.md +116 -1
- data/lib/eco/api/common/loaders/base.rb +2 -2
- data/lib/eco/api/common/loaders/case_base.rb +2 -0
- data/lib/eco/api/common/loaders/config/block.rb +78 -0
- data/lib/eco/api/common/loaders/config/workflow/mailer.rb +39 -7
- data/lib/eco/api/common/loaders/config.rb +3 -26
- data/lib/eco/api/common/loaders/error_handler.rb +2 -0
- data/lib/eco/api/common/loaders/parser.rb +1 -4
- data/lib/eco/api/common/people/entries.rb +23 -6
- data/lib/eco/api/common/people/entry_factory.rb +1 -1
- data/lib/eco/api/common/people/person_entry.rb +104 -27
- data/lib/eco/api/common/people/person_parser.rb +50 -16
- data/lib/eco/api/common/people/supervisor_helpers.rb +12 -6
- data/lib/eco/api/common/session/base_session.rb +75 -81
- data/lib/eco/api/common/session/environment.rb +49 -55
- data/lib/eco/api/common/session/file_manager.rb +132 -135
- data/lib/eco/api/common/session/helpers/prompt_user.rb +23 -30
- data/lib/eco/api/common/session/helpers.rb +10 -15
- data/lib/eco/api/common/session/logger/cache.rb +89 -96
- data/lib/eco/api/common/session/logger/channels.rb +24 -32
- data/lib/eco/api/common/session/logger/log.rb +38 -46
- data/lib/eco/api/common/session/logger.rb +50 -54
- data/lib/eco/api/common/session/mailer/aws_provider.rb +63 -71
- data/lib/eco/api/common/session/mailer/provider_base.rb +43 -48
- data/lib/eco/api/common/session/mailer/sendgrid_provider.rb +101 -109
- data/lib/eco/api/common/session/mailer.rb +78 -83
- data/lib/eco/api/common/session/s3_uploader.rb +132 -138
- data/lib/eco/api/common/session/sftp.rb +202 -208
- data/lib/eco/api/common.rb +0 -3
- data/lib/eco/api/custom/mailer.rb +4 -2
- data/lib/eco/api/error/handlers.rb +1 -1
- data/lib/eco/api/microcases/people/apply_changes/set_core/core_excluded.rb +8 -2
- data/lib/eco/api/microcases/people/manage/search.rb +1 -1
- data/lib/eco/api/organization/people/similarity.rb +3 -3
- data/lib/eco/api/session/batch/base_policy.rb +42 -27
- data/lib/eco/api/session/batch/launcher/mode_size.rb +6 -1
- data/lib/eco/api/session/batch/launcher.rb +16 -3
- data/lib/eco/api/session/config/api.rb +4 -3
- data/lib/eco/api/session/config/apis/one_off.rb +1 -1
- data/lib/eco/api/session/config/files.rb +13 -12
- data/lib/eco/api/session/config/workflow.rb +1 -373
- data/lib/eco/api/session/config.rb +30 -9
- data/lib/eco/api/usecases/base_case/model.rb +6 -6
- data/lib/eco/api/usecases/base_case.rb +1 -1
- data/lib/eco/api/usecases/cli.rb +1 -1
- data/lib/eco/api/usecases/default/locations/tagtree_extract_case.rb +8 -9
- data/lib/eco/api/usecases/default_cases/to_csv_case.rb +4 -1
- data/lib/eco/api/usecases/graphql/samples/location/command/dsl.rb +2 -2
- data/lib/eco/api/usecases/lib/base/env.rb +21 -23
- data/lib/eco/api/usecases/lib/files/file_pattern.rb +41 -14
- data/lib/eco/api/usecases/lib/files/input_file.rb +110 -0
- data/lib/eco/api/usecases/lib/files/sftp.rb +5 -2
- data/lib/eco/api/usecases/lib/files.rb +1 -0
- data/lib/eco/api/usecases/lib/locations/base.rb +23 -0
- data/lib/eco/api/usecases/lib/locations/mapping.rb +94 -0
- data/lib/eco/api/usecases/lib/locations.rb +7 -0
- data/lib/eco/api/usecases/lib/people/base.rb +20 -0
- data/lib/eco/api/usecases/lib/people.rb +6 -0
- data/lib/eco/api/usecases/lib.rb +2 -0
- data/lib/eco/api/usecases/ooze_cases.rb +1 -1
- data/lib/eco/api/usecases/ooze_samples/register_export_case.rb +1 -0
- data/lib/eco/api/usecases/ooze_samples/register_update_case.rb +1 -0
- data/lib/eco/api/usecases/samples/drivers/sftp_sample.rb +2 -0
- data/lib/eco/api/usecases/samples/drivers/url_pull_sample.rb +8 -2
- data/lib/eco/api/usecases/service/sftp/with_target_config.rb +0 -33
- data/lib/eco/api/usecases/service/sftp.rb +7 -1
- data/lib/eco/api/usecases/use_case.rb +3 -2
- data/lib/eco/api/usecases/workflow.rb +5 -0
- data/lib/eco/api/usecases.rb +12 -5
- data/lib/eco/cli/scripting/args_helpers.rb +1 -9
- data/lib/eco/cli_default/options.rb +98 -68
- data/lib/eco/cli_default/workflow/end.rb +22 -0
- data/lib/eco/cli_default/workflow/launch_jobs.rb +14 -0
- data/lib/eco/cli_default/workflow/load/data.rb +27 -0
- data/lib/eco/cli_default/workflow/load/input.rb +28 -0
- data/lib/eco/cli_default/workflow/load.rb +13 -0
- data/lib/eco/cli_default/workflow/options.rb +10 -0
- data/lib/eco/cli_default/workflow/post_launch.rb +65 -0
- data/lib/eco/cli_default/workflow/report.rb +17 -0
- data/lib/eco/cli_default/workflow/rescued_exception.rb +21 -0
- data/lib/eco/cli_default/workflow/usecases.rb +23 -0
- data/lib/eco/cli_default/workflow.rb +24 -180
- data/lib/eco/data/count_trace.rb +51 -0
- data/lib/eco/data/files/content.rb +39 -0
- data/lib/eco/data/files/directory.rb +78 -45
- data/lib/eco/data/files/encoding.rb +12 -21
- data/lib/eco/data/files/file_pattern.rb +15 -8
- data/lib/eco/data/files/folder.rb +196 -0
- data/lib/eco/data/files/relative_path.rb +54 -0
- data/lib/eco/data/files/timestamp.rb +18 -0
- data/lib/eco/data/files.rb +46 -5
- data/lib/eco/data/fuzzy_match.rb +1 -1
- data/lib/eco/data/hashes/array_diff.rb +11 -5
- data/lib/eco/data/hashes/diff_result/meta.rb +12 -4
- data/lib/eco/data/locations/node_diff/accessors.rb +1 -1
- data/lib/eco/data/mapper.rb +5 -1
- data/lib/eco/data.rb +1 -0
- data/lib/eco/language/delegation/delegating_missing.rb +1 -1
- data/lib/eco/language/delegation/delegating_missing_const.rb +1 -1
- data/lib/eco/language/delegation/delegating_missing_on_class.rb +1 -1
- data/lib/eco/language/delegation/for_delegator/delegated_class.rb +1 -1
- data/lib/eco/language/klass/auto_loader.rb +129 -0
- data/lib/eco/language/klass/builder.rb +6 -6
- data/lib/eco/language/klass/const.rb +19 -0
- data/lib/eco/language/klass/helpers_built.rb +3 -1
- data/lib/eco/language/klass/hierarchy.rb +5 -1
- data/lib/eco/language/klass/naming.rb +5 -2
- data/lib/eco/language/klass/resolver.rb +21 -6
- data/lib/eco/language/klass/uid.rb +12 -0
- data/lib/eco/language/klass/when_inherited.rb +30 -6
- data/lib/eco/language/klass.rb +5 -2
- data/lib/eco/language/methods/access_modifier.rb +23 -0
- data/lib/eco/language/methods/instance_method_helpers.rb +6 -1
- data/lib/eco/language/methods.rb +1 -0
- data/lib/eco/language/models/hierarchy.rb +41 -0
- data/lib/eco/language/models/workflow.rb +385 -0
- data/lib/eco/language/models.rb +2 -1
- data/lib/eco/version.rb +1 -1
- metadata +31 -7
- data/lib/eco/api/common/class_auto_loader.rb +0 -114
- data/lib/eco/api/common/class_helpers.rb +0 -9
- data/lib/eco/api/common/class_hierarchy.rb +0 -45
- data/lib/eco/data/files/helpers.rb +0 -152
- data/lib/eco/language/models/class_helpers.rb +0 -136
@@ -0,0 +1,196 @@
|
|
1
|
+
module Eco
|
2
|
+
module Data
|
3
|
+
module Files
|
4
|
+
module Folder
|
5
|
+
PRESERVED_FILES = [
|
6
|
+
/.*\.rb$/,
|
7
|
+
/.*\.sh$/,
|
8
|
+
/.*\.ps1$/,
|
9
|
+
/.*\.ya?ml$/,
|
10
|
+
/.*\.md$/,
|
11
|
+
/.*\.gemspec$/,
|
12
|
+
/Gem/,
|
13
|
+
/Rake/,
|
14
|
+
/LICENSE/,
|
15
|
+
/(?:^|[\\\/])\.[^\\\/]+$/ # i.e. `.env`, `.gitignore`
|
16
|
+
].freeze
|
17
|
+
|
18
|
+
include Eco::Language::AuxiliarLogger
|
19
|
+
include RelativePath
|
20
|
+
|
21
|
+
# It ensure that the path to the file exists
|
22
|
+
def ensure_file_path!(filename)
|
23
|
+
return if File.exist?(filename)
|
24
|
+
|
25
|
+
FileUtils.mkdir_p(File.dirname(filename))
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param pattern [String, Symbol] the `Dir` expression to match
|
29
|
+
# the target files. When a Symbol is provided, it is used as
|
30
|
+
# the expected extension of the files in the target folder `folder`.
|
31
|
+
# @param regexp [Regexp] a regular expression that will be matched
|
32
|
+
# against the **basename** of the files in the `folder` that
|
33
|
+
# matched the `pattern`.
|
34
|
+
# @param older_than [Integer] days
|
35
|
+
# @param newer_than [Integer] days
|
36
|
+
def folder_files( # rubocop:disable Metrics/AbcSize
|
37
|
+
folder = '.',
|
38
|
+
pattern = '*',
|
39
|
+
regexp: nil,
|
40
|
+
older_than: nil,
|
41
|
+
newer_than: nil,
|
42
|
+
&block
|
43
|
+
)
|
44
|
+
return [] unless folder.is_a?(String)
|
45
|
+
|
46
|
+
unless Dir.exist?(folder)
|
47
|
+
log(:error) {
|
48
|
+
"Folder '#{folder}' does not exist"
|
49
|
+
}
|
50
|
+
return []
|
51
|
+
end
|
52
|
+
|
53
|
+
folder = to_relative_path(folder)
|
54
|
+
pattern ||= '.'
|
55
|
+
pattern = "*.#{pattern}" if pattern.is_a?(Symbol)
|
56
|
+
target = File.join(File.expand_path(folder), pattern)
|
57
|
+
|
58
|
+
to_relative_path(
|
59
|
+
Dir[target]
|
60
|
+
).tap do |dir_files|
|
61
|
+
dir_files.select! {|f| File.file?(f)}
|
62
|
+
|
63
|
+
if older_than
|
64
|
+
dir_files.select! do |f|
|
65
|
+
File.mtime(f) < (Time.now - (60*60*24*older_than))
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
if newer_than
|
70
|
+
dir_files.select! do |f|
|
71
|
+
File.mtime(f) > (Time.now - (60*60*24*newer_than))
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
if regexp.is_a?(Regexp)
|
76
|
+
dir_files.select! do |f|
|
77
|
+
File.basename(f).match(regexp)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end.sort.tap do |files|
|
81
|
+
if files.empty?
|
82
|
+
matchers = [pattern, regexp].compact
|
83
|
+
log(:info) {
|
84
|
+
"Couldn't find local files (#{matchers}): in folder '#{folder}'"
|
85
|
+
}
|
86
|
+
else
|
87
|
+
log(:info) {
|
88
|
+
msg = 'Found local files: '
|
89
|
+
msg << "\n • "
|
90
|
+
msg << files.join("\n • ")
|
91
|
+
msg << "\n"
|
92
|
+
msg
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
next unless block_given?
|
97
|
+
|
98
|
+
files.each(&block)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def csv_files(
|
103
|
+
folder = '.',
|
104
|
+
regexp: nil,
|
105
|
+
older_than: nil,
|
106
|
+
newer_than: nil,
|
107
|
+
&block
|
108
|
+
)
|
109
|
+
folder_files(
|
110
|
+
folder,
|
111
|
+
'*.csv',
|
112
|
+
regexp: regexp,
|
113
|
+
older_than: older_than,
|
114
|
+
newer_than: newer_than,
|
115
|
+
&block
|
116
|
+
)
|
117
|
+
end
|
118
|
+
|
119
|
+
# @note it will exclude files matching any of the patterns defined
|
120
|
+
# in `PRESERVED_FILES`
|
121
|
+
# @note never make `folder` an argument with default
|
122
|
+
# (force it to be explicitly declared)
|
123
|
+
def clear_folder(
|
124
|
+
folder,
|
125
|
+
pattern = '*',
|
126
|
+
regexp: nil,
|
127
|
+
older_than: nil,
|
128
|
+
newer_than: nil,
|
129
|
+
timeout: 3
|
130
|
+
)
|
131
|
+
target_files = folder_files(
|
132
|
+
folder,
|
133
|
+
pattern,
|
134
|
+
regexp: regexp,
|
135
|
+
older_than: older_than,
|
136
|
+
newer_than: newer_than
|
137
|
+
).select do |file|
|
138
|
+
File.file?(file)
|
139
|
+
end
|
140
|
+
|
141
|
+
# safety check (exclude certain files)
|
142
|
+
PRESERVED_FILES.reduce(target_files) do |mem, rex|
|
143
|
+
mem.grep_v(rex) # excluse those matching pattern
|
144
|
+
end.tap do |files|
|
145
|
+
next if files.empty?
|
146
|
+
|
147
|
+
msg = ' • '
|
148
|
+
msg << files.join("\n • ")
|
149
|
+
msg << "\n"
|
150
|
+
|
151
|
+
if respond_to?(:session, true)
|
152
|
+
msg = "The following files will be removed:\n#{msg}"
|
153
|
+
|
154
|
+
send(:session).prompt_user(
|
155
|
+
'Do you want to proceed? (Y/n):',
|
156
|
+
explanation: msg,
|
157
|
+
default: 'Y',
|
158
|
+
timeout: timeout
|
159
|
+
) do |response|
|
160
|
+
next unless response.upcase.start_with?('Y')
|
161
|
+
|
162
|
+
File.delete(*files)
|
163
|
+
end
|
164
|
+
else
|
165
|
+
msg = "The following files have been removed:\n#{msg}"
|
166
|
+
|
167
|
+
File.delete(*files)
|
168
|
+
|
169
|
+
log(:info) { msg }
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def archive_file(filename, subfolder: 'Archive')
|
175
|
+
src_folder = File.dirname(filename)
|
176
|
+
subpath = File.join(src_folder, subfolder)
|
177
|
+
FileUtils.mkdir_p(subpath)
|
178
|
+
|
179
|
+
basename = File.basename(filename)
|
180
|
+
archived_file = to_relative_path(File.join(subpath, basename))
|
181
|
+
|
182
|
+
log(:info) {
|
183
|
+
msg = []
|
184
|
+
msg << "Moving file:"
|
185
|
+
msg << " • from: '#{to_relative_path(filename)}'"
|
186
|
+
msg << " • to: '#{archived_file}'"
|
187
|
+
msg.join("\n")
|
188
|
+
}
|
189
|
+
|
190
|
+
File.rename(filename, archived_file)
|
191
|
+
archived_file
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# rubocop: disable Naming/MethodParameterName
|
2
|
+
|
3
|
+
module Eco
|
4
|
+
module Data
|
5
|
+
module Files
|
6
|
+
module RelativePath
|
7
|
+
def to_relative_path(of, from: '.')
|
8
|
+
require 'pathname'
|
9
|
+
|
10
|
+
pn_from = to_pathname(from)
|
11
|
+
|
12
|
+
case of
|
13
|
+
when String, Pathname
|
14
|
+
pn_of = to_pathname(of)
|
15
|
+
|
16
|
+
begin
|
17
|
+
pn_of.relative_path_from(pn_from).to_s
|
18
|
+
rescue ArgumentError => err
|
19
|
+
puts err
|
20
|
+
of
|
21
|
+
end
|
22
|
+
when Array
|
23
|
+
of.map do |path|
|
24
|
+
to_relative_path(path, from: pn_from)
|
25
|
+
end
|
26
|
+
else
|
27
|
+
msg = 'Expected `of` argument to be '
|
28
|
+
msg << 'String, Pathname, Array<String>, or Array<Pathname>. '
|
29
|
+
msg << "Given: #{of.class}"
|
30
|
+
raise ArgumentError, msg
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def to_pathname(value)
|
37
|
+
abs_path =
|
38
|
+
case value
|
39
|
+
when String, Pathname
|
40
|
+
File.expand_path(value.to_s)
|
41
|
+
else
|
42
|
+
msg << 'String, or Pathname. '
|
43
|
+
msg << "Given: #{value.class}"
|
44
|
+
raise ArgumentError, msg
|
45
|
+
end
|
46
|
+
|
47
|
+
Pathname.new(abs_path)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# rubocop:enable Naming/MethodParameterName
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Eco
|
2
|
+
module Data
|
3
|
+
module Files
|
4
|
+
module Timestamp
|
5
|
+
DEFAULT_TIMESTAMP = '%Y-%m-%dT%H%M%S'.freeze
|
6
|
+
|
7
|
+
def timestamp(timestamp_pattern = DEFAULT_TIMESTAMP, date = Time.now)
|
8
|
+
date.strftime(timestamp_pattern)
|
9
|
+
end
|
10
|
+
|
11
|
+
def timestamp_file(filename, timestamp_pattern = DEFAULT_TIMESTAMP)
|
12
|
+
file_pattern = Eco::Data::Files::FilePattern.new(filename)
|
13
|
+
file_pattern.resolve(start: "#{timestamp(timestamp_pattern)}_")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/eco/data/files.rb
CHANGED
@@ -1,11 +1,52 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
require_relative 'files/encoding'
|
4
|
+
require_relative 'files/content'
|
5
|
+
require_relative 'files/relative_path'
|
6
|
+
require_relative 'files/folder'
|
7
|
+
require_relative 'files/file_pattern'
|
8
|
+
require_relative 'files/timestamp'
|
9
|
+
require_relative 'files/directory'
|
10
|
+
|
1
11
|
module Eco
|
2
12
|
module Data
|
3
13
|
module Files
|
14
|
+
class << self
|
15
|
+
def included(base)
|
16
|
+
super
|
17
|
+
|
18
|
+
base.send :include, InstanceMethods
|
19
|
+
base.extend ClassMethods
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module InstanceMethods
|
24
|
+
include Eco::Data::Files::Content
|
25
|
+
end
|
26
|
+
|
27
|
+
module ClassMethods
|
28
|
+
include Eco::Data::Files::Encoding
|
29
|
+
include Eco::Data::Files::Folder
|
30
|
+
include Eco::Data::Files::Timestamp
|
31
|
+
include Eco::Data::Files::RelativePath
|
32
|
+
|
33
|
+
# @todo: change method name to `split_path`
|
34
|
+
def split(path)
|
35
|
+
dir_path, file = File.split(path)
|
36
|
+
dir_path = dir_path.gsub(File::SEPARATOR, File::ALT_SEPARATOR || File::SEPARATOR)
|
37
|
+
dir_path.split(File::ALT_SEPARATOR || File::SEPARATOR).push(file)
|
38
|
+
end
|
39
|
+
|
40
|
+
def copy_file(source_file, dest_file, time_stamp: false)
|
41
|
+
dest_file = timestamp_file(dest_file) if time_stamp
|
42
|
+
File.write(dest_file, File.read(source_file))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class << self
|
47
|
+
include Files::InstanceMethods
|
48
|
+
include Files::ClassMethods
|
49
|
+
end
|
4
50
|
end
|
5
51
|
end
|
6
52
|
end
|
7
|
-
|
8
|
-
require_relative 'files/encoding'
|
9
|
-
require_relative 'files/helpers'
|
10
|
-
require_relative 'files/file_pattern'
|
11
|
-
require_relative 'files/directory'
|
data/lib/eco/data/fuzzy_match.rb
CHANGED
@@ -8,7 +8,6 @@ require_relative 'fuzzy_match/ngrams_score'
|
|
8
8
|
module Eco
|
9
9
|
module Data
|
10
10
|
module FuzzyMatch
|
11
|
-
|
12
11
|
class << self
|
13
12
|
def included(base)
|
14
13
|
super
|
@@ -27,6 +26,7 @@ module Eco
|
|
27
26
|
|
28
27
|
def jaro_winkler(str_1, str_2, **options)
|
29
28
|
return 0 if !str1 || !str2
|
29
|
+
|
30
30
|
options = {
|
31
31
|
ignore_case: true,
|
32
32
|
weight: 0.25
|
@@ -2,7 +2,7 @@ module Eco
|
|
2
2
|
module Data
|
3
3
|
module Hashes
|
4
4
|
class ArrayDiff
|
5
|
-
extend Eco::Language::
|
5
|
+
extend Eco::Language::Klass::HelpersBuilt
|
6
6
|
# We can change the `DiffResult` class (items)
|
7
7
|
class_resolver :diff_result_class, "Eco::Data::Hash::DiffResult"
|
8
8
|
|
@@ -36,9 +36,10 @@ module Eco
|
|
36
36
|
# All the items that contain the diff of a node.
|
37
37
|
# @return [Array<Eco::Data::Hash::DiffResult>]
|
38
38
|
def source_results
|
39
|
-
@source_results ||=
|
40
|
-
|
41
|
-
|
39
|
+
@source_results ||=
|
40
|
+
paired_sources.each_with_object([]) do |(src_1, src_2), res|
|
41
|
+
res << diff_result_class.new(src_1, src_2)
|
42
|
+
end
|
42
43
|
end
|
43
44
|
|
44
45
|
protected
|
@@ -55,7 +56,11 @@ module Eco
|
|
55
56
|
# @return [String] the `key` attribute of `diff_result_class`
|
56
57
|
def key
|
57
58
|
diff_result_class.key.tap do |k|
|
58
|
-
|
59
|
+
next if k.is_a?(String)
|
60
|
+
|
61
|
+
msg = "#{diff_result_class}: missing main key attr to pair records. "
|
62
|
+
msg << "Given: #{k}"
|
63
|
+
raise msg
|
59
64
|
end
|
60
65
|
end
|
61
66
|
|
@@ -85,6 +90,7 @@ module Eco
|
|
85
90
|
log(:error) {
|
86
91
|
"(ArrayDiff) Input data as 'Hash' not supported. Expecting 'Enumerable' or 'String'"
|
87
92
|
}
|
93
|
+
|
88
94
|
exit(1)
|
89
95
|
when String
|
90
96
|
to_array_of_hashes(Eco::CSV.parse(content))
|
@@ -7,7 +7,7 @@ module Eco
|
|
7
7
|
def included(base)
|
8
8
|
super
|
9
9
|
|
10
|
-
base.extend Eco::Language::
|
10
|
+
base.extend Eco::Language::Klass::HelpersBuilt
|
11
11
|
base.extend ClassMethods
|
12
12
|
base.inheritable_class_vars :key, :compared_attrs, :case_sensitive
|
13
13
|
end
|
@@ -18,6 +18,7 @@ module Eco
|
|
18
18
|
# @return [String] the attribute that is key of the node diff elements.
|
19
19
|
def key(value = nil)
|
20
20
|
return @key unless value
|
21
|
+
|
21
22
|
@key = value.to_s
|
22
23
|
end
|
23
24
|
|
@@ -41,7 +42,8 @@ module Eco
|
|
41
42
|
# Whether or not the diff calc of a node should be done case sensitive or insensitive.
|
42
43
|
def case_sensitive(value = nil)
|
43
44
|
@case_sensitive = false unless instance_variable_defined?(:@case_sensitive)
|
44
|
-
return @case_sensitive
|
45
|
+
return @case_sensitive unless value
|
46
|
+
|
45
47
|
@case_sensitive = !!value
|
46
48
|
end
|
47
49
|
|
@@ -92,6 +94,7 @@ module Eco
|
|
92
94
|
return true if val_1 == val_2
|
93
95
|
return false if case_sensitive?
|
94
96
|
return false unless val_2 && val_1
|
97
|
+
|
95
98
|
val_1.upcase == val_2.upcase
|
96
99
|
end
|
97
100
|
|
@@ -102,8 +105,10 @@ module Eco
|
|
102
105
|
def eq_ary?(val_1, val_2)
|
103
106
|
return false unless [val_1, val_2].all? {|v| v.is_a?(Array)}
|
104
107
|
return true if val_1 == val_2
|
108
|
+
|
105
109
|
v_1 = case_sensitive?? val_1 : val_1.map {|v| v&.upcase}
|
106
110
|
v_2 = case_sensitive?? val_2 : val_2.map {|v| v&.upcase}
|
111
|
+
|
107
112
|
joined = (v_1 | v_2)
|
108
113
|
(joined & v_1) == (joined & v_2)
|
109
114
|
end
|
@@ -112,17 +117,20 @@ module Eco
|
|
112
117
|
# @note DSL to be able to modify behaviour
|
113
118
|
# (i.e. indifferent access for camel and snake case keys)
|
114
119
|
def get_attr(src, key)
|
115
|
-
return
|
120
|
+
return unless src
|
121
|
+
|
116
122
|
src[key.to_s]
|
117
123
|
end
|
118
124
|
|
119
125
|
def slice_attrs(src, *keys)
|
120
|
-
return
|
126
|
+
return unless src
|
127
|
+
|
121
128
|
src.slice(*keys)
|
122
129
|
end
|
123
130
|
|
124
131
|
def key_present?(src, key)
|
125
132
|
return false unless src
|
133
|
+
|
126
134
|
src.key?(key)
|
127
135
|
end
|
128
136
|
end
|
data/lib/eco/data/mapper.rb
CHANGED
@@ -34,6 +34,10 @@ module Eco
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
+
def empty?
|
38
|
+
@by_external.empty? && @by_internal.empty?
|
39
|
+
end
|
40
|
+
|
37
41
|
def insensitive?
|
38
42
|
@insensitive
|
39
43
|
end
|
@@ -44,7 +48,7 @@ module Eco
|
|
44
48
|
|
45
49
|
def to_json(internal: @internal_order)
|
46
50
|
content = as_json(internal: internal).map do |pair|
|
47
|
-
"
|
51
|
+
" #{pair.to_json}"
|
48
52
|
end.join(",\n")
|
49
53
|
|
50
54
|
"[\n#{content}\n]"
|
data/lib/eco/data.rb
CHANGED
@@ -6,7 +6,7 @@ module Eco::Language::Delegation
|
|
6
6
|
def included(base)
|
7
7
|
super
|
8
8
|
|
9
|
-
base.extend Eco::Language::Klass::
|
9
|
+
base.extend Eco::Language::Klass::InheritableClassVars
|
10
10
|
base.extend ClassMethods
|
11
11
|
base.inheritable_class_vars :_delegating_missing, :delegating_missing_to
|
12
12
|
end
|
@@ -6,7 +6,7 @@ module Eco::Language::Delegation
|
|
6
6
|
# the aim of scoping its class (so it can chain constant lookup), unless
|
7
7
|
# `delegated_class` is explicitly stated.
|
8
8
|
# @note that not setting `delegated_class` explicitly could fail
|
9
|
-
# to chain the
|
9
|
+
# to chain the lookup.
|
10
10
|
module DelegatingMissingConst
|
11
11
|
class << self
|
12
12
|
def included(base)
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module Eco::Language::Klass
|
2
|
+
# Helpers for dynamic object loading based on class declaration
|
3
|
+
# @note
|
4
|
+
# - this helpers aim to boost the usage of the ruby language
|
5
|
+
# in complex integration configurations.
|
6
|
+
module AutoLoader
|
7
|
+
include Resolver
|
8
|
+
include Hierarchy
|
9
|
+
|
10
|
+
# It loads/creates a new instance of children classes pending to be loaded.
|
11
|
+
# @note This method **should be explicitly called**.
|
12
|
+
# @return [Boolean] `true` if there were children loaded, `false` otherwise.
|
13
|
+
def autoload_children!(object)
|
14
|
+
return false if @loading_children
|
15
|
+
return false unless autoloaded_class
|
16
|
+
|
17
|
+
pending_children = unloaded_children
|
18
|
+
return false if pending_children.empty?
|
19
|
+
|
20
|
+
@loading_children = true
|
21
|
+
|
22
|
+
pending_children.each do |klass|
|
23
|
+
@child = klass.new(object)
|
24
|
+
rescue TypeError => _e
|
25
|
+
# Can't create from this class (must be the singleton class)
|
26
|
+
# Just ignore
|
27
|
+
ensure
|
28
|
+
autoloaded_children.push(klass)
|
29
|
+
end
|
30
|
+
|
31
|
+
true
|
32
|
+
ensure
|
33
|
+
@loading_children = false
|
34
|
+
end
|
35
|
+
|
36
|
+
# To enable the class autoloader, you should use this method
|
37
|
+
def autoloads_children_of(klass)
|
38
|
+
class_resolver :autoloader_class, klass
|
39
|
+
@autoloaded_class = klass
|
40
|
+
end
|
41
|
+
|
42
|
+
# Resolves the class `autoloader_class` if it has been defined via `autoloads_children_of`
|
43
|
+
def autoloaded_class
|
44
|
+
return unless @autoloaded_class
|
45
|
+
|
46
|
+
autoloader_class
|
47
|
+
end
|
48
|
+
|
49
|
+
# To which restricted namespaces this class autoloads from
|
50
|
+
def autoloaded_namespaces(type = :include)
|
51
|
+
@autoloaded_namespaces ||= {}
|
52
|
+
@autoloaded_namespaces[type] ||= []
|
53
|
+
end
|
54
|
+
|
55
|
+
# To restrict which namespaces it is allowed to load from
|
56
|
+
def autoload_namespace(*namespaces)
|
57
|
+
_autoload_namespace(:include, *namespaces)
|
58
|
+
end
|
59
|
+
|
60
|
+
# To ignore certain namespaces this class should not autoload from
|
61
|
+
def autoload_namespace_ignore(*namespaces)
|
62
|
+
_autoload_namespace(:ignore, *namespaces)
|
63
|
+
end
|
64
|
+
|
65
|
+
def _autoload_namespace(type, *namespaces)
|
66
|
+
autoloaded_namespaces(type).concat(namespaces) unless namespaces.empty?
|
67
|
+
end
|
68
|
+
|
69
|
+
# @param constant [Class, String] a class or namespace we want to check auto-load entitlement thereof.
|
70
|
+
# @return [Boolean] determines if a given namespace is entitled for autoloading
|
71
|
+
def autoload_class?(constant)
|
72
|
+
constants = constant.to_s.split("::").compact
|
73
|
+
autoload = true
|
74
|
+
|
75
|
+
unless autoloaded_namespaces(:include).empty?
|
76
|
+
autoload = autoloaded_namespaces(:include).any? do |ns|
|
77
|
+
ns.to_s.split("::").compact.zip(constants).all? {|(r, c)| r == c}
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
unless autoloaded_namespaces(:ignore).empty?
|
82
|
+
autoload &&= autoloaded_namespaces(:ignore).none? do |ns|
|
83
|
+
ns.to_s.split("::").compact.zip(constants).all? {|(r, c)| r == c}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
autoload
|
88
|
+
end
|
89
|
+
|
90
|
+
# As children are loaded as they are declared, we should not load twice same children.
|
91
|
+
def autoloaded_children
|
92
|
+
@auto_loaded_children ||= [] # rubocop:disable Naming/MemoizedInstanceVariableName
|
93
|
+
end
|
94
|
+
|
95
|
+
# Children classes of `autoloader_class` that have not been created an instance of.
|
96
|
+
def unloaded_children
|
97
|
+
return [] unless autoloaded_class
|
98
|
+
|
99
|
+
new_detected = new_classes
|
100
|
+
known_class!(*new_detected)
|
101
|
+
|
102
|
+
descendants(
|
103
|
+
parent_class: autoloaded_class,
|
104
|
+
scope: new_detected
|
105
|
+
).select do |child_class|
|
106
|
+
next false if autoloaded_children.include?(child_class)
|
107
|
+
next false unless autoload_class?(child_class)
|
108
|
+
|
109
|
+
true
|
110
|
+
end.sort
|
111
|
+
end
|
112
|
+
|
113
|
+
# Known namespaces serves the purpose to discover recently added namespaces
|
114
|
+
# provided that the namespace discovery is optimized
|
115
|
+
def known_classes
|
116
|
+
@known_classes ||= []
|
117
|
+
end
|
118
|
+
|
119
|
+
# Add to known namespaces
|
120
|
+
def known_class!(*classes)
|
121
|
+
known_classes.concat(classes)
|
122
|
+
end
|
123
|
+
|
124
|
+
# List all new namespaces
|
125
|
+
def new_classes
|
126
|
+
ObjectSpace.each_object(::Class).to_a - known_classes
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|