decombobulate 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 153e6f51eadf84455fdd682c8c6eb3fb986f9dfafaa833f3c60e0e491a08fc94
4
- data.tar.gz: '08e60c60fab9867131aa075177bdd7e470e628804ab51232a1a4bc259844c6ca'
3
+ metadata.gz: e84472027fcfa0174c8c7da896340b34a3c7f15f4575df81c756e6eac38ae171
4
+ data.tar.gz: 39d3ec49fb48c092b7501f7bd158c9b63272b341632b0844f299cfae7e080882
5
5
  SHA512:
6
- metadata.gz: 7ae3eba70539c1d56e98c69e190f1eeb6e4c726dcb3c0a2f390e80c163ef6cb95010bc04908b3fd274d3481de89e271db2f71bd8d14c0d697b13deaa201636a0
7
- data.tar.gz: a32b6a3f54a1afb2b939470ed1b33cf29cf04b32c4f5955552e508a512d76a24832d6f7781e8c5b86c9d06839cbed7009bc9d3d16af76eab36b9555d7faed7f2
6
+ metadata.gz: 776e3e69329510030a4b25bddecdfe247dbbb55cc8812a04fce13aa706747ca5dc29440b88ee05baf3dc8e8dec81ea9e46b00092a46f4366415768f8c02a4d91
7
+ data.tar.gz: 42f98e4aef1abc545856536e84a686b64b6f17ac9d87c320309f9293991fce17f32c200fb669eaf2c8d7c1538e4353ab91ffe8651c2f70f6f542523a3892d5a2
data/.rubocop.yml CHANGED
@@ -1,35 +1,10 @@
1
- require:
2
- - rubocop-rails
3
- - rubocop-performance
4
-
5
- inherit_gem:
6
- rubocop-rails_config:
7
- - config/rails.yml
8
-
9
1
  AllCops:
10
2
  Exclude:
11
- - db/schema.rb
12
- - 'node_modules/**/*'
13
- - 'redis-stable/**/*'
14
3
  - 'bin/**/*'
15
- - 'vendor/**/*'
4
+ - 'pkg/**/*'
5
+ - 'sig/**/*'
16
6
  TargetRubyVersion: 3.0
17
7
 
18
- # Join tables don't really need timestamps
19
- Rails/CreateTableWithTimestamps:
20
- Exclude:
21
- # - 'db/migrate/20180128231930_create_organizations_and_events.rb'
22
-
23
- # Rails generates this file
24
- Style/BlockComments:
25
- Exclude:
26
- - 'db/seeds.rb'
27
-
28
- # The schema file is autogenerated
29
- Layout/EmptyLinesAroundBlockBody:
30
- Exclude:
31
- - 'db/schema.rb'
32
-
33
8
  # This sets us to use the standard Rails format instead of Rubocop's
34
9
  # opinionated Ruby style.
35
10
  Style/FrozenStringLiteralComment:
@@ -40,11 +15,6 @@ Style/FrozenStringLiteralComment:
40
15
  Style/ClassAndModuleChildren:
41
16
  Enabled: false
42
17
 
43
- # Rails generates this file
44
- Layout/IndentationStyle:
45
- Exclude:
46
- - 'db/seeds.rb'
47
-
48
18
  # Temporarily turn this off
49
19
  Metrics/AbcSize:
50
20
  Enabled: false
@@ -58,18 +28,9 @@ Lint/RescueException:
58
28
  Lint/Debugger:
59
29
  Enabled: true
60
30
 
61
- Rails/HasManyOrHasOneDependent:
62
- Enabled: true
63
-
64
- Rails/HasAndBelongsToMany:
65
- Enabled: true
66
-
67
31
  Style/NumericPredicate:
68
32
  Enabled: true
69
33
 
70
- Rails/RefuteMethods:
71
- Enabled: false
72
-
73
34
  # This sets us to use the standard Rails format instead of Rubocop's
74
35
  # opinionated Ruby style.
75
36
  Layout/EmptyLinesAroundAccessModifier:
data/Gemfile CHANGED
@@ -5,8 +5,4 @@ source "https://rubygems.org"
5
5
  # Specify your gem's dependencies in decombobulate.gemspec
6
6
  gemspec
7
7
 
8
- gem "rake", "~> 13.0"
9
-
10
- gem "minitest", "~> 5.0"
11
-
12
- gem "rubocop", "~> 1.21"
8
+ gem "rake", "~> 13.1"
data/Gemfile.lock CHANGED
@@ -1,129 +1,63 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- decombobulate (0.1.3)
4
+ decombobulate (0.1.4)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- actionpack (7.0.7)
10
- actionview (= 7.0.7)
11
- activesupport (= 7.0.7)
12
- rack (~> 2.0, >= 2.2.4)
13
- rack-test (>= 0.6.3)
14
- rails-dom-testing (~> 2.0)
15
- rails-html-sanitizer (~> 1.0, >= 1.2.0)
16
- actionview (7.0.7)
17
- activesupport (= 7.0.7)
18
- builder (~> 3.1)
19
- erubi (~> 1.4)
20
- rails-dom-testing (~> 2.0)
21
- rails-html-sanitizer (~> 1.1, >= 1.2.0)
22
- activesupport (7.0.7)
9
+ activesupport (7.0.8)
23
10
  concurrent-ruby (~> 1.0, >= 1.0.2)
24
11
  i18n (>= 1.6, < 2)
25
12
  minitest (>= 5.1)
26
13
  tzinfo (~> 2.0)
27
14
  ast (2.4.2)
28
- base64 (0.1.1)
29
- builder (3.2.4)
30
15
  concurrent-ruby (1.2.2)
31
- crass (1.0.6)
32
- erubi (1.12.0)
33
16
  i18n (1.14.1)
34
17
  concurrent-ruby (~> 1.0)
35
- json (2.6.3)
18
+ json (2.7.0)
36
19
  language_server-protocol (3.17.0.3)
37
- loofah (2.21.3)
38
- crass (~> 1.0.2)
39
- nokogiri (>= 1.12.0)
40
- method_source (1.0.0)
41
- minitest (5.19.0)
42
- nokogiri (1.15.4-arm64-darwin)
43
- racc (~> 1.4)
44
- nokogiri (1.15.4-x86_64-linux)
45
- racc (~> 1.4)
20
+ minitest (5.20.0)
46
21
  parallel (1.23.0)
47
- parser (3.2.2.3)
22
+ parser (3.2.2.4)
48
23
  ast (~> 2.4.1)
49
24
  racc
50
- racc (1.7.1)
51
- rack (2.2.8)
52
- rack-test (2.1.0)
53
- rack (>= 1.3)
54
- rails-dom-testing (2.2.0)
55
- activesupport (>= 5.0.0)
56
- minitest
57
- nokogiri (>= 1.6)
58
- rails-html-sanitizer (1.6.0)
59
- loofah (~> 2.21)
60
- nokogiri (~> 1.14)
61
- railties (7.0.7)
62
- actionpack (= 7.0.7)
63
- activesupport (= 7.0.7)
64
- method_source
65
- rake (>= 12.2)
66
- thor (~> 1.0)
67
- zeitwerk (~> 2.5)
25
+ racc (1.7.3)
68
26
  rainbow (3.1.1)
69
- rake (13.0.6)
70
- regexp_parser (2.8.1)
27
+ rake (13.1.0)
28
+ regexp_parser (2.8.3)
71
29
  rexml (3.2.6)
72
- rubocop (1.56.0)
73
- base64 (~> 0.1.1)
30
+ rubocop (1.58.0)
74
31
  json (~> 2.3)
75
32
  language_server-protocol (>= 3.17.0)
76
33
  parallel (~> 1.10)
77
- parser (>= 3.2.2.3)
34
+ parser (>= 3.2.2.4)
78
35
  rainbow (>= 2.2.2, < 4.0)
79
36
  regexp_parser (>= 1.8, < 3.0)
80
37
  rexml (>= 3.2.5, < 4.0)
81
- rubocop-ast (>= 1.28.1, < 2.0)
38
+ rubocop-ast (>= 1.30.0, < 2.0)
82
39
  ruby-progressbar (~> 1.7)
83
40
  unicode-display_width (>= 2.4.0, < 3.0)
84
- rubocop-ast (1.29.0)
41
+ rubocop-ast (1.30.0)
85
42
  parser (>= 3.2.1.0)
86
- rubocop-md (1.2.0)
87
- rubocop (>= 1.0)
88
- rubocop-minitest (0.31.0)
89
- rubocop (>= 1.39, < 2.0)
90
- rubocop-packaging (0.5.2)
91
- rubocop (>= 1.33, < 2.0)
92
- rubocop-performance (1.19.0)
43
+ rubocop-performance (1.19.1)
93
44
  rubocop (>= 1.7.0, < 2.0)
94
45
  rubocop-ast (>= 0.4.0)
95
- rubocop-rails (2.20.2)
96
- activesupport (>= 4.2.0)
97
- rack (>= 1.1)
98
- rubocop (>= 1.33.0, < 2.0)
99
- rubocop-rails_config (1.13.0)
100
- railties (>= 5.0)
101
- rubocop (>= 1.48.0)
102
- rubocop-ast (>= 1.26.0)
103
- rubocop-md
104
- rubocop-minitest (~> 0.22)
105
- rubocop-packaging (~> 0.5)
106
- rubocop-performance (~> 1.11)
107
- rubocop-rails (~> 2.0)
108
46
  ruby-progressbar (1.13.0)
109
- thor (1.2.2)
110
47
  tzinfo (2.0.6)
111
48
  concurrent-ruby (~> 1.0)
112
- unicode-display_width (2.4.2)
113
- zeitwerk (2.6.11)
49
+ unicode-display_width (2.5.0)
114
50
 
115
51
  PLATFORMS
116
52
  arm64-darwin-22
117
- x86_64-linux
118
53
 
119
54
  DEPENDENCIES
55
+ activesupport (= 7.0.8)
120
56
  decombobulate!
121
- minitest (~> 5.0)
122
- rake (~> 13.0)
123
- rubocop (~> 1.21)
57
+ minitest
58
+ rake (~> 13.1)
59
+ rubocop
124
60
  rubocop-performance
125
- rubocop-rails
126
- rubocop-rails_config
127
61
 
128
62
  BUNDLED WITH
129
- 2.4.9
63
+ 2.4.22
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Decombobulate
4
- VERSION = "0.1.3"
4
+ VERSION = "0.1.4"
5
5
  end
data/lib/decombobulate.rb CHANGED
@@ -3,28 +3,42 @@
3
3
  require_relative "decombobulate/version"
4
4
  require "json"
5
5
  require "csv"
6
+ require "debug"
6
7
 
7
8
  class Decombobulate
8
9
  class Error < StandardError; end
9
10
 
10
- attr_reader :headers, :rows
11
+ attr_reader :rows, :headers
11
12
 
12
13
  def initialize(json)
13
14
  # Parse the JSON if we need to
14
15
  json = Json.parse(json) if json.is_a?(String)
15
16
 
16
-
17
17
  # Yes, I tried a case/when statement but for some reason that doesn't work when looking at class types
18
- if json.is_a?(Array)
19
- @headers = parse_headers_from_json(json.first)
20
- @rows = json.map do |json_row|
21
- collapse_json_object_row_to_csv_row(json_row)
18
+ columns = json_to_csv(json)
19
+
20
+ @rows = []
21
+ if columns.is_a?(Array)
22
+ merged_columns = {}
23
+
24
+ # Iterate through the array, finding each key, adding it to the headers row if it's not there already
25
+ # then merge them
26
+
27
+ columns.each do |entry|
28
+ entry.keys.each do |key|
29
+ merged_columns[key] = [] unless merged_columns.has_key?(key)
30
+ merged_columns[key] << entry[key]
31
+ merged_columns[key] = merged_columns[key].flatten
32
+ end
22
33
  end
23
- elsif json.is_a?(Hash)
24
- @headers = parse_headers_from_json(json)
25
- @rows = [collapse_json_object_row_to_csv_row(json)]
26
- else
27
- raise "JSON object must have an array or object as the top level object."
34
+ columns = merged_columns
35
+ end
36
+ # 6.) Now we have a hash of @columns, we need to turn it into rows
37
+ # We're going to assume that all the @columns are the same length
38
+ # So we'll just iterate over the first column and add the values to the row
39
+ @headers = columns.keys.map(&:to_s)
40
+ columns[columns.keys.first].size.times do |index|
41
+ @rows << columns.keys.map { |key| columns[key][index] }
28
42
  end
29
43
  end
30
44
 
@@ -50,54 +64,169 @@ class Decombobulate
50
64
  csv_final
51
65
  end
52
66
 
53
- def parse_headers_from_json(json, parent_key = nil)
54
- @headers ||= []
67
+ private
55
68
 
56
- if json.is_a?(Hash)
57
- json.keys.each do |key|
58
- flattened_key = parent_key.nil? ? key.to_s : "#{parent_key}.#{key}"
69
+ def json_to_csv(json = {}, nested_name = nil, depth: 0)
70
+ @columns ||= {}
71
+ unless json.is_a?(Hash) || json.is_a?(Array)
72
+ @columns[nested_name] = [] unless @columns.has_key?(nested_name)
73
+ @columns[nested_name] << json
74
+ return @columns
75
+ end
59
76
 
60
- if json[key].is_a?(Hash) || json[key].is_a?(Array)
61
- parse_headers_from_json(json[key], flattened_key)
62
- else
63
- @headers << flattened_key
64
- end
77
+ if json.is_a?(Array)
78
+ json.each do |j|
79
+ json_to_csv(j, depth: depth + 1)
65
80
  end
66
- elsif json.is_a?(Array)
67
- json.each do |j_object|
68
- parse_headers_from_json(j_object, parent_key)
81
+ return @columns
82
+ # return @columns
83
+ end
84
+ # OK, so we need arbitrarily long fields so we're doing a different algorithm I literally came up
85
+ # with in five minutes one morning before falling back asleep, let's see if it works.
86
+ #
87
+ # Basically we're going to represent the @columns separately, and hope the row line up correctly
88
+ # This allows us to add new @columns and fill up the empty fields as needed
89
+ #
90
+ # 1. Make a hash of arrays
91
+ # This is already done in the function call
92
+
93
+ # 2.) Add all the keys in the JSON at the level to the hash
94
+ json.keys.each do |key|
95
+ column_name = nested_name.nil? ? key : "#{nested_name}.#{key}".to_sym
96
+ @columns[column_name] = [] unless @columns.has_key?(column_name)
97
+ end
98
+
99
+ # 3.) Go through each key, if it's a bare value just add it in
100
+ json.keys.each do |key|
101
+ value = json[key]
102
+ # 4.) If it's an array, start adding @columns in the format "key_1" "key_2" for the length of the array
103
+ if value.is_a?(Array)
104
+ column_name = nested_name.nil? ? key : "#{nested_name}.#{key}".to_sym
105
+ # We want to get rid of the key if it's an array, since we're going to be adding @columns
106
+ @columns.delete(column_name)
107
+
108
+ # Now add the values to the @columns
109
+ value.each_with_index do |array_value, index|
110
+ column_name = nested_name.nil? ? "#{key}.#{index + 1}".to_sym : "#{nested_name}.#{key}.#{index + 1}".to_sym
111
+ json_to_csv(array_value, column_name, depth: depth + 1)
112
+ end
113
+ elsif value.is_a?(Hash)
114
+ # 5.) If it's a hash, recursively call this function and add the @columns to the hash
115
+ column_name = nested_name.nil? ? key : "#{nested_name}.#{key}".to_sym
116
+ @columns.delete(column_name)
117
+ json_to_csv(value, column_name, depth: depth + 1)
118
+ else
119
+ column_name = nested_name.nil? ? key.to_sym : "#{nested_name}.#{key}".to_sym
120
+ @columns[column_name] = [] unless @columns.has_key?(column_name) || !@columns[column_name].nil?
121
+
122
+ # Fill the column with nils up to the current length of the other @columns
123
+ # get the longest column size
124
+ begin
125
+ longest_column_size = @columns.values.map(&:size).max
126
+ rescue StandardError => e
127
+ debugger
128
+ end
129
+
130
+
131
+ while @columns[column_name].size < longest_column_size - 1
132
+ @columns[column_name] << nil
133
+ end if longest_column_size > 1
134
+
135
+ @columns[column_name] << value
69
136
  end
70
- else
71
- # This mean's we either have a parsing error or we're in an array.
72
- # I'm assuming the JSON coming in is valid, so we're going to just skip this
73
- return nil
74
137
  end
75
138
 
76
- @headers
139
+ @columns = group_columns(@columns)
77
140
  end
78
141
 
79
- def collapse_json_object_row_to_csv_row(json)
80
- # Time to do a graph traversal! And recursively! Java 103 is usefuL!!!!
81
- row_array = []
142
+ def group_columns(columns)
143
+ grouped_keys_array = group_keys(columns.keys)
144
+
145
+ reordered_columns = {}
146
+ grouped_keys_array.each do |key|
147
+ # Try to find the key using the string or symbol version
148
+ # First convert to string because we don't know what type this is
149
+ key = key.to_s
150
+ key = key.to_sym unless columns.has_key?(key)
151
+ new_key = key.to_sym
152
+ reordered_columns[new_key] = columns[key]
153
+ end
82
154
 
83
- if json.is_a?(Array)
84
- json.each do |j_object|
85
- if j_object.is_a?(Array) || j_object.is_a?(Hash)
86
- row_array << collapse_json_object_row_to_csv_row(j_object)
87
- else
88
- row_array << j_object
155
+ puts "reordered: #{reordered_columns}"
156
+
157
+ reordered_columns
158
+ end
159
+
160
+ def group_keys(keys = [], previous_key = nil)
161
+ # Arrange the columns so that they're grouped as nested in the json
162
+ ordered_keys = []
163
+
164
+ keys.each do |key|
165
+ key_path = key.to_s.split(".")
166
+ if key_path.size > 1
167
+ # Grab all keys that include the same first part
168
+ subcolumns = []
169
+ # get the keys and call recursively
170
+
171
+ keys.each do |k|
172
+ k_path = k.to_s.split(".")
173
+ if k_path[0] == key_path[0]
174
+ subcolumns << k_path[1..-1].join(".")
175
+ end
89
176
  end
90
- end
91
- elsif json.is_a?(Hash)
92
- json.keys.each do |key|
93
- if json[key].is_a?(Array) || json[key].is_a?(Hash)
94
- row_array << collapse_json_object_row_to_csv_row(json[key])
177
+
178
+ # Since we don't want to sort the same top we need to remove the others
179
+ for_recursion = subcolumns.map do |column_name|
180
+ column_name.to_sym
181
+ end
182
+
183
+ for_recursion.each do |key_to_delete|
184
+ keys.delete("#{key_path[0]}.#{key_to_delete}".to_sym)
185
+ end
186
+
187
+ grouped_keys = group_keys(for_recursion, key_path[0])
188
+
189
+ if previous_key.nil?
190
+ ordered_keys << grouped_keys
95
191
  else
96
- row_array << json[key]
192
+ ordered_keys << grouped_keys.map do |subkey|
193
+ "#{previous_key}.#{subkey}".to_sym
194
+ end
97
195
  end
196
+
197
+ else
198
+ to_add = previous_key.nil? ? key : "#{previous_key}.#{key}"
199
+ next if to_add.end_with?(".")
200
+
201
+ ordered_keys << to_add
98
202
  end
99
203
  end
100
-
101
- row_array.flatten
204
+ ordered_keys.flatten.map(&:to_sym)
102
205
  end
103
206
  end
207
+
208
+
209
+
210
+
211
+
212
+
213
+
214
+
215
+
216
+
217
+
218
+
219
+
220
+
221
+
222
+
223
+
224
+
225
+
226
+
227
+
228
+
229
+
230
+
231
+
232
+
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: decombobulate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christopher Guess
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-08-15 00:00:00.000000000 Z
11
+ date: 2023-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubocop
@@ -25,7 +25,7 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rubocop-rails
28
+ name: rubocop-performance
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rubocop-performance
42
+ name: minitest
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
@@ -53,19 +53,19 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: rubocop-rails_config
56
+ name: activesupport
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - '='
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: 7.0.8
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - '='
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: 7.0.8
69
69
  description: Convert JSON structures to CSVs
70
70
  email:
71
71
  - cguess@gmail.com
@@ -106,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
106
  - !ruby/object:Gem::Version
107
107
  version: '0'
108
108
  requirements: []
109
- rubygems_version: 3.4.10
109
+ rubygems_version: 3.4.22
110
110
  signing_key:
111
111
  specification_version: 4
112
112
  summary: Convert JSON structures to CSVs