eco-helpers 2.5.2 → 2.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +62 -2
  3. data/eco-helpers.gemspec +2 -2
  4. data/lib/eco/api/common/loaders/use_case.rb +0 -2
  5. data/lib/eco/api/common/people/person_entry_attribute_mapper.rb +0 -2
  6. data/lib/eco/api/common/session/logger.rb +22 -77
  7. data/lib/eco/api/microcases/with_each.rb +0 -1
  8. data/lib/eco/api/organization/tag_tree.rb +64 -15
  9. data/lib/eco/api/session/config/tagtree.rb +32 -10
  10. data/lib/eco/api/session/config/workflow.rb +0 -1
  11. data/lib/eco/api/session/config.rb +6 -2
  12. data/lib/eco/api/session.rb +2 -2
  13. data/lib/eco/api/usecases/default_cases/abstract_policygroup_abilities_case.rb +2 -3
  14. data/lib/eco/api/usecases/default_cases/analyse_people_case.rb +2 -3
  15. data/lib/eco/api/usecases/default_cases/append_usergroups_case.rb +0 -1
  16. data/lib/eco/api/usecases/default_cases/change_email_case.rb +1 -2
  17. data/lib/eco/api/usecases/default_cases/clean_unknown_tags_case.rb +0 -5
  18. data/lib/eco/api/usecases/default_cases/clear_abilities_case.rb +2 -2
  19. data/lib/eco/api/usecases/default_cases/codes_to_tags_case.rb +5 -7
  20. data/lib/eco/api/usecases/default_cases/create_case.rb +0 -5
  21. data/lib/eco/api/usecases/default_cases/create_details_case.rb +0 -5
  22. data/lib/eco/api/usecases/default_cases/create_details_with_supervisor_case.rb +0 -5
  23. data/lib/eco/api/usecases/default_cases/csv_to_tree_case/helper.rb +1 -1
  24. data/lib/eco/api/usecases/default_cases/csv_to_tree_case.rb +0 -4
  25. data/lib/eco/api/usecases/default_cases/delete_sync_case.rb +2 -4
  26. data/lib/eco/api/usecases/default_cases/delete_trans_case.rb +2 -3
  27. data/lib/eco/api/usecases/default_cases/email_as_id_case.rb +0 -1
  28. data/lib/eco/api/usecases/default_cases/entries_to_csv_case.rb +0 -4
  29. data/lib/eco/api/usecases/default_cases/hris_case.rb +2 -3
  30. data/lib/eco/api/usecases/default_cases/new_email_case.rb +0 -2
  31. data/lib/eco/api/usecases/default_cases/new_id_case.rb +0 -2
  32. data/lib/eco/api/usecases/default_cases/org_data_convert_case.rb +0 -5
  33. data/lib/eco/api/usecases/default_cases/refresh_case.rb +0 -1
  34. data/lib/eco/api/usecases/default_cases/reinvite_sync_case.rb +1 -3
  35. data/lib/eco/api/usecases/default_cases/reinvite_trans_case.rb +2 -2
  36. data/lib/eco/api/usecases/default_cases/remove_account_sync_case.rb +1 -2
  37. data/lib/eco/api/usecases/default_cases/remove_account_trans_case.rb +2 -3
  38. data/lib/eco/api/usecases/default_cases/reset_landing_page_case.rb +1 -7
  39. data/lib/eco/api/usecases/default_cases/restore_db_case.rb +0 -10
  40. data/lib/eco/api/usecases/default_cases/set_default_tag_case.rb +0 -1
  41. data/lib/eco/api/usecases/default_cases/set_supervisor_case.rb +0 -1
  42. data/lib/eco/api/usecases/default_cases/supers_cyclic_identify_case.rb +2 -3
  43. data/lib/eco/api/usecases/default_cases/supers_hierarchy_case.rb +2 -3
  44. data/lib/eco/api/usecases/default_cases/switch_supervisor_case.rb +2 -4
  45. data/lib/eco/api/usecases/default_cases/tagtree_case.rb +0 -2
  46. data/lib/eco/api/usecases/default_cases/to_csv_case.rb +4 -5
  47. data/lib/eco/api/usecases/default_cases/to_csv_detailed_case.rb +0 -1
  48. data/lib/eco/api/usecases/default_cases/transfer_account_case.rb +0 -2
  49. data/lib/eco/api/usecases/default_cases/update_case.rb +0 -2
  50. data/lib/eco/api/usecases/default_cases/update_details_case.rb +0 -2
  51. data/lib/eco/api/usecases/default_cases/upsert_case.rb +0 -4
  52. data/lib/eco/api/usecases/graphql/base.rb +6 -18
  53. data/lib/eco/api/usecases/graphql/helpers/base/case_env.rb +15 -0
  54. data/lib/eco/api/usecases/graphql/helpers/base.rb +23 -0
  55. data/lib/eco/api/usecases/graphql/helpers/location/base.rb +87 -0
  56. data/lib/eco/api/usecases/graphql/helpers/location/command/result.rb +69 -0
  57. data/lib/eco/api/usecases/graphql/helpers/location/command/results.rb +126 -0
  58. data/lib/eco/api/usecases/graphql/helpers/location/command.rb +92 -0
  59. data/lib/eco/api/usecases/graphql/helpers/location.rb +7 -0
  60. data/lib/eco/api/usecases/graphql/helpers.rb +2 -1
  61. data/lib/eco/api/usecases/graphql/samples/location/command/dsl.rb +54 -0
  62. data/lib/eco/api/usecases/graphql/samples/location/command/results.rb +125 -0
  63. data/lib/eco/api/usecases/graphql/samples/location/command.rb +10 -0
  64. data/lib/eco/api/usecases/graphql/samples/location/dsl.rb +6 -0
  65. data/lib/eco/api/usecases/graphql/samples/location.rb +10 -0
  66. data/lib/eco/api/usecases/graphql/samples.rb +6 -0
  67. data/lib/eco/api/usecases/graphql/utils/sftp.rb +74 -0
  68. data/lib/eco/api/usecases/graphql/utils.rb +6 -0
  69. data/lib/eco/api/usecases/graphql.rb +3 -1
  70. data/lib/eco/api/usecases/ooze_cases/export_register_case.rb +0 -1
  71. data/lib/eco/api/usecases/ooze_samples/ooze_base_case.rb +0 -2
  72. data/lib/eco/api/usecases/ooze_samples/register_migration_case.rb +0 -2
  73. data/lib/eco/api/usecases/use_case.rb +2 -2
  74. data/lib/eco/cli/config/default/workflow.rb +2 -4
  75. data/lib/eco/cli/scripting/args_helpers.rb +0 -2
  76. data/lib/eco/csv/table.rb +39 -3
  77. data/lib/eco/data/files/helpers.rb +4 -3
  78. data/lib/eco/data/hashes/array_diff.rb +21 -61
  79. data/lib/eco/data/hashes/diff_meta.rb +52 -0
  80. data/lib/eco/data/hashes/diff_result.rb +36 -25
  81. data/lib/eco/data/hashes.rb +1 -0
  82. data/lib/eco/data/locations/convert.rb +92 -0
  83. data/lib/eco/data/locations/dsl.rb +35 -0
  84. data/lib/eco/data/locations/node_base/builder.rb +26 -0
  85. data/lib/eco/data/locations/node_base/csv_convert.rb +57 -0
  86. data/lib/eco/data/locations/node_base/parsing.rb +30 -0
  87. data/lib/eco/data/locations/node_base/serial.rb +26 -0
  88. data/lib/eco/data/locations/node_base/tag_validations.rb +52 -0
  89. data/lib/eco/data/locations/node_base/treeify.rb +150 -0
  90. data/lib/eco/data/locations/node_base.rb +48 -0
  91. data/lib/eco/data/locations/node_diff/accessors.rb +46 -0
  92. data/lib/eco/data/locations/node_diff/nodes_diff.rb +90 -0
  93. data/lib/eco/data/locations/node_diff/selectors.rb +20 -0
  94. data/lib/eco/data/locations/node_diff.rb +55 -0
  95. data/lib/eco/data/locations/node_level/builder.rb +6 -0
  96. data/lib/eco/data/locations/node_level/cleaner.rb +74 -0
  97. data/lib/eco/data/locations/node_level/parsing.rb +63 -0
  98. data/lib/eco/data/locations/node_level/serial.rb +37 -0
  99. data/lib/eco/data/locations/node_level.rb +153 -0
  100. data/lib/eco/data/locations/node_plain/builder.rb +6 -0
  101. data/lib/eco/data/locations/node_plain/parsing.rb +36 -0
  102. data/lib/eco/data/locations/node_plain/serial.rb +14 -0
  103. data/lib/eco/data/locations/node_plain.rb +31 -0
  104. data/lib/eco/data/locations.rb +13 -0
  105. data/lib/eco/data.rb +1 -0
  106. data/lib/eco/language/auxiliar_logger.rb +9 -1
  107. data/lib/eco/language/basic_logger.rb +74 -0
  108. data/lib/eco/language.rb +2 -1
  109. data/lib/eco/version.rb +1 -1
  110. metadata +45 -8
  111. data/lib/eco/api/usecases/default_cases/new_id_case0.rb +0 -14
  112. data/lib/eco/api/usecases/graphql/helpers/locations/commands.rb +0 -4
  113. data/lib/eco/api/usecases/graphql/helpers/locations.rb +0 -6
@@ -7,5 +7,7 @@ module Eco
7
7
  end
8
8
  end
9
9
 
10
- require_relative 'graphql/base'
11
10
  require_relative 'graphql/helpers'
11
+ require_relative 'graphql/utils'
12
+ require_relative 'graphql/base'
13
+ require_relative 'graphql/samples'
@@ -5,7 +5,6 @@ class Eco::API::UseCases::OozeCases::ExportRegisterCase < Eco::API::UseCases::Oo
5
5
  batch_size 5
6
6
 
7
7
  def main(session, options, usecase)
8
-
9
8
  super(session, options, usecase) do
10
9
  # Save the File
11
10
  CSV.open(filename, "w") do |csv|
@@ -5,7 +5,6 @@ class Eco::API::UseCases::OozeSamples::OozeBaseCase < Eco::API::Common::Loaders:
5
5
 
6
6
  include Eco::API::UseCases::OozeSamples::Helpers
7
7
 
8
- attr_reader :session, :options, :usecase
9
8
  attr_reader :target
10
9
 
11
10
  SAVE_PATCH = "ooze_patch_update.json"
@@ -14,7 +13,6 @@ class Eco::API::UseCases::OozeSamples::OozeBaseCase < Eco::API::Common::Loaders:
14
13
  def main(session, options, usecase)
15
14
  options[:end_get] = false
16
15
  raise "You need to inherit from this class ('#{self.class}') and call super with a block" unless block_given?
17
- @session = session; @options = options; @usecase = usecase
18
16
  @target = nil
19
17
  yield
20
18
  end
@@ -40,11 +40,9 @@ class Eco::API::UseCases::OozeSamples::RegisterMigrationCase < Eco::API::UseCase
40
40
 
41
41
  include Eco::API::UseCases::OozeSamples::HelpersMigration
42
42
 
43
- attr_reader :session, :options
44
43
  attr_reader :csv
45
44
 
46
45
  def main(session, options, usecase, &block)
47
- @session = session; @options = options
48
46
  if options[:dry_run]
49
47
  @csv = []
50
48
  super(session, options, usecase, &block)
@@ -75,10 +75,10 @@ module Eco
75
75
  return false unless callback_from_loader?
76
76
  use_case_self = self
77
77
  callback_self.instance_eval do
78
- next unless self.is_a?(Eco::API::Common::Loaders::CaseBase)
79
- # `self` is the use case itself (when used the Loader)
80
78
  @session = session
81
79
  @options = options
80
+ # `self` is the use case itself (when used the Loader)
81
+ next unless self.is_a?(Eco::API::Common::Loaders::CaseBase)
82
82
  @usecase = use_case_self
83
83
  end
84
84
  true
@@ -1,8 +1,7 @@
1
1
  ASSETS.cli.config do |config|
2
2
  ASSETS.config.workflow do |wf|
3
-
4
- io = nil
5
- rescued = false
3
+ io = nil
4
+ rescued = false
6
5
  cases_with_input = nil
7
6
  cases_with_output = nil
8
7
 
@@ -183,6 +182,5 @@ ASSETS.cli.config do |config|
183
182
  end
184
183
  io
185
184
  end
186
-
187
185
  end
188
186
  end
@@ -2,7 +2,6 @@ module Eco
2
2
  class CLI
3
3
  class Scripting
4
4
  module ArgsHelpers
5
-
6
5
  # @return [Array<String] the command line arguments.
7
6
  def argv
8
7
  @argv || ARGV
@@ -91,7 +90,6 @@ module Eco
91
90
  def file_exists?(filename)
92
91
  File.exists?(filename) || File.exists?(File.expand_path(filename))
93
92
  end
94
-
95
93
  end
96
94
  end
97
95
  end
data/lib/eco/csv/table.rb CHANGED
@@ -11,6 +11,25 @@ module Eco
11
11
  end
12
12
  end
13
13
 
14
+ # It ensures blank strings are set to `nil`
15
+ # @note assumes there are no repeated header names
16
+ # @return [Eco::CSV::Table]
17
+ def nil_blank_cells!
18
+ self.each do |row|
19
+ row.dup.each do |header, value|
20
+ value = value.to_s.strip
21
+ row[header] = value.empty?? nil : value
22
+ end
23
+ end
24
+ self
25
+ end
26
+
27
+ # A new table from `self` where blank strings are have been set to `nil`
28
+ # @return [Eco::CSV::Table]
29
+ def nil_blank_cells
30
+ self.class.new(self).nil_blank_cells!
31
+ end
32
+
14
33
  # @return [Hash] where keys are the groups and the values a `Eco::CSV::Table`
15
34
  def group_by(&block)
16
35
  rows.group_by(&block).transform_values do |rows|
@@ -111,11 +130,29 @@ module Eco
111
130
  end
112
131
 
113
132
  # Adds a new column at the end
133
+ # @note by default it adds it to the end.
114
134
  # @param header_name [String] header of the new column
135
+ # @param pos [Integer] index where to add the column (i.e. `0` for first)
115
136
  # @return [Eco::CSV::Table] with a new empty column
116
- def add_column(header_name)
137
+ def add_column(header_name, pos: -1)
138
+ header_name = header_name.to_s.strip
139
+ raise ArgumentError, "header_name can't be blank" if header_name.empty?
117
140
  new_col = Array.new(length).unshift(header_name)
118
- columns_to_table(columns.push(new_col))
141
+ columns_to_table(columns.insert(pos, new_col))
142
+ end
143
+
144
+ # @note by default it adds as a first column
145
+
146
+ # @param header_name [String] header of the new column
147
+ # @param pos [Integer] index where to add the column (i.e. `-1` for last)
148
+ # @return [Eco::CSV::Table] with a new column named `name` with the row number
149
+ def add_index_column(header_name = 'idx', pos: 0)
150
+ header_name = header_name.to_s.strip
151
+ add_column(header_name, pos: pos).tap do |table|
152
+ table.each.with_index do |row, idx|
153
+ row[header_name] = idx + 2
154
+ end
155
+ end
119
156
  end
120
157
 
121
158
  # @return [Array<::CSV::Row>]
@@ -221,7 +258,6 @@ module Eco
221
258
  raise "Input type not supported. Given: #{data.class}"
222
259
  end
223
260
  end
224
-
225
261
  end
226
262
  end
227
263
  end
@@ -75,11 +75,11 @@ module Eco
75
75
  Dir.exist?(path) || Dir.exist?(File.expand_path(path))
76
76
  end
77
77
 
78
- def timestamp(timestamp_pattern = DEFAULT_TIMESTAMP_PATTERN)
79
- Time.now.strftime(timestamp_pattern)
78
+ def timestamp(timestamp_pattern = DEFAULT_TIMESTAMP_PATTERN, date = Time.now)
79
+ date.strftime(timestamp_pattern)
80
80
  end
81
81
 
82
- def timestamp_file(filename, timestamp_pattern = DEFAULT_TIMESTAMP_PATTERN)
82
+ def timestamp_file(filename, timestamp_pattern =DEFAULT_TIMESTAMP_PATTERN)
83
83
  file_pattern = Eco::Data::Files::FilePattern.new(filename)
84
84
  file_pattern.resolve(start: timestamp(timestamp_pattern) + '_')
85
85
  end
@@ -119,6 +119,7 @@ module Eco
119
119
  end
120
120
 
121
121
  class << self
122
+ include Files::InstanceMethods
122
123
  include Files::ClassMethods
123
124
  end
124
125
  end
@@ -3,46 +3,16 @@ module Eco
3
3
  module Hashes
4
4
  class ArrayDiff
5
5
  extend Eco::Language::Models::ClassHelpers
6
+ # We can change the `DiffResult` class (items)
7
+ class_resolver :diff_result_class, "Eco::Data::Hash::DiffResult"
6
8
 
7
- class << self
8
- def key(value = nil)
9
- return @key unless value
10
- @key = value.to_s
11
- end
12
-
13
- def key?
14
- !!@key
15
- end
16
-
17
- def compare(*attrs)
18
- compared_attrs.push(*attrs.map(&:to_s)).uniq!
19
- end
20
-
21
- def case_sensitive(value = nil)
22
- @case_sensitive = false unless instance_variable_defined?(:@case_sensitive)
23
- return @case_sensitive unless value
24
- @case_sensitive = !!value
25
- end
26
-
27
- def case_sensitive?
28
- !!@case_sensitive
29
- end
30
-
31
- def compared_attrs
32
- @compared_attrs ||= []
33
- @compared_attrs
34
- end
35
- end
9
+ include Eco::Language::AuxiliarLogger
36
10
 
37
11
  attr_reader :source1, :source2
38
12
  attr_reader :src_h1, :src_h2
39
- attr_reader :logger
40
-
41
- class_resolver :diff_result_class, "Eco::Data::Hash::DiffResult"
42
13
 
43
- def initialize(source1, source2, logger: ::Logger.new(IO::NULL), **kargs)
44
- @logger = logger
45
- @options = kargs
14
+ def initialize(source1, source2, logger: nil)
15
+ @logger = logger if logger
46
16
  @source1 = source1
47
17
  @source2 = source2
48
18
  @src_h1 = by_key(source1)
@@ -66,14 +36,11 @@ module Eco
66
36
  !diffs.empty?
67
37
  end
68
38
 
39
+ # All the items that contain the diff of a node.
40
+ # @return [Array<Eco::Data::Hash::DiffResult>]
69
41
  def source_results
70
42
  @source_results ||= paired_sources.each_with_object([]) do |(src1, src2), res|
71
- args = {
72
- key: key,
73
- compare: compared_attrs,
74
- case_sensitive: case_sensitive?
75
- }
76
- res << diff_result_class.new(src1, src2, **args)
43
+ res << diff_result_class.new(src1, src2)
77
44
  end
78
45
  end
79
46
 
@@ -84,40 +51,29 @@ module Eco
84
51
  # - It also ensures they are in their Hash form (with string keys)
85
52
  # - This will merge entries of the same source that hold the same `key` attr value (latest wins)
86
53
  def paired_sources
87
- keys1 = src_h1.keys; keys2 = src_h2.keys
54
+ keys1 = src_h1.keys
55
+ keys2 = src_h2.keys
88
56
  all_keys = keys1 | keys2
89
57
  all_keys.map {|key| [src_h1[key], src_h2[key]]}
90
58
  end
91
59
 
60
+ # @return [String] the `key` attribute of `diff_result_class`
92
61
  def key
93
- @key ||= options_or(:key) do
94
- self.class.key
95
- end.tap do |k|
96
- raise "missing main key attr to pair records. Given: #{k}" unless k.is_a?(String)
62
+ diff_result_class.key.tap do |k|
63
+ raise "#{diff_result_class}: missing main key attr to pair records. Given: #{k}" unless k.is_a?(String)
97
64
  end
98
65
  end
99
66
 
100
67
  def case_sensitive?
101
- @case_sensitive ||= options_or(:case_sensitive) { self.class.case_sensitive? }
68
+ diff_result_class.case_sensitive?
102
69
  end
103
70
 
104
71
  def compared_attrs
105
- @compared_attrs ||= options_or(:compared_attrs) do
106
- self.class.compared_attrs
107
- end.yield_self do |attrs|
108
- raise "compared_attrs should be an array" unless attrs.is_a?(Array)
109
- attrs.map(&:to_s)
110
- end
72
+ diff_result_class.compared_attrs.map(&:to_s)
111
73
  end
112
74
 
113
75
  private
114
76
 
115
- def options_or(opt)
116
- opt = opt.to_sym
117
- return @options[opt] if @options.key?(opt)
118
- yield
119
- end
120
-
121
77
  def symbolize_keys(hash)
122
78
  hash.each_with_object({}) do |(k, v), h|
123
79
  h[k.to_sym] = v
@@ -149,11 +105,15 @@ module Eco
149
105
  when Hash, Array, ::CSV::Row
150
106
  Eco::CSV::Table.new(content).to_array_of_hashes
151
107
  else
152
- logger.error("Input content 'Array' of '#{sample.class}' is not supported.")
108
+ log(:error) {
109
+ "Input content 'Array' of '#{sample.class}' is not supported."
110
+ }
153
111
  exit(1)
154
112
  end
155
113
  else
156
- logger.error("Could not obtain any data out content: '#{content.class}'")
114
+ log(:error) {
115
+ "Could not obtain any data out content: '#{content.class}'"
116
+ }
157
117
  exit(1)
158
118
  end
159
119
  end
@@ -0,0 +1,52 @@
1
+ module Eco
2
+ module Data
3
+ module Hashes
4
+ module DiffMeta
5
+ class << self
6
+ def included(base)
7
+ super(base)
8
+ base.extend ClassMethods
9
+ end
10
+ end
11
+
12
+ module ClassMethods
13
+ # @param value [String, NilClass]
14
+ # @return [String] the attribute that is key of the node diff elements.
15
+ def key(value = nil)
16
+ return @key unless value
17
+ @key = value.to_s
18
+ end
19
+
20
+ # @return [Boolean] is there a `key` attribute defined?
21
+ def key?
22
+ !!@key
23
+ end
24
+
25
+ # @param attrs [Array<Symbol>, Array<String>]
26
+ # @return [Array<String>] the comparable attributes
27
+ def compare(*attrs)
28
+ compared_attrs.push(*attrs.map(&:to_s)).uniq!
29
+ compared_attrs
30
+ end
31
+
32
+ # Whether or not the diff calc of a node should be done case sensitive or insensitive.
33
+ def case_sensitive(value = nil)
34
+ @case_sensitive = false unless instance_variable_defined?(:@case_sensitive)
35
+ return @case_sensitive unless value
36
+ @case_sensitive = !!value
37
+ end
38
+
39
+ # @return [Boolean] are comparisons of values done case sensitive?
40
+ def case_sensitive?
41
+ !!@case_sensitive
42
+ end
43
+
44
+ # @return [Array<String>] the comparable attributes
45
+ def compared_attrs
46
+ @compared_attrs ||= []
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -2,20 +2,32 @@ module Eco
2
2
  module Data
3
3
  module Hashes
4
4
  class DiffResult
5
+ extend Eco::Language::Models::ClassHelpers
6
+ include Eco::Data::Hashes::DiffMeta
7
+
8
+ inheritable_class_vars :key, :compared_attrs, :case_sensitive
5
9
 
6
- attr_reader :key
7
10
  attr_reader :src1, :src2
8
11
 
9
- # @param [Array<String>, sym]
10
- # - `:all` compares the matching attrs between both hashes only
11
- def initialize(src1, src2, key:, compare: :all, case_sensitive: false)
12
- @key = key
13
- @compare = compare
14
- @case_sensitive = case_sensitive
12
+ def initialize(src1, src2)
15
13
  @src1 = src1
16
14
  @src2 = src2
17
15
  end
18
16
 
17
+ def key
18
+ self.class.key
19
+ end
20
+
21
+ def case_sensitive?
22
+ self.class.case_sensitive?
23
+ end
24
+
25
+ def dup(src1: nil, src2: nil)
26
+ src1 ||= self.src1
27
+ src2 ||= self.src2
28
+ self.class.new(src1.dup, src2.dup)
29
+ end
30
+
19
31
  def new?
20
32
  !src1 && !!src2
21
33
  end
@@ -28,35 +40,39 @@ module Eco
28
40
  !new? && !del? && diff?
29
41
  end
30
42
 
43
+ # @note `diff_attrs` may not include the `key` attribute
44
+ # This is always included via `new?` (new key value) and `del?` (missing key value)
45
+ # @return [Boolean] was there any change?
31
46
  def diff?
32
47
  new? || del? || !diff_attrs.empty?
33
48
  end
34
49
 
35
- # Is the key attr value changing?
50
+ # Is the `key` attr value changing?
36
51
  def key?
37
52
  !(new? || del?) && diff_attr?(key)
38
53
  end
39
54
 
55
+ # Is `attr` part of the attributes that change?
40
56
  def diff_attr?(attr)
41
57
  return true if new?
42
58
  return true if del?
43
59
  diff_attrs.include?(attr.to_s)
44
60
  end
45
61
 
62
+ # @return [Value] the current value of `attr` (in `src2`)
46
63
  def attr(attr)
47
64
  return nil unless src2
48
65
  src2[attr.to_s]
49
66
  end
50
67
 
68
+ # @return [Value] the previous value of `attr` (in `src1`)
51
69
  def attr_prev(attr)
52
70
  return nil unless src1
53
71
  src1[attr.to_s]
54
72
  end
55
73
 
56
- def previous(attr)
57
- src1 && src1[attr.to_s]
58
- end
59
-
74
+ # @note the `key` attribute will always be added (even if there's no change)
75
+ # @return [Hash] hash with the differences as per `src2`
60
76
  def diff_hash
61
77
  target_attrs = [key] | compared_attrs
62
78
  return src2.slice(*target_attrs) if new?
@@ -64,12 +80,14 @@ module Eco
64
80
  src2.slice(key, *diff_attrs)
65
81
  end
66
82
 
83
+ # @return [Array<Symbol>] hash with the differences as per `src2`
67
84
  def diff_attrs
68
85
  @diff_attrs ||= comparable_attrs.each_with_object([]) do |attr, out|
69
86
  out << attr unless eq?(src1[attr], src2[attr])
70
87
  end
71
88
  end
72
89
 
90
+ # @return [Boolean] whether `val1` is equal to `val2`
73
91
  def eq?(val1, val2)
74
92
  return true if val1 == val2
75
93
  return false if case_sensitive?
@@ -77,25 +95,18 @@ module Eco
77
95
  val1.upcase == val2.upcase
78
96
  end
79
97
 
80
- def case_sensitive?
81
- !!@case_sensitive
82
- end
83
-
98
+ # @note when is `new?` or to be deleted (`del?`), it returns empty array.
99
+ # @return [Array<String>] the set of attributes that are comparable in this instance.
84
100
  def comparable_attrs
85
101
  return [] if new? || del?
86
102
  compared_attrs
87
103
  end
88
104
 
105
+ # @return [Array<String>] the set of attributes that are comparable in this class.
89
106
  def compared_attrs
90
- return @compared_attrs if instance_variable_defined?(:@compared_attrs)
91
- @compared_attrs = \
92
- if @compare == :all
93
- src1.keys & src2.keys
94
- elsif @compare.is_a?(Array)
95
- @compare.map(&:to_s)
96
- else
97
- raise "Expecting 'compare' to be sym (:all) or Array<String>. Given: #{@compare.class}"
98
- end
107
+ comp_attrs = self.class.compared_attrs.map(&:to_s).uniq
108
+ return comp_attrs unless comp_attrs.empty?
109
+ (src1&.keys || []) & (src2&.keys || [])
99
110
  end
100
111
  end
101
112
  end
@@ -5,5 +5,6 @@ module Eco
5
5
  end
6
6
  end
7
7
 
8
+ require_relative 'hashes/diff_meta'
8
9
  require_relative 'hashes/diff_result'
9
10
  require_relative 'hashes/array_diff'
@@ -0,0 +1,92 @@
1
+ module Eco::Data::Locations
2
+ module Convert
3
+ include Eco::Language::AuxiliarLogger
4
+
5
+ # Helper to open a csv
6
+ # @note this is a shortcut helper.
7
+ # @param filename [String] the csv file.
8
+ # @return [Eco::CSV::Table]
9
+ def csv_from(filename, encoding: 'utf-8')
10
+ raise ArgumentError, "Expecting String filename. Given: #{filename.class}" unless filename.is_a?(String)
11
+ raise "Missing #{filename}" unless File.exists?(filename)
12
+ Eco::CSV.read(filename, encoding: encoding)
13
+ rescue CSV::MalformedCSVError => e
14
+ if match = e.message.match(/line (?<line>\d+)/i)
15
+ log(:error) {"An encoding problem was found on line #{match[:line]}"}
16
+ end
17
+ raise
18
+ end
19
+
20
+ # Generic converter/helper to generate the csv data export for a hierarchical csv tree
21
+ # @note The steps of usage would be:
22
+ # 1. First **treeify** your input (i.e. `Eco::API::Organization::TagTree#as_json`,
23
+ # or `treeify(nodes)`
24
+ # @param hash_nodes [Array<Hash>] a hierarchical tree of Hash nodes, nested via `nodes`
25
+ # @return [CSV::Table] ready to be made a hierarchical csv tree (i.e. out.to_csv)
26
+ def hash_tree_to_tree_csv(hash_nodes, out: [], done_ids: [], repeated_ids: [], level: 0)
27
+ lev = level + 1
28
+ base = empty_array(level)
29
+
30
+ hash_nodes.each_with_object(out) do |node, out|
31
+ if done_ids.include?(id = node["id"])
32
+ repeated_ids << id
33
+ else
34
+ has_offspring = (children = node["nodes"]) && !children.empty?
35
+ done_ids << id
36
+ out << (base.dup << node["id"])
37
+ hash_tree_to_tree_csv(node["nodes"], out: out, done_ids: done_ids, repeated_ids: repeated_ids, level: lev)
38
+ end
39
+ end.tap do |out|
40
+ if level == 0
41
+ report_repeated_node_ids(repeated_ids)
42
+ return Eco::CSV::Table.new(normalize_arrays(out))
43
+ end
44
+ end
45
+ end
46
+
47
+ # It normalizes the size of the arrays to the max size among the arrays
48
+ # @param rows [Array<Array>] where arrays may not have the same length
49
+ # @return [Array<Array>] where arrays have all the same length
50
+ def normalize_arrays(rows)
51
+ max_row = rows.max {|a, b| a.length <=> b.length}
52
+ holder = empty_array(max_row.length)
53
+ rows.map do |row|
54
+ row.dup.concat(holder[0..-(row.length+1)])
55
+ end
56
+ end
57
+
58
+ # @param count [Integer] number of possitions of the new array
59
+ # @return [Array<NilClass>] with `count` positions.
60
+ def empty_array(count)
61
+ Array.new(count, nil)
62
+ end
63
+
64
+ # @note
65
+ # 1. Initially it has as many keys as levels `count`
66
+ # 2. It serves the purpose to track the lastest seen node
67
+ # for a given level, during a loop.
68
+ # @return [Hash] with integer level counts as keys and
69
+ # nodes as values.
70
+ def empty_level_tracker_hash(count = 11)
71
+ Array(1..count).zip(empty_array(count)).to_h
72
+ end
73
+
74
+ # It logs a message from `yield` and appends a `pretty_inspect` on object.
75
+ # @note it only works where `object` is `Enumberable`
76
+ def log_pretty_inspect(object, lev = :info)
77
+ return unless object.is_a?(Enumerable)
78
+ return if object.empty?
79
+ msg = ''
80
+ msg << "#{yield(object)}\n" if block_given?
81
+ msg << object.pretty_inspect
82
+ log(lev) { msg }
83
+ end
84
+
85
+ # Prints a common message
86
+ def report_repeated_node_ids(repeated)
87
+ log_pretty_inspect(repeated, :warn) do
88
+ "There were #{repeated.length} repeated node ids. Only one included. These excluded:"
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,35 @@
1
+ module Eco::Data::Locations
2
+ # It allows to create your own helper
3
+ # @example of usage:
4
+ # module LocHelp
5
+ # include Eco::Data::Locations::DSL
6
+ # end
7
+ #
8
+ # nodes_list = LocHelp.csv_nodes_from(csv_list_filename)
9
+ # csv_tree = LocHelp.nodes_to_csv_tree(nodes_list)
10
+ # File.open(output_filename, 'w') { |fd| fd.write(csv_tree.to_csv) }
11
+ #
12
+ # @example of usage:
13
+ # class Foo
14
+ # include Eco::Data::Locations::DSL
15
+ #
16
+ # def csv_tree_to_csv_list(csv_tree_filename)
17
+ # csv_list = nodes_to_csv_list(csv_nodes_from(csv_tree_filename))
18
+ # File.open(output_filename, 'w') { |fd| fd.write(csv_list.to_csv) }
19
+ # end
20
+ #
21
+ # def live_tree_to_csv_tree(org_tagtree)
22
+ # File.open(output_filename, 'w') { |fd| fd.write(csv_tree(org_tagtree).to_csv) }
23
+ # end
24
+ # end
25
+ #
26
+ module DSL
27
+ class << self
28
+ def included(base)
29
+ super(base)
30
+ base.extend Eco::Data::Locations::NodeBase::Builder
31
+ base.send :include, Eco::Data::Locations::NodeBase::Builder
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,26 @@
1
+ module Eco::Data::Locations::NodeBase
2
+ module Builder
3
+ include Eco::Data::Locations::NodeBase::Parsing
4
+ include Eco::Data::Locations::NodeBase::Serial
5
+ include Eco::Data::Locations::NodeBase::CsvConvert
6
+ include Eco::Data::Locations::NodeBase::Treeify
7
+
8
+ # @param data [CSV::Table, NodeBase, Array<NodeBase>]
9
+ # @return [NodeBase::Class] the Node class we can use.
10
+ def node_class(data)
11
+ case data
12
+ when ::CSV::Table
13
+ return Eco::Data::Locations::NodePlain if Eco::Data::Locations::NodePlain.csv_matches_format?(csv)
14
+ return Eco::Data::Locations::NodeLevel if Eco::Data::Locations::NodeLevel.csv_matches_format?(csv)
15
+ when Array
16
+ return nil unless sample = data.first
17
+ node_class(sample)
18
+ when Eco::Data::Locations::NodeBase
19
+ return nil unless data.class < Eco::Data::Locations::NodeBase
20
+ data.class
21
+ else
22
+ raise ArgumentError, "Expecting CSV::Table. Given: #{csv.class}" unless csv.is_a?(::CSV::Table)
23
+ end
24
+ end
25
+ end
26
+ end