hash_mapper 0.2.3 → 0.2.7

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
- SHA1:
3
- metadata.gz: 629554ad3f54ff8ab3a4f97857cca331dcfa254d
4
- data.tar.gz: 6027744023d385caaf2ead1c37a2c9e0ef68bcf4
2
+ SHA256:
3
+ metadata.gz: e16c3b3b73cba0fc400759652542dfe19c0f5516f04c0e7ae83ddd07c46eb549
4
+ data.tar.gz: cf6abbd34bd06c262ecff7db0333052e99064cdf74d6d46afd83328d4b46c008
5
5
  SHA512:
6
- metadata.gz: 12d54cfb68716383dc3628a2b07e724d315c881b58938f7a5baa22edbc6f48ad05ba1e0d80b8f31ef34ac3cd1fa2ccd903dad60e91d75cc93a7d01ce6b5e712b
7
- data.tar.gz: 6e63b73160cab1344e1e00afe4643082c1048566a79d274f70c45f69bb4482ca883c1fd1c755757037c87bbc66d9a6ce5604c522fa8239498473ed7e30a783d7
6
+ metadata.gz: cad09d8c2835a211c74e70d4556415942467dcf7c333f8e0703948b1febb786fd6a5cb54e2e211c459e41c6a3807aacffb734bafec7bc1b54c93c6c89b4c0057
7
+ data.tar.gz: 7d22e3ef4ed97d6d6d4af226e78f07b1ab8e5ed7b6be63d9145a52e32c4f2551a2becc08399bca65bded5bbb7b7013e9fd218776b60dd2b9323aa0d25e69012f
data/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  ## DESCRIPTION:
8
8
 
9
9
  Maps values from hashes with different structures and/or key names. Ideal for normalizing arbitrary data to be consumed by your applications, or to prepare your data for different display formats (ie. json).
10
-
10
+
11
11
  Tiny module that allows you to easily adapt from one hash structure to another with a simple declarative DSL.
12
12
 
13
13
  ## FEATURES/PROBLEMS:
@@ -61,7 +61,7 @@ You can use HashMapper in your own little hash-like objects:
61
61
  class NiceHash
62
62
  include Enumerable
63
63
  extend HashMapper
64
-
64
+
65
65
  map from('/names/first'), to('/first_name')
66
66
  map from('/names/last'), to('/last_name')
67
67
 
@@ -89,7 +89,7 @@ end
89
89
 
90
90
  #### Coercing values
91
91
 
92
- You want to make sure an incoming value gets converted to a certain type, so
92
+ You want to make sure an incoming value gets converted to a certain type, so
93
93
 
94
94
  ```ruby
95
95
  {'one' => '1', 'two' => '2'}
@@ -97,7 +97,7 @@ You want to make sure an incoming value gets converted to a certain type, so
97
97
 
98
98
  gets translated to
99
99
 
100
- ```ruby`
100
+ ```ruby
101
101
  {:one => 1, :two => 2}
102
102
  ```
103
103
 
@@ -165,9 +165,9 @@ output = NameMapper.normalize(input) # => {:first_name => 'Mark', :last_name =>
165
165
 
166
166
  NameMapper.denormalize(output) # => input
167
167
  ```
168
-
168
+
169
169
  This will work with your block filters and even nested mappers (see below).
170
-
170
+
171
171
  ### Advanced usage
172
172
  #### Array access
173
173
  You want:
@@ -252,7 +252,7 @@ end
252
252
 
253
253
  But HashMapper's nested mappers will actually do that for you if a value is an array, so:
254
254
 
255
- ```ruby
255
+ ```ruby
256
256
  map from('/employees'), to('employees'), using: UserMapper
257
257
  ```
258
258
  ... Will map each employee using UserMapper.
@@ -268,12 +268,12 @@ They all yield a block with 2 arguments - the hash you are mapping from and the
268
268
  ```ruby
269
269
  class EggMapper
270
270
  map from('/raw'), to('/fried')
271
-
271
+
272
272
  before_normalize do |input, output|
273
- input['raw'] ||= 'please' # this will give 'raw' a default value
273
+ input['raw'] ||= 'please' # this will give 'raw' a default value
274
274
  input
275
275
  end
276
-
276
+
277
277
  after_denormalize do |input, output|
278
278
  output.to_a # the denormalized object will now be an array, not a hash!!
279
279
  end
@@ -290,23 +290,23 @@ You can pass one extra argument to before and after filters if you need to:
290
290
  ```ruby
291
291
  class EggMapper
292
292
  map from('/raw'), to('/fried')
293
-
293
+
294
294
  before_normalize do |input, output, opts|
295
- input['raw'] ||= 'please' unless opts[:no_default] # this will give 'raw' a default value
295
+ input['raw'] ||= 'please' unless opts[:no_default] # this will give 'raw' a default value
296
296
  input
297
297
  end
298
-
298
+
299
299
  after_denormalize do |input, output, opts|
300
300
  output.to_a # the denormalized object will now be an array, not a hash!!
301
301
  end
302
302
 
303
303
  end
304
304
 
305
- EggMapper.normalize({}, no_default: true)
305
+ EggMapper.normalize({}, options: { no_default: true })
306
306
  EggMapper.denormalize({fried: 4})
307
307
  ```
308
308
 
309
-
309
+
310
310
  ## REQUIREMENTS:
311
311
 
312
312
  ## TODO:
data/hash_mapper.gemspec CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
8
8
  s.authors = ['Ismael Celis']
9
9
  s.description = %q{Tiny module that allows you to easily adapt from one hash structure to another with a simple declarative DSL.}
10
10
  s.email = %q{ismaelct@gmail.com}
11
-
11
+
12
12
  s.files = `git ls-files`.split("\n")
13
13
  s.homepage = %q{http://github.com/ismasan/hash_mapper}
14
14
  s.rdoc_options = ['--charset=UTF-8']
@@ -17,14 +17,14 @@ Gem::Specification.new do |s|
17
17
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
18
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
19
  s.require_paths = ['lib']
20
-
20
+
21
21
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
22
22
  s.add_runtime_dependency("activesupport", ">= 4")
23
23
  else
24
24
  s.add_dependency("activesupport", ">= 4")
25
25
  end
26
-
26
+
27
27
  # specify any dependencies here; for example:
28
- s.add_development_dependency 'rspec'
28
+ s.add_development_dependency 'rspec', '>= 3.9'
29
29
  s.add_development_dependency 'rake'
30
30
  end
data/lib/hash_mapper.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'hash_mapper/version'
2
4
 
3
5
  $:.unshift(File.dirname(__FILE__)) unless
@@ -39,6 +41,9 @@ unless [].respond_to?(:inject_with_index)
39
41
  end
40
42
 
41
43
  module HashMapper
44
+ DEFAULT_OPTIONS = {}.freeze
45
+ NO_VALUE = :hash_mapper_no_value
46
+ NO_DEFAULT = :hash_mapper_no_default
42
47
 
43
48
  def self.extended(base)
44
49
  base.class_eval do
@@ -73,12 +78,12 @@ module HashMapper
73
78
  { using: mapper_class }
74
79
  end
75
80
 
76
- def normalize(a_hash, opts = {})
77
- perform_hash_mapping a_hash, :normalize, opts
81
+ def normalize(a_hash, options: DEFAULT_OPTIONS, context: nil)
82
+ perform_hash_mapping a_hash, :normalize, options: options, context: context
78
83
  end
79
84
 
80
- def denormalize(a_hash, opts = {})
81
- perform_hash_mapping a_hash, :denormalize, opts
85
+ def denormalize(a_hash, options: DEFAULT_OPTIONS, context: nil)
86
+ perform_hash_mapping a_hash, :denormalize, options: options, context: context
82
87
  end
83
88
 
84
89
  def before_normalize(&blk)
@@ -99,22 +104,22 @@ module HashMapper
99
104
 
100
105
  protected
101
106
 
102
- def perform_hash_mapping(a_hash, meth, opts)
107
+ def perform_hash_mapping(a_hash, meth, options:, context:)
103
108
  output = {}
104
109
 
105
110
  # Before filters
106
111
  a_hash = self.send(:"before_#{meth}_filters").inject(a_hash) do |memo, filter|
107
- filter.call(memo, output, opts)
112
+ filter.call(memo, output, options)
108
113
  end
109
114
 
110
115
  # Do the mapping
111
116
  self.maps.each do |m|
112
- m.process_into(output, a_hash, meth)
117
+ m.process_into(output, a_hash, method_name: meth, context: context)
113
118
  end
114
119
 
115
120
  # After filters
116
121
  self.send(:"after_#{meth}_filters").inject(output) do |memo, filter|
117
- filter.call(a_hash, memo, opts)
122
+ filter.call(a_hash, memo, options)
118
123
  end
119
124
  end
120
125
 
@@ -129,58 +134,58 @@ module HashMapper
129
134
  @path_from = path_from
130
135
  @path_to = path_to
131
136
  @delegated_mapper = options.fetch(:using, nil)
132
- @default_value = options.fetch(:default, :hash_mapper_no_default)
137
+ @default_value = options.fetch(:default, NO_DEFAULT)
133
138
  end
134
139
 
135
- def process_into(output, input, meth = :normalize)
136
- path_1, path_2 = (meth == :normalize ? [path_from, path_to] : [path_to, path_from])
137
- value = get_value_from_input(output, input, path_1, meth)
138
- set_value_in_output(output, path_2, value)
140
+ def process_into(output, input, method_name: :normalize, context: nil)
141
+ path_1, path_2 = (method_name == :normalize ? [path_from, path_to] : [path_to, path_from])
142
+ value = get_value_from_input(output, input, path_1, method_name: method_name, context: context)
143
+ set_value_in_output(output, path_2, value, context: context)
139
144
  end
140
145
  protected
141
146
 
142
- def get_value_from_input(output, input, path, meth)
147
+ def get_value_from_input(output, input, path, method_name:, context:)
143
148
  value = path.inject(input) do |h,e|
144
149
  if h.is_a?(Hash)
145
150
  v = [h[e.to_sym], h[e.to_s]].compact.first
146
151
  else
147
152
  v = h[e]
148
153
  end
149
- return :hash_mapper_no_value if v.nil?
154
+ return NO_VALUE if v.nil?
150
155
  v
151
156
  end
152
- delegated_mapper ? delegate_to_nested_mapper(value, meth) : value
157
+ delegated_mapper ? delegate_to_nested_mapper(value, method_name, context: context) : value
153
158
  end
154
159
 
155
- def set_value_in_output(output, path, value)
156
- if value == :hash_mapper_no_value
157
- if default_value == :hash_mapper_no_default
160
+ def set_value_in_output(output, path, value, context: nil)
161
+ if value == NO_VALUE
162
+ if default_value == NO_DEFAULT
158
163
  return
159
164
  else
160
165
  value = default_value
161
166
  end
162
167
  end
163
- add_value_to_hash!(output, path, value)
168
+ add_value_to_hash!(output, path, value, context: context)
164
169
  end
165
170
 
166
- def delegate_to_nested_mapper(value, meth)
171
+ def delegate_to_nested_mapper(value, method_name, context:)
167
172
  case value
168
173
  when Array
169
- value.map {|h| delegated_mapper.send(meth, h)}
174
+ value.map {|v| delegated_mapper.public_send(method_name, v, context: context)}
170
175
  when nil
171
- return :hash_mapper_no_value
176
+ return NO_VALUE
172
177
  else
173
- delegated_mapper.send(meth, value)
178
+ delegated_mapper.public_send(method_name, value, context: context)
174
179
  end
175
180
  end
176
181
 
177
- def add_value_to_hash!(hash, path, value)
182
+ def add_value_to_hash!(hash, path, value, context: nil)
178
183
  path.inject_with_index(hash) do |h,e,i|
179
184
  if !h[e].nil? # it can be FALSE
180
185
  h[e]
181
186
  else
182
187
  h[e] = if i == path.size-1
183
- path.apply_filter(value)
188
+ path.apply_filter(value, context: context)
184
189
  else
185
190
  if path.segments[i+1].is_a? Integer
186
191
  []
@@ -201,17 +206,24 @@ module HashMapper
201
206
  include Enumerable
202
207
 
203
208
  attr_reader :segments
204
- attr_writer :filter
205
209
  attr_reader :path
206
210
 
207
211
  def initialize(path)
208
212
  @path = path.dup
209
213
  @segments = parse(path)
210
214
  @filter = lambda{|value| value}# default filter does nothing
215
+ @filter_arity = 1
216
+ end
217
+
218
+ def filter=(f)
219
+ @filter_arity = f.respond_to?(:arity) ? f.arity : 1
220
+ @filter = f
211
221
  end
212
222
 
213
- def apply_filter(value)
214
- @filter.call(value)
223
+ def apply_filter(value, context: nil)
224
+ args = [value]
225
+ args << context if @filter_arity > 1
226
+ @filter.call(*args)
215
227
  end
216
228
 
217
229
  def each(&blk)
@@ -1,3 +1,3 @@
1
1
  module HashMapper
2
- VERSION = "0.2.3"
2
+ VERSION = "0.2.7"
3
3
  end
@@ -6,24 +6,24 @@ class OneLevel
6
6
  end
7
7
 
8
8
  describe 'mapping a hash with one level' do
9
-
9
+
10
10
  before :each do
11
- @from = {:name => 'ismael'}
12
- @to = {:nombre => 'ismael'}
11
+ @from = {name: 'ismael'}
12
+ @to = {nombre: 'ismael'}
13
13
  end
14
-
14
+
15
15
  it "should map to" do
16
- OneLevel.normalize(@from).should == @to
16
+ expect(OneLevel.normalize(@from)).to eq(@to)
17
17
  end
18
-
18
+
19
19
  it "should have indifferent access" do
20
- OneLevel.normalize({'name' => 'ismael'}).should == @to
20
+ expect(OneLevel.normalize({'name' => 'ismael'})).to eq(@to)
21
21
  end
22
-
22
+
23
23
  it "should map back the other way" do
24
- OneLevel.denormalize(@to).should == @from
24
+ expect(OneLevel.denormalize(@to)).to eq(@from)
25
25
  end
26
-
26
+
27
27
  end
28
28
 
29
29
  class ManyLevels
@@ -35,35 +35,35 @@ class ManyLevels
35
35
  end
36
36
 
37
37
  describe 'mapping from one nested hash to another' do
38
-
38
+
39
39
  before :each do
40
40
  @from = {
41
- :name => 'ismael',
42
- :tagid => 1,
43
- :properties => {
44
- :type => 'BLAH',
45
- :egg => 33
41
+ name: 'ismael',
42
+ tagid: 1,
43
+ properties: {
44
+ type: 'BLAH',
45
+ egg: 33
46
46
  }
47
47
  }
48
-
48
+
49
49
  @to = {
50
- :tag_id => 1,
51
- :chicken => 33,
52
- :tag_attributes => {
53
- :name => 'ismael',
54
- :type => 'BLAH'
50
+ tag_id: 1,
51
+ chicken: 33,
52
+ tag_attributes: {
53
+ name: 'ismael',
54
+ type: 'BLAH'
55
55
  }
56
56
  }
57
57
  end
58
-
58
+
59
59
  it "should map from and to different depths" do
60
- ManyLevels.normalize(@from).should == @to
60
+ expect(ManyLevels.normalize(@from)).to eq(@to)
61
61
  end
62
-
62
+
63
63
  it "should map back the other way" do
64
- ManyLevels.denormalize(@to).should == @from
64
+ expect(ManyLevels.denormalize(@to)).to eq(@from)
65
65
  end
66
-
66
+
67
67
  end
68
68
 
69
69
  class DifferentTypes
@@ -73,53 +73,53 @@ class DifferentTypes
73
73
  end
74
74
 
75
75
  describe 'coercing types' do
76
-
76
+
77
77
  before :each do
78
78
  @from = {
79
- :strings => {:a => '10'},
80
- :integers =>{:b => 20}
79
+ strings: {a: '10'},
80
+ integers: {b: 20}
81
81
  }
82
-
82
+
83
83
  @to = {
84
- :integers => {:a => 10},
85
- :strings => {:b => '20'}
84
+ integers: {a: 10},
85
+ strings: {b: '20'}
86
86
  }
87
87
  end
88
-
88
+
89
89
  it "should coerce values to specified types" do
90
- DifferentTypes.normalize(@from).should == @to
90
+ expect(DifferentTypes.normalize(@from)).to eq(@to)
91
91
  end
92
-
92
+
93
93
  it "should coerce the other way if specified" do
94
- DifferentTypes.denormalize(@to).should == @from
94
+ expect(DifferentTypes.denormalize(@to)).to eq(@from)
95
95
  end
96
-
96
+
97
97
  end
98
98
 
99
99
 
100
100
  describe 'arrays in hashes' do
101
101
  before :each do
102
102
  @from = {
103
- :name => ['ismael','sachiyo'],
104
- :tagid => 1,
105
- :properties => {
106
- :type => 'BLAH',
107
- :egg => 33
103
+ name: ['ismael','sachiyo'],
104
+ tagid: 1,
105
+ properties: {
106
+ type: 'BLAH',
107
+ egg: 33
108
108
  }
109
109
  }
110
-
110
+
111
111
  @to = {
112
- :tag_id => 1,
113
- :chicken => 33,
114
- :tag_attributes => {
115
- :name => ['ismael','sachiyo'],
116
- :type => 'BLAH'
112
+ tag_id: 1,
113
+ chicken: 33,
114
+ tag_attributes: {
115
+ name: ['ismael','sachiyo'],
116
+ type: 'BLAH'
117
117
  }
118
118
  }
119
119
  end
120
-
120
+
121
121
  it "should map array values as normal" do
122
- ManyLevels.normalize(@from).should == @to
122
+ expect(ManyLevels.normalize(@from)).to eq(@to)
123
123
  end
124
124
  end
125
125
 
@@ -129,34 +129,34 @@ class WithArrays
129
129
  map from('/arrays/names[1]'), to('/last_name')
130
130
  map from('/arrays/company'), to('/work/company')
131
131
  end
132
-
132
+
133
133
  describe "array indexes" do
134
134
  before :each do
135
135
  @from = {
136
- :arrays => {
137
- :names => ['ismael','celis'],
138
- :company => 'New Bamboo'
136
+ arrays: {
137
+ names: ['ismael','celis'],
138
+ company: 'New Bamboo'
139
139
  }
140
140
  }
141
- @to ={
142
- :first_name => 'ismael',
143
- :last_name => 'celis',
144
- :work => {:company => 'New Bamboo'}
141
+ @to = {
142
+ first_name: 'ismael',
143
+ last_name: 'celis',
144
+ work: {company: 'New Bamboo'}
145
145
  }
146
146
  end
147
-
147
+
148
148
  it "should extract defined array values" do
149
- WithArrays.normalize(@from).should == @to
149
+ expect(WithArrays.normalize(@from)).to eq(@to)
150
150
  end
151
-
151
+
152
152
  it "should map the other way restoring arrays" do
153
- WithArrays.denormalize(@to).should == @from
153
+ expect(WithArrays.denormalize(@to)).to eq(@from)
154
154
  end
155
155
  end
156
156
 
157
157
  class PersonWithBlock
158
158
  extend HashMapper
159
- def self.normalize(h)
159
+ def self.normalize(*_)
160
160
  super
161
161
  end
162
162
  map from('/names/first'){|n| n.gsub('+','')}, to('/first_name'){|n| "+++#{n}+++"}
@@ -166,64 +166,64 @@ class PersonWithBlockOneWay
166
166
  map from('/names/first'), to('/first_name') do |n| "+++#{n}+++" end
167
167
  end
168
168
 
169
- describe "with blocks filters" do
169
+ describe "with block filters" do
170
170
  before :each do
171
171
  @from = {
172
- :names => {:first => 'Ismael'}
172
+ names: {first: 'Ismael'}
173
173
  }
174
174
  @to = {
175
- :first_name => '+++Ismael+++'
175
+ first_name: '+++Ismael+++'
176
176
  }
177
177
  end
178
-
178
+
179
179
  it "should pass final value through given block" do
180
- PersonWithBlock.normalize(@from).should == @to
180
+ expect(PersonWithBlock.normalize(@from)).to eq(@to)
181
181
  end
182
-
182
+
183
183
  it "should be able to map the other way using a block" do
184
- PersonWithBlock.denormalize(@to).should == @from
184
+ expect(PersonWithBlock.denormalize(@to)).to eq(@from)
185
185
  end
186
-
186
+
187
187
  it "should accept a block for just one direction" do
188
- PersonWithBlockOneWay.normalize(@from).should == @to
188
+ expect(PersonWithBlockOneWay.normalize(@from)).to eq(@to)
189
189
  end
190
-
190
+
191
191
  end
192
192
 
193
193
  class ProjectMapper
194
194
  extend HashMapper
195
-
195
+
196
196
  map from('/name'), to('/project_name')
197
- map from('/author_hash'), to('/author'), using(PersonWithBlock)
197
+ map from('/author_hash'), to('/author'), using: PersonWithBlock
198
198
  end
199
199
 
200
200
  describe "with nested mapper" do
201
201
  before :each do
202
202
  @from ={
203
- :name => 'HashMapper',
204
- :author_hash => {
205
- :names => {:first => 'Ismael'}
203
+ name: 'HashMapper',
204
+ author_hash: {
205
+ names: {first: 'Ismael'}
206
206
  }
207
207
  }
208
208
  @to = {
209
- :project_name => 'HashMapper',
210
- :author => {:first_name => '+++Ismael+++'}
209
+ project_name: 'HashMapper',
210
+ author: {first_name: '+++Ismael+++'}
211
211
  }
212
212
  end
213
-
213
+
214
214
  it "should delegate nested hashes to another mapper" do
215
- ProjectMapper.normalize(@from).should == @to
215
+ expect(ProjectMapper.normalize(@from)).to eq(@to)
216
216
  end
217
-
217
+
218
218
  it "should translate the other way using nested hashes" do
219
- ProjectMapper.denormalize(@to).should == @from
219
+ expect(ProjectMapper.denormalize(@to)).to eq(@from)
220
220
  end
221
-
221
+
222
222
  end
223
223
 
224
224
  class CompanyMapper
225
225
  extend HashMapper
226
-
226
+
227
227
  map from('/name'), to('/company_name')
228
228
  map from('/employees'), to('/employees') do |employees_array|
229
229
  employees_array.collect{|emp_hash| PersonWithBlock.normalize(emp_hash)}
@@ -232,82 +232,82 @@ end
232
232
 
233
233
  class CompanyEmployeesMapper
234
234
  extend HashMapper
235
-
235
+
236
236
  map from('/name'), to('/company_name')
237
- map from('/employees'), to('/employees'), using(PersonWithBlock)
237
+ map from('/employees'), to('/employees'), using: PersonWithBlock
238
238
  end
239
239
 
240
240
  describe "with arrays of nested hashes" do
241
241
  before :each do
242
242
  @from = {
243
- :name => 'New Bamboo',
244
- :employees => [
245
- {:names => {:first => 'Ismael'}},
246
- {:names => {:first => 'Sachiyo'}},
247
- {:names => {:first => 'Pedro'}}
243
+ name: 'New Bamboo',
244
+ employees: [
245
+ {names: {first: 'Ismael'}},
246
+ {names: {first: 'Sachiyo'}},
247
+ {names: {first: 'Pedro'}}
248
248
  ]
249
249
  }
250
250
  @to = {
251
- :company_name => 'New Bamboo',
252
- :employees => [
253
- {:first_name => '+++Ismael+++'},
254
- {:first_name => '+++Sachiyo+++'},
255
- {:first_name => '+++Pedro+++'}
251
+ company_name: 'New Bamboo',
252
+ employees: [
253
+ {first_name: '+++Ismael+++'},
254
+ {first_name: '+++Sachiyo+++'},
255
+ {first_name: '+++Pedro+++'}
256
256
  ]
257
257
  }
258
258
  end
259
-
259
+
260
260
  it "should pass array value though given block mapper" do
261
- CompanyMapper.normalize(@from).should == @to
261
+ expect(CompanyMapper.normalize(@from)).to eq(@to)
262
262
  end
263
-
263
+
264
264
  it "should map array elements automatically" do
265
- CompanyEmployeesMapper.normalize(@from).should == @to
265
+ expect(CompanyEmployeesMapper.normalize(@from)).to eq(@to)
266
266
  end
267
267
  end
268
268
 
269
269
  class NoKeys
270
270
  extend HashMapper
271
-
271
+
272
272
  map from('/exists'), to('/exists_yahoo') #in
273
273
  map from('/exists_as_nil'), to('/exists_nil') #in
274
274
  map from('/foo'), to('/bar') # not in
275
-
275
+
276
276
  end
277
277
 
278
278
  describe "with non-matching maps" do
279
279
  before :all do
280
280
  @input = {
281
- :exists => 1,
282
- :exists_as_nil => nil,
283
- :doesnt_exist => 2
281
+ exists: 1,
282
+ exists_as_nil: nil,
283
+ doesnt_exist: 2
284
284
  }
285
285
  @output = {
286
- :exists_yahoo => 1
286
+ exists_yahoo: 1
287
287
  }
288
288
  end
289
-
289
+
290
290
  it "should ignore maps that don't exist" do
291
- NoKeys.normalize(@input).should == @output
291
+ expect(NoKeys.normalize(@input)).to eq(@output)
292
292
  end
293
293
  end
294
294
 
295
295
  describe "with false values" do
296
-
296
+
297
297
  it "should include values in output" do
298
- NoKeys.normalize({'exists' => false}).should == {:exists_yahoo => false}
299
- NoKeys.normalize({:exists => false}).should == {:exists_yahoo => false}
298
+ expect(NoKeys.normalize({'exists' => false})).to eq({exists_yahoo: false})
299
+ expect(NoKeys.normalize({exists: false})).to eq({exists_yahoo: false})
300
300
  end
301
-
301
+
302
302
  end
303
303
 
304
304
  describe "with nil values" do
305
-
305
+
306
306
  it "should not include values in output" do
307
- NoKeys.normalize({:exists => nil}).should == {}
308
- NoKeys.normalize({'exists' => nil}).should == {}
307
+ expect(NoKeys.normalize({exists: nil})).to eq({})
308
+ expect(NoKeys.normalize({'exists' => nil})).to eq({})
309
309
  end
310
-
310
+
311
311
  end
312
312
 
313
313
  class WithBeforeFilters
@@ -341,22 +341,22 @@ end
341
341
 
342
342
  describe "before and after filters" do
343
343
  before(:all) do
344
- @denorm = {:hello => 'wassup?!'}
345
- @norm = {:goodbye => 'seeya later!'}
344
+ @denorm = {hello: 'wassup?!'}
345
+ @norm = {goodbye: 'seeya later!'}
346
346
  end
347
+
347
348
  it "should allow filtering before normalize" do
348
- WithBeforeFilters.normalize(@denorm).should == {:goodbye => 'wassup?!', :extra => 'extra wassup?! innit'}
349
+ expect(WithBeforeFilters.normalize(@denorm)).to eq({goodbye: 'wassup?!', extra: 'extra wassup?! innit'})
349
350
  end
350
351
  it "should allow filtering before denormalize" do
351
- WithBeforeFilters.denormalize(@norm).should == {:hello => 'changed'}
352
+ expect(WithBeforeFilters.denormalize(@norm)).to eq({hello: 'changed'})
352
353
  end
353
354
  it "should allow filtering after normalize" do
354
- WithAfterFilters.normalize(@denorm).should == [[:goodbye, 'wassup?!']]
355
+ expect(WithAfterFilters.normalize(@denorm)).to eq([[:goodbye, 'wassup?!']])
355
356
  end
356
357
  it "should allow filtering after denormalize" do
357
- WithAfterFilters.denormalize(@norm).should == {}
358
+ expect(WithAfterFilters.denormalize(@norm)).to eq({})
358
359
  end
359
-
360
360
  end
361
361
 
362
362
  class NotRelated
@@ -380,23 +380,23 @@ end
380
380
  describe "inherited mappers" do
381
381
  before :all do
382
382
  @from = {
383
- :a => 'a',
384
- :b => 'b',
385
- :c => 'c'
383
+ a: 'a',
384
+ b: 'b',
385
+ c: 'c'
386
386
  }
387
387
  @to_b ={
388
- :a => {:a => 'a'},
389
- :b => {:b => 'b'}
388
+ a: {a: 'a'},
389
+ b: {b: 'b'}
390
390
  }
391
391
 
392
392
  end
393
-
393
+
394
394
  it "should inherit mappings" do
395
- B.normalize(@from).should == @to_b
395
+ expect(B.normalize(@from)).to eq(@to_b)
396
396
  end
397
-
397
+
398
398
  it "should not affect other mappers" do
399
- NotRelated.normalize('n' => 'nn').should == {:n => {:n => 'nn'}}
399
+ expect(NotRelated.normalize('n' => 'nn')).to eq({n: {n: 'nn'}})
400
400
  end
401
401
  end
402
402
 
@@ -407,23 +407,21 @@ class MixedMappings
407
407
  end
408
408
 
409
409
  describe "dealing with strings and symbols" do
410
-
410
+
411
411
  it "should be able to normalize from a nested hash with string keys" do
412
- MixedMappings.normalize(
412
+ expect(MixedMappings.normalize(
413
413
  'big' => {'jobs' => 5},
414
414
  'timble' => 3.2
415
- ).should == {:dodo => 5,
416
- :bingo => {:biscuit => 3.2}}
415
+ )).to eq({dodo: 5, bingo: {biscuit: 3.2}})
417
416
  end
418
-
417
+
419
418
  it "should not symbolized keys in value hashes" do
420
- MixedMappings.normalize(
419
+ expect(MixedMappings.normalize(
421
420
  'big' => {'jobs' => 5},
422
421
  'timble' => {'string key' => 'value'}
423
- ).should == {:dodo => 5,
424
- :bingo => {:biscuit => {'string key' => 'value'}}}
422
+ )).to eq({dodo: 5, bingo: {biscuit: {'string key' => 'value'}}})
425
423
  end
426
-
424
+
427
425
  end
428
426
 
429
427
  class DefaultValues
@@ -435,16 +433,16 @@ end
435
433
 
436
434
  describe "default values" do
437
435
  it "should use a default value whenever a key is not set" do
438
- DefaultValues.normalize(
436
+ expect(DefaultValues.normalize(
439
437
  'without_default' => 'some_value'
440
- ).should == { not_defaulted: 'some_value', defaulted: 'the_default_value' }
438
+ )).to eq({ not_defaulted: 'some_value', defaulted: 'the_default_value' })
441
439
  end
442
440
 
443
441
  it "should not use a default if a key is set (even if the value is falsy)" do
444
- DefaultValues.normalize({
442
+ expect(DefaultValues.normalize({
445
443
  'without_default' => 'some_value',
446
444
  'with_default' => false
447
- }).should == { not_defaulted: 'some_value', defaulted: false }
445
+ })).to eq({ not_defaulted: 'some_value', defaulted: false })
448
446
  end
449
447
  end
450
448
 
@@ -488,20 +486,20 @@ end
488
486
 
489
487
  describe 'multiple before filters' do
490
488
  it 'runs before_normalize filters in the order they are defined' do
491
- MultiBeforeFilter.normalize({ foo: 'X' }).should == { bar: 'XYZ' }
489
+ expect(MultiBeforeFilter.normalize({ foo: 'X' })).to eq({ bar: 'XYZ' })
492
490
  end
493
491
 
494
492
  it 'runs before_denormalize filters in the order they are defined' do
495
- MultiBeforeFilter.denormalize({ bar: 'X' }).should == { foo: 'BAX' }
493
+ expect(MultiBeforeFilter.denormalize({ bar: 'X' })).to eq({ foo: 'BAX' })
496
494
  end
497
495
 
498
496
  context 'when the filters are spread across classes' do
499
497
  it 'runs before_normalize filters in the order they are defined' do
500
- MultiBeforeFilterSubclass.normalize({ foo: 'X' }).should == { bar: 'XYZ!' }
498
+ expect(MultiBeforeFilterSubclass.normalize({ foo: 'X' })).to eq({ bar: 'XYZ!' })
501
499
  end
502
500
 
503
501
  it 'runs before_denormalize filters in the order they are defined' do
504
- MultiBeforeFilterSubclass.denormalize({ bar: 'X' }).should == { foo: 'CBAX' }
502
+ expect(MultiBeforeFilterSubclass.denormalize({ bar: 'X' })).to eq({ foo: 'CBAX' })
505
503
  end
506
504
  end
507
505
  end
@@ -546,20 +544,20 @@ end
546
544
 
547
545
  describe 'multiple after filters' do
548
546
  it 'runs after_normalize filters in the order they are defined' do
549
- MultiAfterFilter.normalize({ baz: '0' }).should == { bat: '012' }
547
+ expect(MultiAfterFilter.normalize({ baz: '0' })).to eq({ bat: '012' })
550
548
  end
551
549
 
552
550
  it 'runs after_denormalize filters in the order they are defined' do
553
- MultiAfterFilter.denormalize({ bat: '0' }).should == { baz: '890' }
551
+ expect(MultiAfterFilter.denormalize({ bat: '0' })).to eq({ baz: '890' })
554
552
  end
555
553
 
556
554
  context 'when the filters are spread across classes' do
557
555
  it 'runs after_normalize filters in the order they are defined' do
558
- MultiAfterFilterSubclass.normalize({ baz: '0' }).should == { bat: '0123' }
556
+ expect(MultiAfterFilterSubclass.normalize({ baz: '0' })).to eq({ bat: '0123' })
559
557
  end
560
558
 
561
559
  it 'runs after_denormalize filters in the order they are defined' do
562
- MultiAfterFilterSubclass.denormalize({ bat: '0' }).should == { baz: '7890' }
560
+ expect(MultiAfterFilterSubclass.denormalize({ bat: '0' })).to eq({ baz: '7890' })
563
561
  end
564
562
  end
565
563
  end
@@ -591,15 +589,58 @@ end
591
589
  describe 'with options' do
592
590
  context 'when called with options' do
593
591
  it 'passes the options to all the filters' do
594
- WithOptions.normalize({}, bn: 1, an: 2).should == {bn: 1, an: 2}
595
- WithOptions.denormalize({}, bdn: 1, adn: 2).should == {bdn: 1, adn: 2}
592
+ expect(WithOptions.normalize({}, options: { bn: 1, an: 2 })).to eq({bn: 1, an: 2})
593
+ expect(WithOptions.denormalize({}, options: { bdn: 1, adn: 2 })).to eq({bdn: 1, adn: 2})
596
594
  end
597
595
  end
598
596
 
599
597
  context 'when called without options' do
600
598
  it 'stills work' do
601
- WithOptions.normalize({}).should == {}
602
- WithOptions.denormalize({}).should == {}
599
+ expect(WithOptions.normalize({})).to eq({})
600
+ expect(WithOptions.denormalize({})).to eq({})
603
601
  end
604
602
  end
605
603
  end
604
+
605
+ describe 'passing custom context object' do
606
+ it 'passes context object down to sub-mappers' do
607
+ friend_mapper = Class.new do
608
+ extend HashMapper
609
+
610
+ map from('/name'), to('/name')
611
+
612
+ def normalize(input, context: , **kargs)
613
+ context[:names] ||= []
614
+ context[:names] << input[:name]
615
+ self.class.normalize(input, context: context, **kargs)
616
+ end
617
+ end
618
+
619
+ mapper = Class.new do
620
+ extend HashMapper
621
+
622
+ map from('/friends'), to('/friends'), using: friend_mapper.new
623
+ end
624
+
625
+ input = {friends: [{name: 'Ismael', last_name: 'Celis'}, {name: 'Joe'}]}
626
+ ctx = {}
627
+ mapper.normalize(input, context: ctx)
628
+ expect(ctx[:names]).to eq(%w(Ismael Joe))
629
+ end
630
+ end
631
+
632
+ describe 'passing context down to filters' do
633
+ it 'yields context to filters' do
634
+ mapper = Class.new do
635
+ extend HashMapper
636
+
637
+ map from('/name'), to('/name', &(->(name, ctx) { "#{ctx[:title]} #{name}" }))
638
+ map from('/age'), to('/age') do |age, ctx|
639
+ "#{age} #{ctx[:age_suffix]}"
640
+ end
641
+ end
642
+
643
+ output = mapper.normalize({ name: 'Ismael', age: 43 }, context: { title: 'Mr.', age_suffix: 'years old' })
644
+ expect(output).to eq({ name: 'Mr. Ismael', age: '43 years old' })
645
+ end
646
+ end
data/spec/spec_helper.rb CHANGED
@@ -9,7 +9,7 @@ RSpec.configure do |config|
9
9
  config.filter_run focus: true
10
10
 
11
11
  config.expect_with :rspec do |c|
12
- c.syntax = :should
12
+ c.syntax = :expect
13
13
  end
14
14
  end
15
15
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hash_mapper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.2.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ismael Celis
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-09-14 00:00:00.000000000 Z
11
+ date: 2021-07-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: '3.9'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: '3.9'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -72,7 +72,7 @@ files:
72
72
  homepage: http://github.com/ismasan/hash_mapper
73
73
  licenses: []
74
74
  metadata: {}
75
- post_install_message:
75
+ post_install_message:
76
76
  rdoc_options:
77
77
  - "--charset=UTF-8"
78
78
  require_paths:
@@ -88,9 +88,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0'
90
90
  requirements: []
91
- rubyforge_project:
92
- rubygems_version: 2.4.5
93
- signing_key:
91
+ rubygems_version: 3.0.3
92
+ signing_key:
94
93
  specification_version: 4
95
94
  summary: Maps input hashes to a normalized format
96
95
  test_files: