eco-helpers 2.7.16 → 2.7.17
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -4
- data/lib/eco/api/common/loaders/config/workflow/mailer.rb +10 -1
- data/lib/eco/api/microcases/people_cache.rb +3 -4
- data/lib/eco/api/microcases/people_load.rb +11 -8
- data/lib/eco/api/microcases/people_refresh.rb +8 -7
- data/lib/eco/api/microcases/people_search.rb +28 -22
- data/lib/eco/api/usecases/cli/dsl.rb +8 -6
- data/lib/eco/api/usecases/cli/option.rb +1 -0
- data/lib/eco/api/usecases/default/utils/cli/split_csv_cli.rb +5 -0
- data/lib/eco/api/usecases/default/utils/split_csv_case.rb +24 -4
- data/lib/eco/api/usecases/default_cases/to_csv_case.rb +36 -12
- data/lib/eco/cli/config/filters.rb +13 -3
- data/lib/eco/cli/config/help.rb +11 -9
- data/lib/eco/cli/config/input.rb +6 -6
- data/lib/eco/cli/config/options_set.rb +34 -21
- data/lib/eco/cli/config/use_cases.rb +22 -6
- data/lib/eco/cli/config.rb +19 -11
- data/lib/eco/cli/scripting/args_helpers.rb +40 -25
- data/lib/eco/cli/scripting/arguments.rb +24 -3
- data/lib/eco/cli.rb +0 -1
- data/lib/eco/cli_default/workflow.rb +3 -2
- data/lib/eco/csv/split.rb +23 -10
- data/lib/eco/csv/stream.rb +1 -0
- data/lib/eco/csv.rb +28 -6
- data/lib/eco/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 35f96561e2b58978c8949c744ac79c87888f067def10f63e00e3393e94cb992c
|
4
|
+
data.tar.gz: 7dba34b74428c53f7167eb1075b11bbd306432b3687c05986c66610da5b69f5e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35223b9e52a315cf78b65393b509675a5ab515976e36535906b1a1e4588ccab178272ce209ab964b4f0365f74adccae9536db778edc5b524fa0cc70b327f0ccf
|
7
|
+
data.tar.gz: 0b356c4f3ca79f4ae57cae1db37f97455199c0772a165f294577bf7cfe3b4f1249a3c6cbee34c8903cb2d637a3b4b28658a3897680d61b2494aeffdd026efb8f
|
data/CHANGELOG.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
4
4
|
|
5
|
-
## [2.7.
|
5
|
+
## [2.7.18] - 2024-06-xx
|
6
6
|
|
7
7
|
### Added
|
8
8
|
|
@@ -10,14 +10,36 @@ All notable changes to this project will be documented in this file.
|
|
10
10
|
|
11
11
|
### Fixed
|
12
12
|
|
13
|
-
-
|
14
|
-
|
15
|
-
## [2.7.15] - 2024-06-18
|
13
|
+
## [2.7.17] - 2024-06-22
|
16
14
|
|
17
15
|
### Added
|
18
16
|
|
17
|
+
- **Stop on uknown** options will try to offer suggestions from now on
|
18
|
+
- `Eco::CSV::count` class method to stream-count the number of rows
|
19
|
+
- Option `-start-at` allows to start the count at certain row idx
|
20
|
+
|
19
21
|
### Changed
|
20
22
|
|
23
|
+
- Added options to case `-split-csv`
|
24
|
+
- `-start-at`
|
25
|
+
- `-simulate` a dry-run will return the count and won't generate the files.
|
26
|
+
- `Eco::CSV::split`
|
27
|
+
- the `block` allows to filter what rows should be included
|
28
|
+
|
29
|
+
### Fixed
|
30
|
+
|
31
|
+
- `Workflow::Mailer`: shouldn't send notification when there is an **error** that doesn't really need to be notified.
|
32
|
+
- `to-csv` case should accepte target folder as an argument
|
33
|
+
- `options_set` sort namespaces
|
34
|
+
|
35
|
+
## [2.7.16] - 2024-06-18
|
36
|
+
|
37
|
+
### Fixed
|
38
|
+
|
39
|
+
- `RegisterUpdateCase` filters
|
40
|
+
|
41
|
+
## [2.7.15] - 2024-06-18
|
42
|
+
|
21
43
|
### Fixed
|
22
44
|
|
23
45
|
- `Eco::API::Common::People::EntryFactor` super must be called before
|
@@ -17,7 +17,10 @@ class Eco::API::Common::Loaders::Workflow::Mailer < Eco::API::Common::Loaders::W
|
|
17
17
|
next unless session.mailer?
|
18
18
|
next if session.config.dry_run?
|
19
19
|
next unless session.config.run_mode_remote?
|
20
|
-
|
20
|
+
|
21
|
+
# temporary contingency
|
22
|
+
maybe_error_pages_or_tree_updates = other_case?(io) && error?
|
23
|
+
next unless some_update?(io) || maybe_error_pages_or_tree_updates
|
21
24
|
|
22
25
|
subject = base_subject
|
23
26
|
|
@@ -53,6 +56,12 @@ class Eco::API::Common::Loaders::Workflow::Mailer < Eco::API::Common::Loaders::W
|
|
53
56
|
end
|
54
57
|
end
|
55
58
|
|
59
|
+
def other_case?(io)
|
60
|
+
cli.config.usecases.active(io: io).any? do |usecase, _data|
|
61
|
+
%i[other].any? { |type| usecase.type == type }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
56
65
|
def error?
|
57
66
|
!!error
|
58
67
|
end
|
@@ -7,18 +7,17 @@ module Eco
|
|
7
7
|
def people_cache(filename = enviro.config.people.cache)
|
8
8
|
logger.info("Going to get all the people via API")
|
9
9
|
|
10
|
-
start
|
11
|
-
people
|
10
|
+
start = Time.now
|
11
|
+
people = session.batch.get_people
|
12
12
|
secs = (Time.now - start).round(3)
|
13
13
|
cnt = people.count
|
14
14
|
per_sec = (cnt.to_f / secs).round(2)
|
15
15
|
logger.info("Loaded #{cnt} people in #{secs} seconds (#{per_sec} people/sec)")
|
16
16
|
|
17
|
-
file
|
17
|
+
file = file_manager.save_json(people, filename, :timestamp)
|
18
18
|
logger.info("#{people.length} people loaded and saved locally to #{file}.")
|
19
19
|
Eco::API::Organization::People.new(people)
|
20
20
|
end
|
21
|
-
|
22
21
|
end
|
23
22
|
end
|
24
23
|
end
|
@@ -15,19 +15,23 @@ module Eco
|
|
15
15
|
# - `:file` if it is supposed to load people from a file.
|
16
16
|
# - `:save` if it is supposed to cache/save the data locally once obtained people from the server (`:api`)
|
17
17
|
# @return [Eco::API::Organization::People] the `People` object with the data.
|
18
|
-
def people_load(filename = enviro.config.people.cache, modifier: [
|
18
|
+
def people_load(filename = enviro.config.people.cache, modifier: %i[newest api]) # rubocop:disable Metrics/AbcSize
|
19
19
|
modifier = [modifier].flatten
|
20
|
-
load_file = [
|
20
|
+
load_file = %i[file newest].any? {|flag| modifier.include?(flag)}
|
21
|
+
|
21
22
|
case
|
22
23
|
when filename && load_file
|
23
|
-
|
24
|
+
file = people_load_filename(filename, newest: modifier.include?(:newest))
|
25
|
+
|
26
|
+
if file
|
24
27
|
file_manager.load_json(file).tap do |people|
|
25
28
|
logger.info("#{people&.length} people loaded from file #{file}") if people.is_a?(Array)
|
26
29
|
end
|
27
30
|
else
|
28
31
|
logger.error("could not find the file #{file_manager.dir.file(filename)}")
|
29
32
|
exit unless modifier.include?(:api)
|
30
|
-
|
33
|
+
|
34
|
+
people_load(modifier: modifier - %i[newest file])
|
31
35
|
end
|
32
36
|
when modifier.include?(:api)
|
33
37
|
logger.info("Going to get all the people via API (load)")
|
@@ -39,12 +43,12 @@ module Eco
|
|
39
43
|
per_sec = (cnt.to_f / secs).round(2)
|
40
44
|
logger.info("Loaded #{cnt} people in #{secs} seconds (#{per_sec} people/sec)")
|
41
45
|
|
42
|
-
if modifier.include?(:save) && people && people.length
|
46
|
+
if modifier.include?(:save) && people && people.length.positive?
|
43
47
|
file = file_manager.save_json(people, filename, :timestamp)
|
44
|
-
logger.info("#{people.length
|
48
|
+
logger.info("#{people.length} people saved to file #{file}.")
|
45
49
|
end
|
46
50
|
end
|
47
|
-
end.
|
51
|
+
end.then do |people|
|
48
52
|
Eco::API::Organization::People.new(people)
|
49
53
|
end
|
50
54
|
end
|
@@ -61,7 +65,6 @@ module Eco
|
|
61
65
|
file_manager.dir.file(filename, should_exist: true)
|
62
66
|
end
|
63
67
|
end
|
64
|
-
|
65
68
|
end
|
66
69
|
end
|
67
70
|
end
|
@@ -9,24 +9,26 @@ module Eco
|
|
9
9
|
# @param people [Eco::API::Organization::People] the people that needs refresh.
|
10
10
|
# @param include_created [Boolean] include people created during this session? (will check `:create` batch jobs).
|
11
11
|
# @return [Eco::API::Organization::People] the `People` object with the data.
|
12
|
-
def people_refresh(people:, include_created: true)
|
12
|
+
def people_refresh(people:, include_created: true) # rubocop:disable Metrics/AbcSize
|
13
13
|
people = people.newFrom people.select do |person|
|
14
14
|
!person.new? || !person.dirty?
|
15
15
|
end
|
16
|
+
|
16
17
|
ini = people.length
|
18
|
+
|
17
19
|
if include_created
|
18
20
|
session.job_groups.find_jobs(type: :create).map do |job|
|
19
|
-
to_add = job.people.
|
21
|
+
to_add = job.people.reject(&:dirty?)
|
20
22
|
people = people.merge(to_add)
|
21
23
|
end
|
22
24
|
end
|
23
25
|
|
24
26
|
created = people.length - ini
|
25
|
-
msg
|
26
|
-
msg
|
27
|
+
msg = "Going to refresh #{people.length} people with server data"
|
28
|
+
msg += " (including #{created} that were created)" if created.positive?
|
27
29
|
logger.info(msg)
|
28
30
|
|
29
|
-
start
|
31
|
+
start = Time.now
|
30
32
|
entries = session.batch.get_people(people, silent: true)
|
31
33
|
secs = (Time.now - start).round(3)
|
32
34
|
cnt = entries.count
|
@@ -34,11 +36,10 @@ module Eco
|
|
34
36
|
logger.info("Re-loaded #{cnt} people (out of #{people.length}) in #{secs} seconds (#{per_sec} people/sec)")
|
35
37
|
|
36
38
|
missing = people.length - entries.length
|
37
|
-
logger.error("Missed to obtain #{missing} people during the refresh") if missing
|
39
|
+
logger.error("Missed to obtain #{missing} people during the refresh") if missing.positive?
|
38
40
|
|
39
41
|
Eco::API::Organization::People.new(entries)
|
40
42
|
end
|
41
|
-
|
42
43
|
end
|
43
44
|
end
|
44
45
|
end
|
@@ -5,52 +5,57 @@ module Eco
|
|
5
5
|
# @note
|
6
6
|
# - this helper is normally used to **get partial** part of the people manager.
|
7
7
|
# - therefore, normally used with _**delta** input files_ (files with only the differences).
|
8
|
-
# @param data [Eco::API::Organization::People, Enumerable<Person>, Enumerable<Hash>]
|
8
|
+
# @param data [Eco::API::Organization::People, Enumerable<Person>, Enumerable<Hash>]
|
9
|
+
# `People` to search against the server.
|
9
10
|
# @param options [Hash] the options.
|
10
11
|
# @param silent [Boolean] `false` if low level search messages should be shown.
|
11
12
|
# @return [Eco::API::Organization::People] the `People` object with the found persons.
|
12
|
-
def people_search(data, options: {}, silent: true)
|
13
|
+
def people_search(data, options: {}, silent: true) # rubocop:disable Metrics/AbcSize
|
13
14
|
session.logger.info("Going to api get #{data.length} entries...")
|
14
15
|
|
15
16
|
start = Time.now
|
16
|
-
people = session.batch.search(data, silent: silent).
|
17
|
-
secs
|
18
|
-
Eco::API::Organization::People.new(status.people).tap do |people|
|
17
|
+
people = session.batch.search(data, silent: silent).then do |status|
|
18
|
+
secs = (Time.now - start).round(3)
|
19
|
+
Eco::API::Organization::People.new(status.people).tap do |people| # rubocop:disable Lint/ShadowingOuterLocalVariable
|
19
20
|
cnt = people.count
|
20
21
|
per_sec = (cnt.to_f / secs).round(2)
|
21
|
-
msg = "... could get #{cnt} people
|
22
|
+
msg = "... could get #{cnt} people "
|
23
|
+
msg << "(out of #{data.length} entries) in #{secs} seconds (#{per_sec} people/sec)"
|
22
24
|
session.logger.info(msg)
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
26
28
|
# get the supervisors of found people (current supervisors)
|
27
29
|
supers = people_search_prepare_supers_request(people)
|
28
|
-
if supers.length
|
30
|
+
if supers.length.positive?
|
29
31
|
session.logger.info(" Going to api get #{supers.length} current supervisors...")
|
30
32
|
start = Time.now
|
31
|
-
people = session.batch.search(supers, silent: silent).
|
33
|
+
people = session.batch.search(supers, silent: silent).then do |status|
|
32
34
|
secs = (Time.now - start).round(3)
|
33
35
|
found = status.people
|
34
36
|
cnt = found.count
|
35
37
|
per_sec = (cnt.to_f / secs).round(2)
|
36
|
-
msg = "... could find #{cnt} current supers
|
38
|
+
msg = "... could find #{cnt} current supers "
|
39
|
+
msg << "(out of #{supers.length}) in #{secs} seconds (#{per_sec} people/sec)"
|
37
40
|
session.logger.info(msg)
|
41
|
+
|
38
42
|
people.merge(found, strict: micro.strict_search?(options))
|
39
43
|
end
|
40
44
|
end
|
41
45
|
|
42
46
|
# get the supervisors referred in the input data (future supervisors)
|
43
47
|
supers = people_search_prepare_supers_request(data, people)
|
44
|
-
if supers.length
|
48
|
+
if supers.length.positive?
|
45
49
|
session.logger.info(" Going to api get #{supers.length} supervisors as per input entries...")
|
46
50
|
start = Time.now
|
47
51
|
|
48
|
-
people = session.batch.search(supers, silent: silent).
|
52
|
+
people = session.batch.search(supers, silent: silent).then do |status|
|
49
53
|
secs = (Time.now - start).round(3)
|
50
54
|
found = status.people
|
51
55
|
cnt = found.count
|
52
56
|
per_sec = (cnt.to_f / secs).round(2)
|
53
|
-
msg = "... could find #{cnt} input supers
|
57
|
+
msg = "... could find #{cnt} input supers "
|
58
|
+
msg << "(out of #{supers.length}) in #{secs} seconds (#{per_sec} people/sec)"
|
54
59
|
session.logger.info(msg)
|
55
60
|
people.merge(found, strict: micro.strict_search?(options))
|
56
61
|
end
|
@@ -66,25 +71,26 @@ module Eco
|
|
66
71
|
def people_search_prepare_supers_request(data, people = data)
|
67
72
|
data.each_with_object([]) do |entry, request|
|
68
73
|
spr = {"id" => (sup_id = people_search_super_id(entry))}
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
74
|
+
next if !sup_id || request.include?(spr)
|
75
|
+
|
76
|
+
micro.with_supervisor(sup_id, people) do |supervisor|
|
77
|
+
request.push(spr) unless supervisor
|
73
78
|
end
|
74
79
|
end
|
75
80
|
end
|
76
81
|
|
77
82
|
# Gets the `supervisor_id` from `value`
|
78
83
|
def people_search_super_id(value)
|
79
|
-
sup_id =
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
+
sup_id =
|
85
|
+
if value.respond_to?(:supervisor_id)
|
86
|
+
value.supervisor_id
|
87
|
+
elsif value.is_a?(Hash) && value.key("supervisor_id")
|
88
|
+
value["supervisor_id"]
|
89
|
+
end
|
90
|
+
|
84
91
|
sup_id = nil if sup_id.to_s.strip.empty?
|
85
92
|
sup_id
|
86
93
|
end
|
87
|
-
|
88
94
|
end
|
89
95
|
end
|
90
96
|
end
|
@@ -7,9 +7,10 @@ module Eco
|
|
7
7
|
def apply!(arg_case = cli_name)
|
8
8
|
#puts "DEFINING CLI for '#{arg_case}' via #{self}"
|
9
9
|
if applied?(arg_case)
|
10
|
-
puts
|
10
|
+
puts "Warning: (#{self}) Tried to call again cli.apply! on '#{arg_case}'"
|
11
11
|
return self
|
12
12
|
end
|
13
|
+
|
13
14
|
cli_config_case(arg_case)
|
14
15
|
apply_options(arg_case)
|
15
16
|
applied!(arg_case)
|
@@ -29,10 +30,11 @@ module Eco
|
|
29
30
|
end
|
30
31
|
|
31
32
|
attr_writer :usecase
|
33
|
+
|
32
34
|
# Unless specified, assume Cli class hangs from its case namespace
|
33
35
|
def usecase
|
34
|
-
raise "#{self} is to use to extend a class" unless
|
35
|
-
@usecase ||= Kernel.const_get(
|
36
|
+
raise "#{self} is to use to extend a class" unless is_a?(Class)
|
37
|
+
@usecase ||= Kernel.const_get(to_s.split('::')[0..-2].join('::'))
|
36
38
|
end
|
37
39
|
|
38
40
|
def description(value = nil)
|
@@ -50,7 +52,7 @@ module Eco
|
|
50
52
|
|
51
53
|
# It defaults to the use case preceded by dash
|
52
54
|
def cli_name(arg_name = nil)
|
53
|
-
@cli_name = (arg_name.nil? ? @cli_name : arg_name).
|
55
|
+
@cli_name = (arg_name.nil? ? @cli_name : arg_name).then do |value|
|
54
56
|
value = "-#{name}" if value.nil?
|
55
57
|
value
|
56
58
|
end
|
@@ -66,7 +68,7 @@ module Eco
|
|
66
68
|
end
|
67
69
|
|
68
70
|
def add_option(arg, desc = nil, &block)
|
69
|
-
|
71
|
+
tap do
|
70
72
|
"Overriding option '#{arg}' on case '#{name}'" if options.key?(arg)
|
71
73
|
@options[arg] = Eco::API::UseCases::Cli::Option.new(arg, desc, &block)
|
72
74
|
end
|
@@ -75,7 +77,7 @@ module Eco
|
|
75
77
|
private
|
76
78
|
|
77
79
|
def apply_options(arg_case)
|
78
|
-
options.
|
80
|
+
options.each_value do |option|
|
79
81
|
option.link_case(cli_config_case(arg_case))
|
80
82
|
end
|
81
83
|
end
|
@@ -13,6 +13,7 @@ class Eco::API::UseCases::Cli
|
|
13
13
|
|
14
14
|
def link_case(cli_config_case)
|
15
15
|
raise ArgumentError, "cli_config_case must have an 'add_option' method. Given: #{cli_config_case.class}" unless cli_config_case.respond_to?(:add_option)
|
16
|
+
|
16
17
|
cli_config_case.add_option(name, desc, &callback)
|
17
18
|
end
|
18
19
|
end
|
@@ -11,5 +11,10 @@ class Eco::API::UseCases::Default::People::Utils::SplitCsv
|
|
11
11
|
count = SCR.get_arg("-max-rows", with_param: true)
|
12
12
|
options.deep_merge!(output: {file: {max_rows: count}})
|
13
13
|
end
|
14
|
+
|
15
|
+
add_option("-start-at", "Get only the last N-start_at rows") do |options|
|
16
|
+
count = SCR.get_arg("-start-at", with_param: true)
|
17
|
+
options.deep_merge!(output: {file: {start_at: count}})
|
18
|
+
end
|
14
19
|
end
|
15
20
|
end
|
@@ -7,15 +7,27 @@ class Eco::API::UseCases::Default::People::Utils::SplitCsv < Eco::API::Common::L
|
|
7
7
|
type :other
|
8
8
|
|
9
9
|
def main(*_args)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
if simulate?
|
11
|
+
count = Eco::CSV.count(input_file, start_at: start_at)
|
12
|
+
log(:info) { "CSV '#{input_file}' has #{count} rows." }
|
13
|
+
else
|
14
|
+
Eco::CSV.split(
|
15
|
+
input_file,
|
16
|
+
max_rows: max_rows,
|
17
|
+
start_at: start_at,
|
18
|
+
&filter
|
19
|
+
).each do |file|
|
20
|
+
log(:info) { "Generated file '#{file}'" }
|
21
|
+
end
|
14
22
|
end
|
15
23
|
end
|
16
24
|
|
17
25
|
private
|
18
26
|
|
27
|
+
def filter
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
19
31
|
def input_file
|
20
32
|
options.dig(:source, :file)
|
21
33
|
end
|
@@ -31,4 +43,12 @@ class Eco::API::UseCases::Default::People::Utils::SplitCsv < Eco::API::Common::L
|
|
31
43
|
num = nil if num.zero?
|
32
44
|
num
|
33
45
|
end
|
46
|
+
|
47
|
+
def start_at
|
48
|
+
return nil unless (num = options.dig(:output, :file, :start_at))
|
49
|
+
|
50
|
+
num = num.to_i
|
51
|
+
num = nil if num.zero?
|
52
|
+
num
|
53
|
+
end
|
34
54
|
end
|
@@ -2,6 +2,8 @@ class Eco::API::UseCases::DefaultCases::ToCsvCase < Eco::API::Common::Loaders::U
|
|
2
2
|
name "to-csv"
|
3
3
|
type :export
|
4
4
|
|
5
|
+
OUT_FILENAME = 'pm'
|
6
|
+
|
5
7
|
attr_reader :people
|
6
8
|
|
7
9
|
def main(people, _session, options, _usecase)
|
@@ -16,8 +18,9 @@ class Eco::API::UseCases::DefaultCases::ToCsvCase < Eco::API::Common::Loaders::U
|
|
16
18
|
if options.dig(:export, :options, :split_schemas)
|
17
19
|
by_schema.each do |id, people|
|
18
20
|
sch_name = schemas.to_name(id)
|
19
|
-
prefix
|
20
|
-
|
21
|
+
prefix = sch_name ? sch_name.gsub(" ", "_").downcase : "no_schema"
|
22
|
+
filename = in_folder("#{prefix}_#{File.basename(file)}")
|
23
|
+
create_file!(filename, people)
|
21
24
|
end
|
22
25
|
else
|
23
26
|
create_file!(file, people)
|
@@ -61,15 +64,13 @@ class Eco::API::UseCases::DefaultCases::ToCsvCase < Eco::API::Common::Loaders::U
|
|
61
64
|
end
|
62
65
|
|
63
66
|
def nice_header_names(header, schema: nil)
|
64
|
-
schema
|
65
|
-
name_maps = schema.fields_by_alt_id.
|
66
|
-
mappings[alt_id] = fld.name
|
67
|
-
end.merge(nice_header_maps)
|
67
|
+
schema ||= session.schema
|
68
|
+
name_maps = schema.fields_by_alt_id.transform_values(&:name).merge(nice_header_maps)
|
68
69
|
header.map {|name| name_maps[name] || name}
|
69
70
|
end
|
70
71
|
|
71
72
|
def to_entry_type(person)
|
72
|
-
session.new_entry(person, dependencies: deps).
|
73
|
+
session.new_entry(person, dependencies: deps).then do |person_entry|
|
73
74
|
options.dig(:export, :options, :internal_names) ? person_entry.mapped_entry : person_entry.external_entry
|
74
75
|
end
|
75
76
|
end
|
@@ -79,14 +80,37 @@ class Eco::API::UseCases::DefaultCases::ToCsvCase < Eco::API::Common::Loaders::U
|
|
79
80
|
end
|
80
81
|
|
81
82
|
def file
|
82
|
-
@file ||=
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
83
|
+
@file ||= out_filename.tap do |filename|
|
84
|
+
next if filename
|
85
|
+
|
86
|
+
log(:error) { "Destination file not specified" }
|
87
|
+
return false
|
87
88
|
end
|
88
89
|
end
|
89
90
|
|
91
|
+
def out_filename
|
92
|
+
return options_file unless options_folder?
|
93
|
+
|
94
|
+
File.join(options_file, "#{config.active_enviro}_#{OUT_FILENAME}.csv")
|
95
|
+
end
|
96
|
+
|
97
|
+
def in_folder(filename)
|
98
|
+
basename = File.basename(filename)
|
99
|
+
return basename unless options_folder?
|
100
|
+
|
101
|
+
File.join(options_file, basename)
|
102
|
+
end
|
103
|
+
|
104
|
+
def options_folder?
|
105
|
+
return false unless (value = options_file)
|
106
|
+
|
107
|
+
File.directory?(value)
|
108
|
+
end
|
109
|
+
|
110
|
+
def options_file
|
111
|
+
options[:file] || options.dig(:export, :file, :name)
|
112
|
+
end
|
113
|
+
|
90
114
|
def by_schema
|
91
115
|
people.group_by do |person|
|
92
116
|
if (details = person.details)
|
@@ -15,7 +15,8 @@ module Eco
|
|
15
15
|
def help(msg = nil, refine: nil)
|
16
16
|
refinement = refine.is_a?(String)? " (containing: '#{refine}')" : ""
|
17
17
|
msg ||= "The following are the available filters#{refinement}:"
|
18
|
-
|
18
|
+
|
19
|
+
[msg].then do |lines|
|
19
20
|
max_len = keys_max_len(@filters.keys)
|
20
21
|
@filters.keys.sort.select do |key|
|
21
22
|
!refine.is_a?(String) || key.include?(refine)
|
@@ -26,10 +27,19 @@ module Eco
|
|
26
27
|
end.join("\n")
|
27
28
|
end
|
28
29
|
|
30
|
+
def available(keys: false)
|
31
|
+
return @filters.keys if keys
|
32
|
+
|
33
|
+
@filters.keys.map do |key|
|
34
|
+
[key, @filters[key], @description[key]]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
29
38
|
# @param option [String] the command line option that activates this filter.
|
30
39
|
# @param desc [String] description of the filter.
|
31
40
|
def add(option, desc = nil, &block)
|
32
|
-
raise "Missing block to define the filters builder" unless block_given?
|
41
|
+
raise ArgumentError, "Missing block to define the filters builder" unless block_given?
|
42
|
+
|
33
43
|
callback = block
|
34
44
|
[option].flatten.compact.each do |opt|
|
35
45
|
@filters[opt] = callback
|
@@ -38,7 +48,7 @@ module Eco
|
|
38
48
|
self
|
39
49
|
end
|
40
50
|
|
41
|
-
def process(
|
51
|
+
def process(*)
|
42
52
|
raise "You need to override this method in child classes"
|
43
53
|
end
|
44
54
|
end
|
data/lib/eco/cli/config/help.rb
CHANGED
@@ -2,8 +2,7 @@ module Eco
|
|
2
2
|
class CLI
|
3
3
|
class Config
|
4
4
|
module Help
|
5
|
-
|
6
|
-
def help(*args)
|
5
|
+
def help(*_args)
|
7
6
|
raise "this needs to be reimplemented in the child class and return a string"
|
8
7
|
end
|
9
8
|
|
@@ -16,11 +15,12 @@ module Eco
|
|
16
15
|
# Creatas a well aligned line
|
17
16
|
def help_line(key, desc, keys_max_len = key.length, line_len = 100)
|
18
17
|
blanks = keys_max_len + 3 - key.length
|
19
|
-
blanks = blanks
|
18
|
+
blanks = [blanks, 0].max
|
20
19
|
top_line = " #{key}#{" "*blanks} "
|
21
20
|
indent = top_line.length
|
22
21
|
first = true
|
23
|
-
|
22
|
+
|
23
|
+
each_slice_words(desc, line_len - indent).each_with_object([]) do |line, lines| # rubocop:disable Style/RedundantEach
|
24
24
|
lines << (first ? "#{top_line}#{line}" : "#{" " * indent}#{line}")
|
25
25
|
first = false
|
26
26
|
end.join("\n")
|
@@ -33,14 +33,16 @@ module Eco
|
|
33
33
|
liner << part
|
34
34
|
else
|
35
35
|
yield(liner) if block_given?
|
36
|
-
|
36
|
+
|
37
|
+
out << liner
|
37
38
|
liner = part.strip
|
38
39
|
end
|
39
40
|
end.tap do |out|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
next unless out.empty? || !liner.empty?
|
42
|
+
|
43
|
+
yield(liner) if block_given?
|
44
|
+
|
45
|
+
out << liner if liner
|
44
46
|
end
|
45
47
|
end
|
46
48
|
end
|
data/lib/eco/cli/config/input.rb
CHANGED
@@ -2,14 +2,13 @@ module Eco
|
|
2
2
|
class CLI
|
3
3
|
class Config
|
4
4
|
class Input
|
5
|
-
|
6
5
|
attr_reader :core_config
|
7
6
|
attr_reader :default_option
|
8
7
|
|
9
8
|
def initialize(core_config:, default_option: nil)
|
10
9
|
@core_config = core_config
|
11
10
|
@default_option = default_option
|
12
|
-
@callbacks
|
11
|
+
@callbacks = {}
|
13
12
|
end
|
14
13
|
|
15
14
|
def default_option?
|
@@ -20,16 +19,17 @@ module Eco
|
|
20
19
|
option ||= default_option
|
21
20
|
raise "Missing option to identify the input (no default defined)" unless !!option
|
22
21
|
raise "Missing block to define the input obtention process" unless block
|
22
|
+
|
23
23
|
@callbacks[option] = block
|
24
24
|
end
|
25
25
|
|
26
26
|
def get(io:, option: nil)
|
27
|
-
|
28
|
-
|
29
|
-
end
|
27
|
+
msg = "You need to provide Eco::API::UseCases::BaseIO object. Given: #{io.class}"
|
28
|
+
raise ArgumentError, msg unless io.is_a?(Eco::API::UseCases::BaseIO)
|
30
29
|
|
31
30
|
option ||= default_option
|
32
|
-
raise "Missing option to identify the input (no default defined)" unless
|
31
|
+
raise "Missing option to identify the input (no default defined)" unless option
|
32
|
+
|
33
33
|
callback = @callbacks[option] || @callbacks[default_option]
|
34
34
|
callback.call(io.session, option, io.options)
|
35
35
|
end
|
@@ -3,10 +3,10 @@ module Eco
|
|
3
3
|
class Config
|
4
4
|
class OptionsSet
|
5
5
|
include Eco::CLI::Config::Help
|
6
|
+
|
6
7
|
attr_reader :core_config
|
7
8
|
|
8
|
-
|
9
|
-
end
|
9
|
+
OptConfig = Struct.new(:name, :namespace, :description, :callback)
|
10
10
|
|
11
11
|
def initialize(core_config:)
|
12
12
|
@core_config = core_config
|
@@ -15,19 +15,21 @@ module Eco
|
|
15
15
|
|
16
16
|
# @return [String] summary of the options.
|
17
17
|
def help(refine: nil)
|
18
|
-
indent
|
19
|
-
spaces
|
18
|
+
indent = 2
|
19
|
+
spaces = any_non_general_space_active? ? active_namespaces : namespaces
|
20
20
|
refinement = refine.is_a?(String)? " (containing: '#{refine}')" : ""
|
21
|
-
|
21
|
+
|
22
|
+
["The following are the available options#{refinement}:"].then do |lines|
|
22
23
|
max_len = keys_max_len(options_args(spaces)) + indent
|
23
24
|
spaces.each do |namespace|
|
24
25
|
is_general = (namespace == :general)
|
25
26
|
str_indent = is_general ? "" : " " * indent
|
26
27
|
lines << help_line(namespace, "", max_len) unless is_general
|
27
|
-
|
28
|
+
|
29
|
+
options_set(namespace).select do |_arg, option| # rubocop:disable Style/HashEachMethods
|
28
30
|
!refine.is_a?(String) || option.name.include?(refine)
|
29
|
-
end.each do |
|
30
|
-
lines << help_line("
|
31
|
+
end.each do |_arg, option|
|
32
|
+
lines << help_line("#{str_indent}#{option.name}", option.description, max_len)
|
31
33
|
end
|
32
34
|
end
|
33
35
|
lines
|
@@ -41,6 +43,19 @@ module Eco
|
|
41
43
|
end.uniq
|
42
44
|
end
|
43
45
|
|
46
|
+
# Options that are known for active namespaces (CLI-present)
|
47
|
+
def available(keys: false)
|
48
|
+
sets.select do |namespace, _opts_set|
|
49
|
+
active_namespace?(namespace)
|
50
|
+
end.each_value.with_object([]) do |opts_set, options|
|
51
|
+
options.concat(opts_set.values)
|
52
|
+
end.then do |options|
|
53
|
+
next options unless keys
|
54
|
+
|
55
|
+
options.map(&:name)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
44
59
|
# @param option [String, Array<String>] the command line option(s).
|
45
60
|
# @param namespace [String] preceding command(s) argument that enables this option.
|
46
61
|
# @param desc [String] description of the option.
|
@@ -51,17 +66,18 @@ module Eco
|
|
51
66
|
unless opts.empty?
|
52
67
|
callback = block
|
53
68
|
opts.each do |opt|
|
54
|
-
|
69
|
+
msg = "Overriding CLI option '#{option}' in '#{namespace}' CLI case / namespace"
|
70
|
+
puts msg if option_exists?(opt, namespace)
|
55
71
|
options_set(namespace)[opt] = OptConfig.new(opt, namespace, desc, callback)
|
56
72
|
end
|
57
73
|
end
|
74
|
+
|
58
75
|
self
|
59
76
|
end
|
60
77
|
|
61
78
|
def process(io:)
|
62
|
-
|
63
|
-
|
64
|
-
end
|
79
|
+
msg = "You need to provide Eco::API::UseCases::BaseIO object. Given: #{io.class}"
|
80
|
+
raise ArgumentError, msg unless io.is_a?(Eco::API::UseCases::BaseIO)
|
65
81
|
|
66
82
|
active_options.each do |option|
|
67
83
|
option.callback.call(io.options, io.session)
|
@@ -70,8 +86,9 @@ module Eco
|
|
70
86
|
io.options
|
71
87
|
end
|
72
88
|
|
89
|
+
# Options that have been invoked via CLI
|
73
90
|
def active_options
|
74
|
-
@active_options ||= sets.select do |namespace,
|
91
|
+
@active_options ||= sets.select do |namespace, _opts_set|
|
75
92
|
active_namespace?(namespace)
|
76
93
|
end.each_with_object([]) do |(namespace, opts_set), options|
|
77
94
|
opts_set.each do |arg, option|
|
@@ -81,19 +98,20 @@ module Eco
|
|
81
98
|
end
|
82
99
|
|
83
100
|
def all_options
|
84
|
-
sets.each_with_object([]) do |(
|
101
|
+
sets.each_with_object([]) do |(_namespace, opts_set), options|
|
85
102
|
options << opts_set.values
|
86
103
|
end
|
87
104
|
end
|
88
105
|
|
89
106
|
def namespaces
|
90
107
|
sets.keys.sort_by do |key|
|
91
|
-
key == :general
|
108
|
+
next 1 if key == :general
|
109
|
+
0
|
92
110
|
end
|
93
111
|
end
|
94
112
|
|
95
113
|
def any_non_general_space_active?
|
96
|
-
(active_namespaces - [:general]).length
|
114
|
+
(active_namespaces - [:general]).length.positive?
|
97
115
|
end
|
98
116
|
|
99
117
|
def active_namespaces
|
@@ -104,7 +122,6 @@ module Eco
|
|
104
122
|
end
|
105
123
|
end
|
106
124
|
|
107
|
-
|
108
125
|
private
|
109
126
|
|
110
127
|
def active_namespace?(namespace)
|
@@ -132,10 +149,6 @@ module Eco
|
|
132
149
|
}
|
133
150
|
end
|
134
151
|
|
135
|
-
def namespaces
|
136
|
-
@sets.keys
|
137
|
-
end
|
138
|
-
|
139
152
|
def options_set(namespace = :general)
|
140
153
|
@sets[namespace] ||= {}
|
141
154
|
end
|
@@ -39,6 +39,15 @@ module Eco
|
|
39
39
|
end.join("\n")
|
40
40
|
end
|
41
41
|
|
42
|
+
# The linked cases
|
43
|
+
def available(keys: false)
|
44
|
+
@linked_cases.values.then do |cases|
|
45
|
+
next cases unless keys
|
46
|
+
|
47
|
+
cases.map(&:option)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
42
51
|
# Integrates a usecase to the command line.
|
43
52
|
# @param option_case [String] the command line option to invoke the usecase.
|
44
53
|
# @param type [Symbol] the type of usecase.
|
@@ -46,10 +55,12 @@ module Eco
|
|
46
55
|
# @param case_name [String, nil] the name of the usecase as defined.
|
47
56
|
def add(option_case, type, desc = nil, case_name: nil, &callback)
|
48
57
|
Eco::API::UseCases::UseCase.validate_type(type)
|
58
|
+
|
49
59
|
unless block_given?
|
50
60
|
raise "You must specify a valid 'case_name' when no block is provided" unless case_name
|
51
61
|
raise "'case_name' expected to be a String. Given: #{case_name.class}" unless case_name.is_a?(String)
|
52
62
|
end
|
63
|
+
|
53
64
|
puts "Overriding CLI case '#{option_case}'" if @linked_cases.key?(option_case)
|
54
65
|
@linked_cases[option_case] = CaseConfig.new(self, option_case, type, desc, case_name, callback)
|
55
66
|
end
|
@@ -60,6 +71,7 @@ module Eco
|
|
60
71
|
io.session.usecases.each do |usecase|
|
61
72
|
next unless usecase.respond_to?(:classed_definition)
|
62
73
|
next unless (original_case = usecase.classed_definition)
|
74
|
+
|
63
75
|
original_case.cli_apply!
|
64
76
|
end
|
65
77
|
end
|
@@ -84,9 +96,12 @@ module Eco
|
|
84
96
|
|
85
97
|
def process(io:)
|
86
98
|
validate_io!(io)
|
99
|
+
|
87
100
|
processed = false
|
101
|
+
|
88
102
|
active(io: io).each do |usecase, data|
|
89
103
|
raise "Something went wrong when scoping active cases" unless data
|
104
|
+
|
90
105
|
processed = true
|
91
106
|
io = case_io(io: io, usecase: usecase)
|
92
107
|
# some usecases have a callback to collect the parameters
|
@@ -101,6 +116,7 @@ module Eco
|
|
101
116
|
# Gets a `UseCaseIO`
|
102
117
|
def case_io(io:, usecase:)
|
103
118
|
validate_io!(io)
|
119
|
+
|
104
120
|
case io
|
105
121
|
when Eco::API::UseCases::UseCaseIO
|
106
122
|
io.chain(usecase: usecase)
|
@@ -122,12 +138,12 @@ module Eco
|
|
122
138
|
params = io.params(keyed: true).merge(type: data.type)
|
123
139
|
io = io.new(**params, validate: false)
|
124
140
|
callback.call(*io.params).tap do |use|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
141
|
+
next if use.is_a?(Eco::API::UseCases::UseCase)
|
142
|
+
|
143
|
+
msg = "When adding a usecase, without specifying 'case_name:', "
|
144
|
+
msg << "the block that integrates usecase for cli option '#{data.option}'"
|
145
|
+
msg << "must return an Eco::API::UseCases::UseCase object. It returns #{use.class}"
|
146
|
+
raise msg
|
131
147
|
end
|
132
148
|
end
|
133
149
|
end
|
data/lib/eco/cli/config.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
module Eco
|
2
2
|
class CLI
|
3
3
|
class Config
|
4
|
-
|
5
4
|
attr_reader :cli
|
6
5
|
|
7
6
|
def initialize(cli:)
|
@@ -16,6 +15,16 @@ module Eco
|
|
16
15
|
cli.args
|
17
16
|
end
|
18
17
|
|
18
|
+
# All the available known option args that can be used in the CLI
|
19
|
+
def available_option_args
|
20
|
+
[
|
21
|
+
usecases.available(keys: true),
|
22
|
+
options_set.available(keys: true),
|
23
|
+
people_filters.available(keys: true),
|
24
|
+
input_filters.available(keys: true)
|
25
|
+
].flatten.uniq
|
26
|
+
end
|
27
|
+
|
19
28
|
def options_set
|
20
29
|
@opions_set ||= Eco::CLI::Config::OptionsSet.new(core_config: self)
|
21
30
|
@opions_set.tap do |opts_set|
|
@@ -25,12 +34,10 @@ module Eco
|
|
25
34
|
|
26
35
|
def input(default_option: nil, &block)
|
27
36
|
@input ||= Eco::CLI::Config::Input.new(core_config: self, default_option: default_option)
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
@input
|
33
|
-
end
|
37
|
+
return @input unless block_given?
|
38
|
+
|
39
|
+
@input.define(&block)
|
40
|
+
self
|
34
41
|
end
|
35
42
|
|
36
43
|
def people(io: nil, &block)
|
@@ -38,10 +45,11 @@ module Eco
|
|
38
45
|
@people_load = block
|
39
46
|
self
|
40
47
|
else
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
48
|
+
msg = "There is no definition on how to load people"
|
49
|
+
raise msg unless instance_variable_defined?(:@people_load) && @people_load
|
50
|
+
|
51
|
+
msg = "You need to provide Eco::API::UseCases::BaseIO object. Given: #{io.class}"
|
52
|
+
raise msg unless io.is_a?(Eco::API::UseCases::BaseIO)
|
45
53
|
|
46
54
|
io = io.new(type: :import, input: io.input || [])
|
47
55
|
@people_load.call(*io.params)
|
@@ -7,7 +7,7 @@ module Eco
|
|
7
7
|
@argv || ARGV
|
8
8
|
end
|
9
9
|
|
10
|
-
def is_modifier?(value)
|
10
|
+
def is_modifier?(value) # rubocop:disable Naming/PredicateName
|
11
11
|
Argument.is_modifier?(value)
|
12
12
|
end
|
13
13
|
|
@@ -21,22 +21,34 @@ module Eco
|
|
21
21
|
arguments.add(key, with_param: with_param)
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
24
|
# Validation to stop the `script` if among `argv` there's any **unknown** argument.
|
26
|
-
def stop_on_unknown!(exclude: [], only_options: false)
|
25
|
+
def stop_on_unknown!(exclude: [], only_options: false, all_available: nil)
|
27
26
|
# validate only those that are options
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
suggestions = {}
|
28
|
+
args = {
|
29
|
+
exclude: exclude,
|
30
|
+
all_available: all_available
|
31
|
+
}
|
32
|
+
unknown = arguments.unknown(**args) do |key, correct|
|
33
|
+
suggestions[key] = correct unless correct.empty?
|
31
34
|
end
|
35
|
+
unknown = unknown.select {|arg| is_modifier?(arg)} if only_options
|
32
36
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
return if unknown.empty?
|
38
|
+
|
39
|
+
suggestions_str = suggestions.slice(*unknown).map do |key, correct|
|
40
|
+
str = "Unknown option '#{key}'."
|
41
|
+
str_corr = correct.map {|key| "'#{key}'"}
|
42
|
+
str << " Did you mean: #{str_corr.join(', ')}" unless correct.empty?
|
43
|
+
str
|
44
|
+
end.join("\n * ")
|
45
|
+
|
46
|
+
msg = "\nThere are UNKNOWN OPTIONS in your command line arguments !!"
|
47
|
+
msg << "\n * #{suggestions_str}\n"
|
48
|
+
msg << "\nUse 'ruby main.rb -org [-usecase] --help -options' for more information"
|
49
|
+
msg << "\n - Please, remember that use case specific options "
|
50
|
+
msg << "should come after the use case in the command line.\n"
|
51
|
+
raise msg
|
40
52
|
end
|
41
53
|
|
42
54
|
# @return [Boolean] if `key` is in the command line.
|
@@ -46,28 +58,31 @@ module Eco
|
|
46
58
|
|
47
59
|
# @return [Integer, nil] the position of `key` in the command line.
|
48
60
|
def get_arg_index(key)
|
49
|
-
return
|
61
|
+
return unless arg?(key)
|
62
|
+
|
50
63
|
argv.index(key)
|
51
64
|
end
|
52
65
|
|
53
66
|
# @return [Boolean] if `key1` precedes `key2` in the command line.
|
54
|
-
def arg_order?(
|
55
|
-
|
56
|
-
|
67
|
+
def arg_order?(key_1, key_2)
|
68
|
+
k_1 = get_arg_index(key_1)
|
69
|
+
k_2 = get_arg_index(key_2)
|
70
|
+
return false unless k_1 && k_2
|
71
|
+
|
72
|
+
k_1 < k_2
|
57
73
|
end
|
58
74
|
|
59
75
|
# @return [String, Boolean] the argument value if `with_param` or a `Boolean` if not.
|
60
76
|
def get_arg(key, with_param: false, valid: true)
|
61
77
|
# track what a known option looks like
|
62
78
|
known_argument(key, with_param: with_param)
|
63
|
-
return nil
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
return value
|
79
|
+
return nil unless (index = get_arg_index(key))
|
80
|
+
return true unless with_param
|
81
|
+
|
82
|
+
value = argv[index + 1]
|
83
|
+
#puts "modifier argument: #{value}"
|
84
|
+
value = nil if valid && is_modifier?(value)
|
85
|
+
value
|
71
86
|
end
|
72
87
|
|
73
88
|
# @return [String] the filename.
|
@@ -13,6 +13,7 @@ module Eco
|
|
13
13
|
|
14
14
|
def each(&block)
|
15
15
|
return to_enum(:each) unless block
|
16
|
+
|
16
17
|
@known.values.each(&block)
|
17
18
|
end
|
18
19
|
|
@@ -22,7 +23,8 @@ module Eco
|
|
22
23
|
|
23
24
|
def <<(arg)
|
24
25
|
raise "Expected Argument. Given #{arg.class}" unless arg.is_a?(Argument)
|
25
|
-
|
26
|
+
|
27
|
+
if (karg = @known[arg.key])
|
26
28
|
#puts "Found already existent option #{arg.key} (with_param: arg.with_param?)"
|
27
29
|
karg.with_param! if arg.with_param?
|
28
30
|
else
|
@@ -40,14 +42,33 @@ module Eco
|
|
40
42
|
@known.keys
|
41
43
|
end
|
42
44
|
|
43
|
-
def unknown(exclude: [])
|
45
|
+
def unknown(exclude: [], all_available: nil)
|
44
46
|
reduce(args.dup - exclude) do |not_known, arg|
|
45
47
|
arg.args_slice(*not_known)
|
48
|
+
end.compact.tap do |list|
|
49
|
+
list.each do |key|
|
50
|
+
next unless block_given?
|
51
|
+
|
52
|
+
yield(key, unknown_suggestions(key, all_available: all_available))
|
53
|
+
end
|
46
54
|
end
|
47
55
|
end
|
48
56
|
|
57
|
+
def unknown_suggestions(str, all_available: nil)
|
58
|
+
return [] if str.nil?
|
59
|
+
|
60
|
+
str = str.to_s.strip
|
61
|
+
return [] if str.empty?
|
62
|
+
|
63
|
+
all_available ||= @known.keys
|
64
|
+
spell_checker = DidYouMean::SpellChecker.new(
|
65
|
+
dictionary: all_available
|
66
|
+
)
|
67
|
+
spell_checker.correct(str)
|
68
|
+
end
|
69
|
+
|
49
70
|
def any_unknown?(exclude: [])
|
50
|
-
unknown(exclude: exclude).length
|
71
|
+
unknown(exclude: exclude).length.positive?
|
51
72
|
end
|
52
73
|
|
53
74
|
private
|
data/lib/eco/cli.rb
CHANGED
@@ -74,8 +74,9 @@ ASSETS.cli do |cli| # rubocop:disable Metrics/BlockLength
|
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
77
|
-
wf.before(:launch_jobs) do
|
78
|
-
|
77
|
+
wf.before(:launch_jobs) do |_wf_jobs, io|
|
78
|
+
available_args = cli.config.available_option_args
|
79
|
+
SCR.stop_on_unknown!(all_available: available_args)
|
79
80
|
end
|
80
81
|
|
81
82
|
wf.on(:launch_jobs) do
|
data/lib/eco/csv/split.rb
CHANGED
@@ -5,10 +5,13 @@ module Eco
|
|
5
5
|
|
6
6
|
attr_reader :filename
|
7
7
|
|
8
|
-
def initialize(filename, max_rows:, **kargs)
|
9
|
-
|
8
|
+
def initialize(filename, max_rows:, start_at: nil, **kargs)
|
9
|
+
msg = "File '#{filename}' does not exist"
|
10
|
+
raise ArgumentError, msg unless ::File.exist?(filename)
|
11
|
+
|
10
12
|
@filename = filename
|
11
13
|
@max_rows = max_rows
|
14
|
+
@start_at = start_at
|
12
15
|
@params = kargs
|
13
16
|
init
|
14
17
|
end
|
@@ -20,7 +23,7 @@ module Eco
|
|
20
23
|
# - If `nil` it will create its own filename convention
|
21
24
|
# @return [Array<String>] names of the generated files
|
22
25
|
def call(&block)
|
23
|
-
stream.for_each do |row, ridx|
|
26
|
+
stream.for_each(start_at_idx: start_at) do |row, ridx|
|
24
27
|
copy_row(row, ridx, &block)
|
25
28
|
end
|
26
29
|
out_files
|
@@ -32,7 +35,7 @@ module Eco
|
|
32
35
|
private
|
33
36
|
|
34
37
|
attr_reader :params
|
35
|
-
attr_reader :idx, :max_rows
|
38
|
+
attr_reader :idx, :max_rows, :start_at
|
36
39
|
attr_reader :headers, :row_idx
|
37
40
|
|
38
41
|
attr_accessor :exception
|
@@ -40,19 +43,29 @@ module Eco
|
|
40
43
|
def copy_row(row, ridx, &block)
|
41
44
|
@headers ||= row.headers
|
42
45
|
@row_idx = ridx
|
43
|
-
|
46
|
+
|
47
|
+
current_csv(ridx) do |csv, fidx, file_out|
|
48
|
+
included = true
|
49
|
+
included &&= !block || yield(row, ridx, fidx, file_out)
|
50
|
+
next unless included
|
51
|
+
|
52
|
+
csv << row.fields
|
53
|
+
end
|
44
54
|
end
|
45
55
|
|
46
56
|
def current_csv(ridx)
|
47
57
|
if split?(ridx) || @csv.nil?
|
48
58
|
puts "Split at row #{row_idx}"
|
49
59
|
@csv&.close
|
50
|
-
|
51
|
-
out_filename =
|
52
|
-
@csv
|
53
|
-
@csv
|
54
|
-
out_files
|
60
|
+
|
61
|
+
out_filename = generate_name(next_idx)
|
62
|
+
@csv = ::CSV.open(out_filename, "w")
|
63
|
+
@csv << headers
|
64
|
+
out_files << out_filename
|
55
65
|
end
|
66
|
+
|
67
|
+
yield(@csv, idx, out_files.last) if block_given?
|
68
|
+
|
56
69
|
@csv
|
57
70
|
end
|
58
71
|
|
data/lib/eco/csv/stream.rb
CHANGED
data/lib/eco/csv.rb
CHANGED
@@ -18,16 +18,38 @@ module Eco
|
|
18
18
|
parse(get_file_content(file, **params), **kargs)
|
19
19
|
end
|
20
20
|
|
21
|
-
#
|
22
|
-
# @
|
21
|
+
# Splits the csv `filename` into `max_rows`
|
22
|
+
# @yield [row, ridx, fidx, file]
|
23
|
+
# @yieldparam row [Integer] the row
|
24
|
+
# @yieldparam ridx [Integer] the index of the row in the source file
|
25
|
+
# @yieldparam fidx [Integer] the number of the file
|
23
26
|
# @yieldparam file [String] the default name of the file
|
24
|
-
# @yieldreturn [
|
25
|
-
# - If `nil` it will create its own filename convention
|
27
|
+
# @yieldreturn [Bollean] whether the row should be included
|
26
28
|
# @param filename [String] the orignal file
|
27
29
|
# @param max_rows [Integer] number of rows per file
|
30
|
+
# @param start_at [Integer] row that sets the starting point.
|
31
|
+
# Leave empty for the full set of rows.
|
28
32
|
# @see Eco::CSV::Split#call
|
29
|
-
def split(filename, max_rows:, **kargs, &block)
|
30
|
-
Eco::CSV::Split.new(
|
33
|
+
def split(filename, max_rows:, start_at: nil, **kargs, &block)
|
34
|
+
Eco::CSV::Split.new(
|
35
|
+
filename,
|
36
|
+
max_rows: max_rows,
|
37
|
+
start_at: start_at,
|
38
|
+
**kargs
|
39
|
+
).call(&block)
|
40
|
+
end
|
41
|
+
|
42
|
+
# @note it excludes headers by default
|
43
|
+
# @return [Integer] the number of rows of the file
|
44
|
+
def count(filename, start_at: nil, **kargs)
|
45
|
+
count = 0
|
46
|
+
streamer = Eco::CSV::Stream.new(filename, **kargs)
|
47
|
+
streamer.for_each(start_at_idx: start_at) do |row|
|
48
|
+
included = true
|
49
|
+
included = yield(row) if block_given?
|
50
|
+
count += 1 if included
|
51
|
+
end
|
52
|
+
count
|
31
53
|
end
|
32
54
|
end
|
33
55
|
end
|
data/lib/eco/version.rb
CHANGED