hash_math 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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