hash_math 1.0.0 → 1.1.0

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: f7a08cd91f3734e6acb87f0bc2904be2501b6aa0e8ca07ee8d90e5dd8fe8bd17
4
- data.tar.gz: 065d4819ce940caa4e7f7a9fb5132a8643bccdc363985292aa0fed5b5e68bf77
3
+ metadata.gz: 7df04a330a0de007fb2dba40e7deb6b835ad5aebdf0be39dd596c4af6ee52715
4
+ data.tar.gz: b0277b0818de7dcb5f59a82e46e761bc6afb16c7f74ed3fc5390b9cd6addd6bc
5
5
  SHA512:
6
- metadata.gz: 9943d5a576a015330cbc315ae84b804f32ad2d24981c6e555e3fd4728000b2731ceedf40075a17de883cefce025e837f3b94e7224f3ac671232b98596d43e044
7
- data.tar.gz: 4364b9f176d404f6e0538420c3786a36adb0af260e55ba735e3b4a8aaaa1884dc4e64fc1590b529883278e07c7cebf1a6694047c72b333e79e52df58b0854b03
6
+ metadata.gz: 73f27b9a0746d605e7471aa14f4b1203894c72064555ed19303acc2e2c4e18169e8365daf20aeb299afc5b3faef9ec0552af04bab8b73fdc59bf9beb8baa2bd8
7
+ data.tar.gz: bc0eb13e08c690f7506ad413ad4fe1a3d22698cbe8f313efc1af8d62546ca1c0ab2cea4b0ba55f44758661688d9da6e3ff11931aca88a775226bcfb26758315b
@@ -1,4 +1,8 @@
1
- Metrics/LineLength:
1
+ AllCops:
2
+ TargetRubyVersion: 2.5
3
+ NewCops: enable
4
+
5
+ Layout/LineLength:
2
6
  Max: 100
3
7
 
4
8
  Metrics/BlockLength:
@@ -13,9 +17,6 @@ Metrics/BlockLength:
13
17
  Metrics/MethodLength:
14
18
  Max: 25
15
19
 
16
- AllCops:
17
- TargetRubyVersion: 2.3
18
-
19
20
  Metrics/AbcSize:
20
21
  Max: 16
21
22
 
@@ -1 +1 @@
1
- 2.6.3
1
+ 2.6.6
@@ -4,10 +4,9 @@ env:
4
4
  language: ruby
5
5
  rvm:
6
6
  # Build on the latest stable of all supported Rubies (https://www.ruby-lang.org/en/downloads/):
7
- - 2.3.8
8
- - 2.4.6
9
- - 2.5.5
10
- - 2.6.3
7
+ - 2.5.8
8
+ - 2.6.6
9
+ - 2.7.1
11
10
  cache: bundler
12
11
  before_script:
13
12
  - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
@@ -1,3 +1,17 @@
1
+ # 1.1.0 (August 13t, 2020)
2
+
3
+ Adding:
4
+
5
+ * HashMath#Unpivot class and implementation.
6
+
7
+ Removing:
8
+
9
+ * Support for Ruby < 2.5
10
+
11
+ # 1.0.0 (September 18th, 2019)
12
+
13
+ Initial release.
14
+
1
15
  # 1.0.0-alpha (September 16th, 2019)
2
16
 
3
17
  Added initial implementation of:
data/README.md CHANGED
@@ -179,6 +179,57 @@ Notes:
179
179
  * `#add` will throw a KeyOutOfBoundsError if the key is not found.
180
180
  * key is type-sensitive and works just like Hash keys work.
181
181
 
182
+ ### Unpivot: Hash Key Coalescence and Row Extrapolation
183
+
184
+ Sometimes the expected interface is column-based, but the actual data store is row-based. This causes an impedance between the input set and the persistable data set. HashMath::Unpivot has the ability to extrapolate one hash (row) into multiple hashes (rows) while unpivoting specific keys into key-value pairs.
185
+
186
+ For example: say we have a database table persisting key-value pairs of patient attributes:
187
+
188
+ patient_id | field | value
189
+ ---------- | --------------- | ----------
190
+ 2 | first_exam_date | 2020-01-03
191
+ 2 | last_exam_date | 2020-04-05
192
+ 2 | consent_date | 2020-01-02
193
+
194
+ But our input data looks like this:
195
+
196
+ ````ruby
197
+ patient = {
198
+ patient_id: 2,
199
+ first_exam_date: '2020-01-03',
200
+ last_exam_date: '2020-04-05',
201
+ consent_date: '2020-01-02'
202
+ }
203
+ ````
204
+
205
+ We could use a HashMath::Unpivot to go from one hash to three hashes:
206
+
207
+ ````ruby
208
+ pivot_set = {
209
+ pivots: [
210
+ {
211
+ keys: %i[first_exam_date last_exam_date consent_date],
212
+ coalesce_key: :field,
213
+ coalesce_key_value: :value
214
+ }
215
+ ]
216
+ }
217
+
218
+ rows = HashMath::Unpivot.new(pivot_set).perform(patient)
219
+ ````
220
+
221
+ The `rows` variable should now be equivalent to:
222
+
223
+ ````ruby
224
+ [
225
+ { patient_id: 2, field: :first_exam_date, value: '2020-01-03' },
226
+ { patient_id: 2, field: :last_exam_date, value: '2020-04-05' },
227
+ { patient_id: 2, field: :consent_date, value: '2020-01-02' }
228
+ ]
229
+ ````
230
+
231
+ Note: `HashMath::Unpivot#add` also exists to update an already instantiated object with new pivot_sets.
232
+
182
233
  ## Contributing
183
234
 
184
235
  ### Development Environment Configuration
@@ -19,13 +19,16 @@ Gem::Specification.new do |s|
19
19
  s.homepage = 'https://github.com/bluemarblepayroll/hash_math'
20
20
  s.license = 'MIT'
21
21
 
22
- s.required_ruby_version = '>= 2.3.8'
22
+ s.required_ruby_version = '>= 2.5'
23
+
24
+ s.add_dependency('acts_as_hashable', '~>1')
23
25
 
24
26
  s.add_development_dependency('guard-rspec', '~>4.7')
25
27
  s.add_development_dependency('pry', '~>0')
26
- s.add_development_dependency('rake', '~> 12')
28
+ s.add_development_dependency('pry-byebug', '~>3')
29
+ s.add_development_dependency('rake', '~>13.0')
27
30
  s.add_development_dependency('rspec')
28
- s.add_development_dependency('rubocop', '~>0.74.0')
29
- s.add_development_dependency('simplecov', '~>0.17.0')
30
- s.add_development_dependency('simplecov-console', '~>0.5.0')
31
+ s.add_development_dependency('rubocop', '~>0.88.0')
32
+ s.add_development_dependency('simplecov', '~>0.18.5')
33
+ s.add_development_dependency('simplecov-console', '~>0.7.0')
31
34
  end
@@ -7,9 +7,12 @@
7
7
  # LICENSE file in the root directory of this source tree.
8
8
  #
9
9
 
10
+ require 'acts_as_hashable'
11
+
10
12
  require_relative 'hash_math/matrix'
11
13
  require_relative 'hash_math/record'
12
14
  require_relative 'hash_math/table'
15
+ require_relative 'hash_math/unpivot'
13
16
 
14
17
  # Top-level namespace
15
18
  module HashMath
@@ -40,8 +40,12 @@ module HashMath
40
40
  pairs_by_key[key] ||= KeyValuePair.new(key)
41
41
  end
42
42
 
43
+ def make_pair_groups
44
+ pairs_by_key.values.map(&:pairs)
45
+ end
46
+
43
47
  def pair_products
44
- pair_groups = pairs_by_key.values.map(&:pairs)
48
+ pair_groups = make_pair_groups
45
49
 
46
50
  products = pair_groups.inject(pair_groups.shift) { |memo, f| memo.product(f) }
47
51
  &.map { |f| f.is_a?(KeyValuePair::Pair) ? [f] : f.flatten } || []
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2019-present, Blue Marble Payroll, LLC
5
+ #
6
+ # This source code is licensed under the MIT license found in the
7
+ # LICENSE file in the root directory of this source tree.
8
+ #
9
+
10
+ require_relative 'unpivot/pivot_set'
11
+
12
+ module HashMath
13
+ # This class has the ability to extrapolate one hash (row) into multiple hashes (rows) while
14
+ # unpivoting specific keys into key-value pairs.
15
+ class Unpivot
16
+ acts_as_hashable
17
+ extend Forwardable
18
+
19
+ attr_reader :pivot_set
20
+
21
+ def_delegators :pivot_set, :add
22
+
23
+ def initialize(pivot_set = PivotSet.new)
24
+ @pivot_set = PivotSet.make(pivot_set, nullable: false)
25
+
26
+ freeze
27
+ end
28
+
29
+ # The main method for this class that performs the un-pivoting and hash expansion.
30
+ # Pass in a hash and it will return an array of hashes.
31
+ def expand(hash)
32
+ return [hash] unless pivot_set.any?
33
+
34
+ all_combinations = pivot_set.expand(hash)
35
+
36
+ products = all_combinations.inject(all_combinations.shift) do |memo, array|
37
+ memo.product(array)
38
+ end
39
+
40
+ recombine(products)
41
+ end
42
+
43
+ private
44
+
45
+ def recombine(products)
46
+ products.map do |pairs|
47
+ if pairs.is_a?(Array)
48
+ pairs.inject(pairs.shift) { |memo, p| memo.merge(p) }
49
+ else
50
+ pairs
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2019-present, Blue Marble Payroll, LLC
5
+ #
6
+ # This source code is licensed under the MIT license found in the
7
+ # LICENSE file in the root directory of this source tree.
8
+ #
9
+
10
+ module HashMath
11
+ class Unpivot
12
+ # A single pivot definition consists of which columns to coalesce and to where.
13
+ class Pivot
14
+ acts_as_hashable
15
+
16
+ attr_reader :coalesce_key,
17
+ :coalesce_key_value,
18
+ :keys
19
+
20
+ # keys is an array of keys to include in the un-pivoting.
21
+ # coalesce_key is the new key to use.
22
+ # coalesce_key_value is the new key to use for its corresponding values.
23
+ def initialize(keys:, coalesce_key:, coalesce_key_value:)
24
+ @keys = Array(keys)
25
+ @coalesce_key = coalesce_key
26
+ @coalesce_key_value = coalesce_key_value
27
+
28
+ freeze
29
+ end
30
+
31
+ # The most rudimentary portion of the Unpivoting algorithm, this method works on
32
+ # just one pivot and returns the extrapolated, un-pivoted rows.
33
+ # Takes two hashes as input:
34
+ # the first will serve as the prototype for each returned hash
35
+ # the second will be one to use for value extraction.
36
+ # Returns an array of hashes.
37
+ def expand(base_hash, value_hash)
38
+ keys.map do |key|
39
+ base_hash.merge(
40
+ coalesce_key => key,
41
+ coalesce_key_value => value_hash[key]
42
+ )
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2019-present, Blue Marble Payroll, LLC
5
+ #
6
+ # This source code is licensed under the MIT license found in the
7
+ # LICENSE file in the root directory of this source tree.
8
+ #
9
+
10
+ require_relative 'pivot'
11
+
12
+ module HashMath
13
+ class Unpivot
14
+ # A set of pivots for an Unpivot class to perform.
15
+ class PivotSet
16
+ acts_as_hashable
17
+ extend Forwardable
18
+
19
+ attr_reader :pivots
20
+
21
+ def_delegators :pivots, :any?
22
+
23
+ def initialize(pivots: [])
24
+ @pivots = Pivot.array(pivots)
25
+ end
26
+
27
+ # Adds another Pivot configuration object to this objects list of pivots.
28
+ # Returns self.
29
+ def add(pivot)
30
+ tap { pivots << Pivot.make(pivot) }
31
+ end
32
+
33
+ # An aggregation of Pivot#expand. This method will iterate over all pivots
34
+ # and expand them all out.
35
+ def expand(hash)
36
+ base_hash = make_base_hash(hash)
37
+
38
+ pivots.map { |pivot| pivot.expand(base_hash, hash) }
39
+ end
40
+
41
+ private
42
+
43
+ def make_base_hash(hash)
44
+ keys_to_remove = key_set
45
+
46
+ hash.reject { |k, _v| keys_to_remove.include?(k) }
47
+ end
48
+
49
+ def key_set
50
+ pivots.flat_map(&:keys).to_set
51
+ end
52
+ end
53
+ end
54
+ end
@@ -8,5 +8,5 @@
8
8
  #
9
9
 
10
10
  module HashMath
11
- VERSION = '1.0.0'
11
+ VERSION = '1.1.0'
12
12
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2019-present, Blue Marble Payroll, LLC
5
+ #
6
+ # This source code is licensed under the MIT license found in the
7
+ # LICENSE file in the root directory of this source tree.
8
+ #
9
+
10
+ require 'spec_helper'
11
+
12
+ describe HashMath::Unpivot::Pivot do
13
+ let(:config) do
14
+ {
15
+ keys: %i[first_exam_date last_exam_date consent_date],
16
+ coalesce_key: :field,
17
+ coalesce_key_value: :value
18
+ }
19
+ end
20
+
21
+ let(:patient) do
22
+ {
23
+ patient_id: 2,
24
+ first_exam_date: '2020-01-03',
25
+ last_exam_date: '2020-04-05',
26
+ consent_date: '2020-01-02'
27
+ }
28
+ end
29
+
30
+ subject { described_class.make(config) }
31
+
32
+ describe '#expand' do
33
+ it 'performs a single un-pivoting' do
34
+ actual = subject.expand({}, patient)
35
+
36
+ expected = [
37
+ {
38
+ field: :first_exam_date,
39
+ value: '2020-01-03'
40
+ },
41
+ {
42
+ field: :last_exam_date,
43
+ value: '2020-04-05'
44
+ },
45
+ {
46
+ field: :consent_date, value: '2020-01-02'
47
+ }
48
+ ]
49
+
50
+ expect(actual).to match_array(expected)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Copyright (c) 2019-present, Blue Marble Payroll, LLC
5
+ #
6
+ # This source code is licensed under the MIT license found in the
7
+ # LICENSE file in the root directory of this source tree.
8
+ #
9
+
10
+ require 'spec_helper'
11
+
12
+ describe HashMath::Unpivot do
13
+ let(:a_and_b_pivot) do
14
+ {
15
+ keys: %i[a b],
16
+ coalesce_key: :ab_key,
17
+ coalesce_key_value: :ab_value
18
+ }
19
+ end
20
+
21
+ let(:cd_and_e_pivot) do
22
+ {
23
+ keys: %i[c d e],
24
+ coalesce_key: :cde_key,
25
+ coalesce_key_value: :cde_value
26
+ }
27
+ end
28
+
29
+ let(:hash) do
30
+ { a: 1, b: 2, c: 3, d: 4, e: 5 }
31
+ end
32
+
33
+ context 'with no pivots' do
34
+ it 'returns the inputted hash only' do
35
+ actual = subject.expand(hash)
36
+
37
+ expect(actual).to match_array([hash])
38
+ end
39
+ end
40
+
41
+ context 'with nil inputted' do
42
+ it 'returns array with nil' do
43
+ actual = subject.expand(nil)
44
+
45
+ expect(actual).to match_array([nil])
46
+ end
47
+ end
48
+
49
+ context 'with one pivot' do
50
+ it 'coalesces two columns into two rows' do
51
+ subject.add(**a_and_b_pivot)
52
+
53
+ expected = [
54
+ { ab_key: :a, ab_value: 1, c: 3, d: 4, e: 5 },
55
+ { ab_key: :b, ab_value: 2, c: 3, d: 4, e: 5 }
56
+ ]
57
+
58
+ actual = subject.expand(hash)
59
+
60
+ expect(actual).to eq(expected)
61
+ end
62
+ end
63
+
64
+ context 'with two pivots' do
65
+ it 'coalesces five columns into six rows' do
66
+ subject.add(**a_and_b_pivot).add(**cd_and_e_pivot)
67
+
68
+ expected = [
69
+ { ab_key: :a, ab_value: 1, cde_key: :c, cde_value: 3 },
70
+ { ab_key: :b, ab_value: 2, cde_key: :c, cde_value: 3 },
71
+ { ab_key: :a, ab_value: 1, cde_key: :d, cde_value: 4 },
72
+ { ab_key: :b, ab_value: 2, cde_key: :d, cde_value: 4 },
73
+ { ab_key: :a, ab_value: 1, cde_key: :e, cde_value: 5 },
74
+ { ab_key: :b, ab_value: 2, cde_key: :e, cde_value: 5 }
75
+ ]
76
+
77
+ actual = subject.expand(hash)
78
+
79
+ expect(actual).to match_array expected
80
+ end
81
+ end
82
+
83
+ describe 'README examples' do
84
+ context 'single patient row and one pivot example' do
85
+ let(:patient) do
86
+ {
87
+ patient_id: 2,
88
+ first_exam_date: '2020-01-03',
89
+ last_exam_date: '2020-04-05',
90
+ consent_date: '2020-01-02'
91
+ }
92
+ end
93
+
94
+ let(:pivot_set) do
95
+ {
96
+ pivots: [
97
+ {
98
+ keys: %i[first_exam_date last_exam_date consent_date],
99
+ coalesce_key: :field,
100
+ coalesce_key_value: :value
101
+ }
102
+ ]
103
+ }
104
+ end
105
+
106
+ subject { HashMath::Unpivot.new(pivot_set) }
107
+
108
+ it 'extrapolates one row into N rows (one per column key)' do
109
+ expected = [
110
+ { patient_id: 2, field: :first_exam_date, value: '2020-01-03' },
111
+ { patient_id: 2, field: :last_exam_date, value: '2020-04-05' },
112
+ { patient_id: 2, field: :consent_date, value: '2020-01-02' }
113
+ ]
114
+
115
+ actual = subject.expand(patient)
116
+
117
+ expect(actual).to match_array(expected)
118
+ end
119
+ end
120
+ end
121
+ end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hash_math
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew Ruggio
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-18 00:00:00.000000000 Z
11
+ date: 2020-08-13 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: acts_as_hashable
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: guard-rspec
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -38,20 +52,34 @@ dependencies:
38
52
  - - "~>"
39
53
  - !ruby/object:Gem::Version
40
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry-byebug
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3'
41
69
  - !ruby/object:Gem::Dependency
42
70
  name: rake
43
71
  requirement: !ruby/object:Gem::Requirement
44
72
  requirements:
45
73
  - - "~>"
46
74
  - !ruby/object:Gem::Version
47
- version: '12'
75
+ version: '13.0'
48
76
  type: :development
49
77
  prerelease: false
50
78
  version_requirements: !ruby/object:Gem::Requirement
51
79
  requirements:
52
80
  - - "~>"
53
81
  - !ruby/object:Gem::Version
54
- version: '12'
82
+ version: '13.0'
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: rspec
57
85
  requirement: !ruby/object:Gem::Requirement
@@ -72,42 +100,42 @@ dependencies:
72
100
  requirements:
73
101
  - - "~>"
74
102
  - !ruby/object:Gem::Version
75
- version: 0.74.0
103
+ version: 0.88.0
76
104
  type: :development
77
105
  prerelease: false
78
106
  version_requirements: !ruby/object:Gem::Requirement
79
107
  requirements:
80
108
  - - "~>"
81
109
  - !ruby/object:Gem::Version
82
- version: 0.74.0
110
+ version: 0.88.0
83
111
  - !ruby/object:Gem::Dependency
84
112
  name: simplecov
85
113
  requirement: !ruby/object:Gem::Requirement
86
114
  requirements:
87
115
  - - "~>"
88
116
  - !ruby/object:Gem::Version
89
- version: 0.17.0
117
+ version: 0.18.5
90
118
  type: :development
91
119
  prerelease: false
92
120
  version_requirements: !ruby/object:Gem::Requirement
93
121
  requirements:
94
122
  - - "~>"
95
123
  - !ruby/object:Gem::Version
96
- version: 0.17.0
124
+ version: 0.18.5
97
125
  - !ruby/object:Gem::Dependency
98
126
  name: simplecov-console
99
127
  requirement: !ruby/object:Gem::Requirement
100
128
  requirements:
101
129
  - - "~>"
102
130
  - !ruby/object:Gem::Version
103
- version: 0.5.0
131
+ version: 0.7.0
104
132
  type: :development
105
133
  prerelease: false
106
134
  version_requirements: !ruby/object:Gem::Requirement
107
135
  requirements:
108
136
  - - "~>"
109
137
  - !ruby/object:Gem::Version
110
- version: 0.5.0
138
+ version: 0.7.0
111
139
  description: " This library offers general purpose higher-level data structures
112
140
  that focus on Hash manipulation.\n"
113
141
  email:
@@ -136,10 +164,15 @@ files:
136
164
  - lib/hash_math/matrix/key_value_pair.rb
137
165
  - lib/hash_math/record.rb
138
166
  - lib/hash_math/table.rb
167
+ - lib/hash_math/unpivot.rb
168
+ - lib/hash_math/unpivot/pivot.rb
169
+ - lib/hash_math/unpivot/pivot_set.rb
139
170
  - lib/hash_math/version.rb
140
171
  - spec/hash_math/matrix_spec.rb
141
172
  - spec/hash_math/record_spec.rb
142
173
  - spec/hash_math/table_spec.rb
174
+ - spec/hash_math/unpivot/pivot_spec.rb
175
+ - spec/hash_math/unpivot_spec.rb
143
176
  - spec/spec_helper.rb
144
177
  homepage: https://github.com/bluemarblepayroll/hash_math
145
178
  licenses:
@@ -153,7 +186,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
153
186
  requirements:
154
187
  - - ">="
155
188
  - !ruby/object:Gem::Version
156
- version: 2.3.8
189
+ version: '2.5'
157
190
  required_rubygems_version: !ruby/object:Gem::Requirement
158
191
  requirements:
159
192
  - - ">="
@@ -168,4 +201,6 @@ test_files:
168
201
  - spec/hash_math/matrix_spec.rb
169
202
  - spec/hash_math/record_spec.rb
170
203
  - spec/hash_math/table_spec.rb
204
+ - spec/hash_math/unpivot/pivot_spec.rb
205
+ - spec/hash_math/unpivot_spec.rb
171
206
  - spec/spec_helper.rb