ismasan-hash_mapper 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -188,6 +188,32 @@ But HashMapper's nested mappers will actually do that for you if a value is an a
188
188
  map from('/employees'), to('employees'), using(UserMapper)
189
189
 
190
190
  ... Will map each employee using UserMapper.
191
+
192
+ ==== Before and after filters
193
+
194
+ Sometimes you will need some slightly more complex processing on the whole hash, either before or after normalizing/denormalizing.
195
+
196
+ For this you can use the class methods before_normalize, before_denormalize, after_normalize and after_denormalize.
197
+
198
+ They all yield a block with 2 arguments - the hash you are mapping from and the hash you are mapping to, e.g.
199
+
200
+ class EggMapper
201
+ map from('/raw'), to('/fried')
202
+
203
+ before_normalize do |input, output|
204
+ input['raw'] ||= 'please' # this will give 'raw' a default value
205
+ input
206
+ end
207
+
208
+ after_denormalize do |input, output|
209
+ output.to_a # the denormalized object will now be an array, not a hash!!
210
+ end
211
+
212
+ end
213
+
214
+ Important: note that for before filters, you need to return the (modified) input, and for after filters, you need to return the output.
215
+ Note also that 'output' is correct at the time of the filter, i.e. before_normalize yields 'output' as an empty hash, while after_normalize yields it as an already normalized hash.
216
+
191
217
 
192
218
  == REQUIREMENTS:
193
219
 
@@ -205,8 +231,9 @@ But HashMapper's nested mappers will actually do that for you if a value is an a
205
231
 
206
232
  == Credits:
207
233
 
208
- * Ismael Celis (http://www.estadobeta.com)
209
- * Mark Evans
234
+ * Ismael Celis (Author - http://www.estadobeta.com)
235
+ * Mark Evans (Contributor - http://github.com/markevans)
236
+ * Jdeveloper (Contributor - http://github.com/jdeveloper)
210
237
 
211
238
  == LICENSE:
212
239
 
data/lib/hash_mapper.rb CHANGED
@@ -1,6 +1,15 @@
1
1
  $:.unshift(File.dirname(__FILE__)) unless
2
2
  $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
3
 
4
+ begin
5
+ require 'active_support'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'active_support'
9
+ end
10
+
11
+
12
+
4
13
  # This allows us to call blah(&:some_method) instead of blah{|i| i.some_method }
5
14
  unless Symbol.instance_methods.include?('to_proc')
6
15
  class Symbol
@@ -10,11 +19,26 @@ unless Symbol.instance_methods.include?('to_proc')
10
19
  end
11
20
  end
12
21
 
22
+ # http://rpheath.com/posts/341-ruby-inject-with-index
23
+ unless Array.instance_methods.include?("inject_with_index")
24
+ module Enumerable
25
+ def inject_with_index(injected)
26
+ each_with_index{ |obj, index| injected = yield(injected, obj, index) }
27
+ injected
28
+ end
29
+ end
30
+ end
31
+
13
32
  module HashMapper
14
- VERSION = '0.0.5'
33
+ VERSION = '0.0.6'
15
34
 
16
- def maps
17
- @maps ||= []
35
+ # we need this for inheritable mappers, which is annoying because it needs ActiveSupport, kinda overkill.
36
+ #
37
+ def self.extended(base)
38
+ base.class_eval do
39
+ write_inheritable_attribute :maps, []
40
+ class_inheritable_accessor :maps
41
+ end
18
42
  end
19
43
 
20
44
  def map(from, to, using=nil, &filter)
@@ -41,15 +65,40 @@ module HashMapper
41
65
  def denormalize(a_hash)
42
66
  perform_hash_mapping a_hash, :denormalize
43
67
  end
68
+
69
+ def before_normalize(&blk)
70
+ @before_normalize = blk
71
+ end
72
+
73
+ def before_denormalize(&blk)
74
+ @before_denormalize = blk
75
+ end
44
76
 
77
+ def after_normalize(&blk)
78
+ @after_normalize = blk
79
+ end
80
+
81
+ def after_denormalize(&blk)
82
+ @after_denormalize = blk
83
+ end
84
+
45
85
  protected
86
+
46
87
 
47
88
  def perform_hash_mapping(a_hash, meth)
48
89
  output = {}
90
+ # Before filter
91
+ before_filter = instance_eval "@before_#{meth}"
92
+ a_hash = before_filter.call(a_hash, output) if before_filter
93
+ # Do the mapping
49
94
  a_hash = symbolize_keys(a_hash)
50
95
  maps.each do |m|
51
96
  m.process_into(output, a_hash, meth)
52
97
  end
98
+ # After filter
99
+ after_filter = instance_eval "@after_#{meth}"
100
+ output = after_filter.call(a_hash, output) if after_filter
101
+ # Return
53
102
  output
54
103
  end
55
104
 
@@ -85,50 +134,35 @@ module HashMapper
85
134
 
86
135
  def get_value_from_input(output, input, path, meth)
87
136
  value = path.inject(input) do |h,e|
88
- throw :no_value unless h.has_key?(e[0].to_sym)
89
- e[1].nil? ? h[e[0].to_sym] : h[e[0].to_sym][e[1].to_i]
90
- #h[e[0].to_sym]
137
+ throw :no_value if h[e].nil?#.has_key?(e)
138
+ h[e]
91
139
  end
92
- value = delegate_to_nested_mapper(value, meth) if delegated_mapper
93
- value
140
+ delegated_mapper ? delegate_to_nested_mapper(value, meth) : value
94
141
  end
95
142
 
96
143
 
97
144
  def delegate_to_nested_mapper(value, meth)
98
- v = if value.kind_of?(Array)
145
+ case value
146
+ when Array
99
147
  value.map {|h| delegated_mapper.send(meth, h)}
148
+ when nil
149
+ throw :no_value
100
150
  else
101
151
  delegated_mapper.send(meth, value)
102
152
  end
103
153
  end
104
154
 
105
155
  def add_value_to_hash!(hash, path, value)
106
- path.inject(hash) do |h,e|
107
- if contained?(h,e)
108
- if e[1].nil?
109
- h[e[0].to_sym]
110
- else
111
- if e == path.last
112
- h[e[0].to_sym][e[1].to_i] = value
113
- end
114
- h[e[0].to_sym][e[1].to_i]
115
- end
156
+ path.inject_with_index(hash) do |h,e,i|
157
+ if h[e]
158
+ h[e]
116
159
  else
117
- if e[1].nil?
118
- h[e[0].to_sym] = (e == path.last ? path.apply_filter(value) : {})
119
- else
120
- h[e[0].to_sym] = []
121
- h[e[0].to_sym][e[1].to_i] = (e == path.last ? path.apply_filter(value) : {})
122
- end
160
+ h[e] = (i == path.size-1 ? path.apply_filter(value) : {})
123
161
  end
124
162
  end
163
+
125
164
  end
126
165
 
127
- def contained?(h,e)
128
- e[1].nil? ? h[e[0].to_sym] : h[e[0].to_sym][e[1].to_i].nil?
129
- rescue
130
- false
131
- end
132
166
  end
133
167
 
134
168
  # contains array of path segments
@@ -153,21 +187,24 @@ module HashMapper
153
187
  @segments.each(&blk)
154
188
  end
155
189
 
190
+ def first
191
+ @segments.first
192
+ end
193
+
156
194
  def last
157
195
  @segments.last
158
196
  end
159
197
 
198
+ def size
199
+ @segments.size
200
+ end
201
+
160
202
  private
161
203
 
162
204
  def parse(path)
163
- #path.sub(/^\//,'').split('/').map(&:to_sym)
164
- path.sub(/^\//,'').split('/').map{ |p| key_index p }
165
- end
166
-
167
- def key_index(p)
168
- p =~ /\[[0-9]+\]$/ ? p.sub(/\[([0-9]+)\]$/,' \1').split(' ') : [p,nil]
205
+ path.sub(/^\//,'').split('/').map(&:to_sym)
169
206
  end
170
207
 
171
208
  end
172
209
 
173
- end
210
+ end
@@ -146,10 +146,12 @@ describe "array indexes" do
146
146
  end
147
147
 
148
148
  it "should extract defined array values" do
149
+ pending
149
150
  WithArrays.normalize(@from).should == @to
150
151
  end
151
152
 
152
153
  it "should map the other way restoring arrays" do
154
+ pending
153
155
  WithArrays.denormalize(@to).should == @from
154
156
  end
155
157
  end
@@ -283,8 +285,7 @@ describe "with non-matching maps" do
283
285
  :doesnt_exist => 2
284
286
  }
285
287
  @output = {
286
- :exists_yahoo => 1,
287
- :exists_nil => nil
288
+ :exists_yahoo => 1
288
289
  }
289
290
  end
290
291
 
@@ -293,3 +294,92 @@ describe "with non-matching maps" do
293
294
  end
294
295
  end
295
296
 
297
+ class WithBeforeFilters
298
+ extend HashMapper
299
+ map from('/hello'), to('/goodbye')
300
+ map from('/extra'), to('/extra')
301
+
302
+ before_normalize do |input, output|
303
+ input[:extra] = "extra #{input[:hello]} innit"
304
+ input
305
+ end
306
+ before_denormalize do |input, output|
307
+ input[:goodbye] = 'changed'
308
+ input
309
+ end
310
+ end
311
+
312
+ class WithAfterFilters
313
+ extend HashMapper
314
+ map from('/hello'), to('/goodbye')
315
+
316
+ after_normalize do |input, output|
317
+ output = output.to_a
318
+ output
319
+ end
320
+ after_denormalize do |input, output|
321
+ output.delete(:hello)
322
+ output
323
+ end
324
+ end
325
+
326
+ describe "before and after filters" do
327
+ before(:all) do
328
+ @denorm = {:hello => 'wassup?!'}
329
+ @norm = {:goodbye => 'seeya later!'}
330
+ end
331
+ it "should allow filtering before normalize" do
332
+ WithBeforeFilters.normalize(@denorm).should == {:goodbye => 'wassup?!', :extra => 'extra wassup?! innit'}
333
+ end
334
+ it "should allow filtering before denormalize" do
335
+ WithBeforeFilters.denormalize(@norm).should == {:hello => 'changed'}
336
+ end
337
+ it "should allow filtering after normalize" do
338
+ WithAfterFilters.normalize(@denorm).should == [[:goodbye, 'wassup?!']]
339
+ end
340
+ it "should allow filtering after denormalize" do
341
+ WithAfterFilters.denormalize(@norm).should == {}
342
+ end
343
+
344
+ end
345
+
346
+ class NotRelated
347
+ extend HashMapper
348
+ map from('/n'), to('/n/n')
349
+ end
350
+
351
+ class A
352
+ extend HashMapper
353
+ map from('/a'), to('/a/a')
354
+ end
355
+
356
+ class B < A
357
+ map from('/b'), to('/b/b')
358
+ end
359
+
360
+ class C < B
361
+ map from('/c'), to('/c/c')
362
+ end
363
+
364
+ describe "inherited mappers" do
365
+ before :all do
366
+ @from = {
367
+ :a => 'a',
368
+ :b => 'b',
369
+ :c => 'c'
370
+ }
371
+ @to_b ={
372
+ :a => {:a => 'a'},
373
+ :b => {:b => 'b'}
374
+ }
375
+
376
+ end
377
+
378
+ it "should inherit mappings" do
379
+ B.normalize(@from).should == @to_b
380
+ end
381
+
382
+ it "should not affect other mappers" do
383
+ NotRelated.normalize('n' => 'nn').should == {:n => {:n => 'nn'}}
384
+ end
385
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ismasan-hash_mapper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ismael Celis
@@ -9,11 +9,12 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-02-08 00:00:00 -08:00
12
+ date: 2009-02-16 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: newgem
17
+ type: :development
17
18
  version_requirement:
18
19
  version_requirements: !ruby/object:Gem::Requirement
19
20
  requirements:
@@ -23,6 +24,7 @@ dependencies:
23
24
  version:
24
25
  - !ruby/object:Gem::Dependency
25
26
  name: hoe
27
+ type: :development
26
28
  version_requirement:
27
29
  version_requirements: !ruby/object:Gem::Requirement
28
30
  requirements: