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.
@@ -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 < 0 ? 0 : blanks
18
+ blanks = [blanks, 0].max
20
19
  top_line = " #{key}#{" "*blanks} "
21
20
  indent = top_line.length
22
21
  first = true
23
- each_slice_words(desc, line_len - indent).each_with_object([]) do |line, lines|
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
- out << liner
36
+
37
+ out << liner
37
38
  liner = part.strip
38
39
  end
39
40
  end.tap do |out|
40
- if out.empty? || !liner.empty?
41
- yield(liner) if block_given?
42
- out << liner if liner
43
- end
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
@@ -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
- unless io && io.is_a?(Eco::API::UseCases::BaseIO)
28
- raise "You need to provide Eco::API::UseCases::BaseIO object. Given: #{io.class}"
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 !!option
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
- class OptConfig < Struct.new(:name, :namespace, :description, :callback)
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 = 2
19
- spaces = any_non_general_space_active? ? active_namespaces : namespaces
18
+ indent = 2
19
+ spaces = any_non_general_space_active? ? active_namespaces : namespaces
20
20
  refinement = refine.is_a?(String)? " (containing: '#{refine}')" : ""
21
- ["The following are the available options#{refinement}:"].yield_self do |lines|
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
- options_set(namespace).select do |arg, option|
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 |arg, option|
30
- lines << help_line(" " * indent + "#{option.name}", option.description, max_len)
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
- puts "Overriding CLI option '#{option}' in '#{namespace}' CLI case / namespace" if option_exists?(opt, namespace)
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
- unless io && io.is_a?(Eco::API::UseCases::BaseIO)
63
- raise "You need to provide Eco::API::UseCases::BaseIO object. Given: #{io.class}"
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, opts_set|
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 |(namespace, opts_set), options|
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 > 0
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
- unless use.is_a?(Eco::API::UseCases::UseCase)
126
- msg = "When adding a usecase, without specifying 'case_name:', "
127
- msg += "the block that integrates usecase for cli option '#{data.option}'"
128
- msg += " must return an Eco::API::UseCases::UseCase object. It returns #{use.class}"
129
- raise msg
130
- end
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
@@ -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
- if block_given?
29
- @input.define(&block)
30
- self
31
- else
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
- raise "There is no definition on how to load people" unless instance_variable_defined?(:@people_load) && @people_load
42
- unless io && io.is_a?(Eco::API::UseCases::BaseIO)
43
- raise "You need to provide Eco::API::UseCases::BaseIO object. Given: #{io.class}"
44
- end
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
- unknown = arguments.unknown(exclude: exclude)
29
- if only_options
30
- unknown = unknown..select {|arg| is_modifier?(arg)}
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
- unless unknown.empty?
34
- msg = "There are unknown options in your command line arguments:\n"
35
- msg += "#{unknown}\n"
36
- msg += "Please, remember that use case specific options should come after the use case in the command line.\n"
37
- msg += "Use 'ruby main.rb -org [-usecase] --help -options' for more information"
38
- raise msg
39
- end
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 nil if !arg?(key)
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?(key1, key2)
55
- return false unless (k1 = get_arg_index(key1)) && k2 = get_arg_index(key2)
56
- k1 < k2
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 unless index = get_arg_index(key)
64
- value = true
65
- if with_param
66
- value = argv[index + 1]
67
- #puts "modifier argument: #{value}"
68
- value = nil if valid && is_modifier?(value)
69
- end
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
- if karg = @known[arg.key]
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 > 0
71
+ unknown(exclude: exclude).length.positive?
51
72
  end
52
73
 
53
74
  private
data/lib/eco/cli.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  module Eco
2
2
  class CLI
3
-
4
3
  def initialize
5
4
  @config = nil
6
5
  end
@@ -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
- SCR.stop_on_unknown!
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
- raise ArgumentError, "File '#{filename}' does not exist" unless ::File.exist?(filename)
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
- current_csv(ridx, &block) << row.fields
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
- out_filename = generate_name(nidx = next_idx)
51
- out_filename = yield(nidx, out_filename) if block_given?
52
- @csv = ::CSV.open(out_filename, "w")
53
- @csv << headers
54
- out_files << out_filename
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
 
@@ -37,6 +37,7 @@ module Eco
37
37
  end
38
38
 
39
39
  def move_to_idx(start_at_idx)
40
+ start_at_idx ||= 0
40
41
  next_idx while (idx < start_at_idx) && (self.row = csv.shift)
41
42
  end
42
43
 
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
- # @yield [idx, file] a block to spot the filename
22
- # @yieldparam idx [Integer] the number of the file
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 [String] the filename of the file `idx`.
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(filename, max_rows: max_rows, **kargs).call(&block)
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
@@ -1,3 +1,3 @@
1
1
  module Eco
2
- VERSION = '2.7.16'.freeze
2
+ VERSION = '2.7.18'.freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eco-helpers
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.16
4
+ version: 2.7.18
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oscar Segura