eco-helpers 2.7.16 → 2.7.18
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -3
- 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/api/usecases/graphql/samples/location/service/tree_diff.rb +13 -1
- data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter/parser.rb +5 -5
- data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter.rb +3 -5
- 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
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