xml_mapper 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,515 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "XmlMapper" do
4
+ before(:each) do
5
+ @mapper = XmlMapper.new
6
+ end
7
+
8
+ describe "#add_mapping" do
9
+ it "adds the correct mappings when only one symbol given" do
10
+ @mapper.add_mapping(:text, :title)
11
+ @mapper.mappings.should == [{ :type => :text, :xpath => "title", :key => :title, :options => {} }]
12
+ end
13
+
14
+ it "adds the correct mapping when type is not text" do
15
+ @mapper.add_mapping(:length, :title)
16
+ @mapper.mappings.should == [{ :type => :length, :xpath => "title", :key => :title, :options => {} }]
17
+ end
18
+
19
+ it "adds the correct mapping when type is node" do
20
+ @mapper.add_mapping(:node, :title)
21
+ @mapper.mappings.should == [{ :type => :node, :xpath => "title", :key => :title, :options => {} }]
22
+ end
23
+
24
+ it "adds multiple mappings when more symbols given" do
25
+ @mapper.add_mapping(:text, :first_name, :last_name)
26
+ @mapper.mappings.should == [
27
+ { :type => :text, :xpath => "first_name", :key => :first_name, :options => {} },
28
+ { :type => :text, :xpath => "last_name", :key => :last_name, :options => {} },
29
+ ]
30
+ end
31
+
32
+ it "adds mappings when mapping given as hash" do
33
+ @mapper.add_mapping(:text, :name => :first_name)
34
+ @mapper.mappings.should == [{ :type => :text, :xpath => "name", :key => :first_name, :options => {} }]
35
+ end
36
+
37
+ it "adds mappings with options when mapping given as symbols and last arg is hash" do
38
+ @mapper.add_mapping(:text, :name, :born, :after_map => :to_s)
39
+ @mapper.mappings.should == [
40
+ { :type => :text, :xpath => "name", :key => :name, :options => { :after_map => :to_s } },
41
+ { :type => :text, :xpath => "born", :key => :born, :options => { :after_map => :to_s } },
42
+ ]
43
+ end
44
+ end
45
+
46
+ describe "map attributes" do
47
+ before(:each) do
48
+ @xml = %(
49
+ <album>
50
+ <title>Black on Both Sides</title>
51
+ <artist_name>Mos Def</artist_name>
52
+ <track_number>7</track_number>
53
+ </album>)
54
+ end
55
+
56
+ it "maps the correct inner text when node found" do
57
+ @mapper.add_mapping(:text, :artist_name, :title)
58
+ @mapper.attributes_from_xml(@xml).should == { :title => "Black on Both Sides", :artist_name => "Mos Def" }
59
+ end
60
+
61
+ describe "#exists" do
62
+ it "returns true when node exists" do
63
+ xml = %(<album><title>Black on Both Sides</title><rights><country>DE</country></rights></album>)
64
+ @mapper.add_mapping(:exists, "rights[country='DE']" => :allows_streaming)
65
+ @mapper.attributes_from_xml(xml).should == { :allows_streaming => true }
66
+ end
67
+
68
+ it "returns false when node does not exist" do
69
+ xml = %(<album><title>Black on Both Sides</title><rights><country>DE</country></rights></album>)
70
+ @mapper.add_mapping(:exists, "rights[country='FR']" => :allows_streaming)
71
+ @mapper.attributes_from_xml(xml).should == { :allows_streaming => false }
72
+ end
73
+ end
74
+
75
+ describe "#node" do
76
+ it "returns a nokogiri node" do
77
+ @mapper.add_mapping(:node, :title)
78
+ @mapper.attributes_from_xml(@xml)[:title].should be_an_instance_of(Nokogiri::XML::Element)
79
+ end
80
+
81
+ it "returns nil when node not found" do
82
+ @mapper.add_mapping(:node, :rgne)
83
+ @mapper.attributes_from_xml(@xml)[:rgne].should be_nil
84
+ end
85
+
86
+ it "returns the correct nokogiri node" do
87
+ @mapper.add_mapping(:node, :title)
88
+ node = @mapper.attributes_from_xml(@xml)[:title]
89
+ node.inner_text.should == "Black on Both Sides"
90
+ end
91
+
92
+ it "can be combined with after_map" do
93
+ @mapper.add_mapping(:node, :title, :after_map => :inner_text)
94
+ @mapper.attributes_from_xml(@xml)[:title].should == "Black on Both Sides"
95
+ end
96
+ end
97
+
98
+ it "maps not found nodes to nil" do
99
+ @mapper.add_mapping(:text, :artist_name, :version_title, :long_title)
100
+ @mapper.attributes_from_xml(@xml).should == {
101
+ :version_title => nil, :long_title => nil, :artist_name => "Mos Def"
102
+ }
103
+ end
104
+
105
+ it "converts integers to integer when found" do
106
+ @mapper.add_mapping(:text, :artist_name)
107
+ @mapper.add_mapping(:integer, :track_number)
108
+ @mapper.attributes_from_xml(@xml).should == {
109
+ :track_number => 7, :artist_name => "Mos Def"
110
+ }
111
+ end
112
+
113
+ it "does not convert nil to integer for integer type" do
114
+ @mapper.add_mapping(:text, :artist_name)
115
+ @mapper.add_mapping(:integer, :track_number, :set_count)
116
+ @mapper.attributes_from_xml(@xml).should == {
117
+ :track_number => 7, :artist_name => "Mos Def", :set_count => nil
118
+ }
119
+ end
120
+
121
+ it "calls method with name type on value when found and responding" do
122
+ @mapper.add_mapping(:text, :artist_name, :after_map => :upcase)
123
+ @mapper.attributes_from_xml(@xml).should == {
124
+ :artist_name => "MOS DEF"
125
+ }
126
+ end
127
+
128
+ it "uses mapper method defined in xml_mapper when value does not respond to :after_map" do
129
+ class << @mapper
130
+ def double(value)
131
+ value.to_s * 2
132
+ end
133
+ end
134
+
135
+ @mapper.add_mapping(:text, :artist_name, :after_map => :double)
136
+ @mapper.attributes_from_xml(@xml).should == {
137
+ :artist_name => "Mos DefMos Def"
138
+ }
139
+ end
140
+
141
+ it "uses mapper method defined in xml_mapper when value does not respond to :after_map and given as hash" do
142
+ class << @mapper
143
+ def double(value)
144
+ value.to_s * 2
145
+ end
146
+ end
147
+ # [{:type=>:text, :xpath=>"Graphic/ImgFormat", :key=>:image_format, :options=>{:after_map=>:double}}]
148
+ @mapper.add_mapping(:text, { :artist_name => :name }, {:after_map => :double})
149
+ @mapper.attributes_from_xml(@xml).should == {
150
+ :name => "Mos DefMos Def"
151
+ }
152
+ end
153
+
154
+ it "takes a nokogiri node as argument" do
155
+ @mapper.add_mapping(:text, :artist_name)
156
+ @mapper.attributes_from_xml(Nokogiri::XML(@xml)).should == {
157
+ :artist_name => "Mos Def"
158
+ }
159
+ end
160
+
161
+ it "should also takes an array of nodes as argument" do
162
+ @mapper.add_mapping(:text, :artist_name)
163
+ @mapper.attributes_from_xml([Nokogiri::XML(@xml), Nokogiri::XML(@xml)]).should == [
164
+ { :artist_name => "Mos Def" },
165
+ { :artist_name => "Mos Def" }
166
+ ]
167
+ end
168
+
169
+ describe "mapping embedded attributes" do
170
+ before(:each) do
171
+ @xml = %(
172
+ <album>
173
+ <artist_name>Mos Def</artist_name>
174
+ <tracks>
175
+ <track>
176
+ <track_number>1</track_number>
177
+ <title>Track 1</title>
178
+ </track>
179
+ <track>
180
+ <track_number>2</track_number>
181
+ <title>Track 2</title>
182
+ </track>
183
+ </tracks>
184
+ </album>
185
+ )
186
+ end
187
+
188
+ it "maps all embedded attributes" do
189
+ submapper = XmlMapper.new
190
+ submapper.add_mapping(:integer, :track_number)
191
+ submapper.add_mapping(:text, :title)
192
+ @mapper.add_mapping(:text, :artist_name)
193
+ @mapper.add_mapping(:many, { "tracks/track" => :tracks }, :mapper => submapper)
194
+ @mapper.attributes_from_xml(@xml).should == {
195
+ :artist_name => "Mos Def",
196
+ :tracks => [
197
+ { :title => "Track 1", :track_number => 1 },
198
+ { :title => "Track 2", :track_number => 2 },
199
+ ]
200
+ }
201
+ end
202
+ end
203
+ end
204
+
205
+ describe "#attributes_from_xml_path" do
206
+ before(:each) do
207
+ @mapper.add_mapping(:text, :title)
208
+ @xml = %(
209
+ <album>
210
+ <title>Black on Both Sides</title>
211
+ </album>
212
+ )
213
+ File.stub(:read).and_return @xml
214
+ end
215
+
216
+ it "sets the xml_path" do
217
+ @mapper.attributes_from_xml_path("/some/path.xml").should == {
218
+ :title => "Black on Both Sides", :xml_path => "/some/path.xml"
219
+ }
220
+ end
221
+
222
+ it "calls File.read with correct parameters" do
223
+ File.should_receive(:read).with("/some/path.xml").and_return @xml
224
+ @mapper.attributes_from_xml_path("/some/path.xml")
225
+ end
226
+
227
+ it "allows using the xml_path in after_map block" do
228
+ @mapper.after_map do
229
+ self[:new_xml_path] = self[:xml_path]
230
+ end
231
+ @mapper.attributes_from_xml_path("/some/path.xml")[:new_xml_path].should == "/some/path.xml"
232
+ end
233
+
234
+ it "allows deleting the xml_path in after_map block" do
235
+ @mapper.after_map do
236
+ self.delete(:xml_path)
237
+ end
238
+ @mapper.attributes_from_xml_path("/some/path.xml").should_not have_key(:xml_path)
239
+ end
240
+ end
241
+
242
+ describe "#after_map" do
243
+ before(:each) do
244
+ @mapper.after_map do
245
+ self[:upc] = "1234"
246
+ end
247
+ end
248
+
249
+ it "assigns after_map block" do
250
+ @mapper.after_map_block.should_not be_nil
251
+ end
252
+
253
+ it "assigns a block to after_map_block" do
254
+ @mapper.after_map_block.should be_an_instance_of(Proc)
255
+ end
256
+
257
+ it "should executes after_map block after mapping" do
258
+ @mapper.attributes_from_xml("<album><title>Some Titel</title></album>").should == {
259
+ :upc => "1234"
260
+ }
261
+ end
262
+ end
263
+
264
+ describe "converting strings" do
265
+ describe "#string_to_boolean" do
266
+ {
267
+ "true" => true, "false" => false, "y" => true, "TRUE" => true, "" => nil, "YES" => true, "yes" => true,
268
+ "n" => false
269
+ }.each do |value, result|
270
+ it "converts #{value.inspect} to #{result}" do
271
+ @mapper.string_to_boolean(value).should == result
272
+ end
273
+ end
274
+ end
275
+ end
276
+
277
+ describe "defining a DSL" do
278
+ def create_class(base_class = "XmlMapper")
279
+ class_name = "TestMapping#{Time.now.to_f.to_s.gsub(".", "")}"
280
+ str = %(
281
+ class #{class_name} < #{base_class}
282
+ end
283
+ )
284
+ eval(str)
285
+ eval(class_name)
286
+ end
287
+
288
+ before(:each) do
289
+ # so that we have a new class in each spec
290
+ @clazz = create_class
291
+ end
292
+
293
+ it "initializes a mapper of the same class" do
294
+ @clazz.mapper.class.name.should == @clazz.name
295
+ end
296
+
297
+ it "sets the correct mapping for text keyword" do
298
+ @clazz.text(:title)
299
+ @clazz.mapper.mappings.should == [{ :type => :text, :key => :title, :xpath => "title", :options => {} }]
300
+ end
301
+
302
+ it "sets the correct mapping for node keyword" do
303
+ @clazz.node(:title)
304
+ @clazz.mapper.mappings.should == [{ :type => :node, :key => :title, :xpath => "title", :options => {} }]
305
+ end
306
+
307
+ it "sets the correct mapping for text keyword" do
308
+ @clazz.integer(:title)
309
+ @clazz.mapper.mappings.should == [{ :type => :integer, :key => :title, :xpath => "title", :options => {} }]
310
+ end
311
+
312
+ it "allows getting attributes form xml_path" do
313
+ File.stub(:read).and_return %(<album><title>Test Title</title></album>)
314
+ @clazz.text(:title)
315
+ @clazz.attributes_from_xml_path("/some/path.xml").should == {
316
+ :title => "Test Title",
317
+ :xml_path => "/some/path.xml"
318
+ }
319
+ end
320
+
321
+ it "allows defining a after_map block" do
322
+ @clazz.after_map do
323
+ self[:upc] = "1234"
324
+ end
325
+ @clazz.text(:title)
326
+ @clazz.attributes_from_xml(%(<album><title>Test Title</title></album>)).should == {
327
+ :upc => "1234", :title => "Test Title"
328
+ }
329
+ end
330
+
331
+ it "allows deleteing the xml path in after_block" do
332
+ @clazz.after_map do
333
+ self.delete(:xml_path)
334
+ end
335
+ File.stub(:read).and_return %(<album><title>Test Title</title></album>)
336
+ @clazz.attributes_from_xml_path("/some/path.xml").should_not have_key(:xml_path)
337
+ end
338
+
339
+ it "allows using attributes from superclass in after_map block"
340
+
341
+ it "allows using of instance methods of mapper for after_map" do
342
+ @clazz.class_eval do
343
+ def custom_mapper(txt)
344
+ txt * 2
345
+ end
346
+ end
347
+
348
+ @clazz.text(:title, :after_map => :custom_mapper)
349
+ @clazz.attributes_from_xml(%(<album><title>Test</title></album>)).should == {
350
+ :title => "TestTest"
351
+ }
352
+ end
353
+
354
+ it "accepts boolean as keyword" do
355
+ @clazz.boolean(:allows_streaming)
356
+ xml = %(<album><title>Test Title</title><allows_streaming>true</allows_streaming></album>)
357
+ @clazz.attributes_from_xml(xml).should == { :allows_streaming => true }
358
+ end
359
+
360
+ it "accepts exists as keyword" do
361
+ @clazz.exists("rights[country='DE']" => :allows_streaming)
362
+ xml = %(<album><title>Black on Both Sides</title><rights><country>DE</country></rights></album>)
363
+ @clazz.attributes_from_xml(xml).should == { :allows_streaming => true }
364
+ end
365
+
366
+ describe "#within" do
367
+ it "adds the within xpath to all xpath mappings" do
368
+ @clazz.within("artist") do
369
+ text :name => :artist_name
370
+ integer :id => :artist_id
371
+ end
372
+ @clazz.mapper.mappings.should == [
373
+ { :type => :text, :xpath => "artist/name", :key => :artist_name, :options => {} },
374
+ { :type => :integer, :xpath => "artist/id", :key => :artist_id, :options => {} },
375
+ ]
376
+ end
377
+
378
+ it "adds all nested within xpaths to xpath mappings" do
379
+ @clazz.within("contributions") do
380
+ within "artist" do
381
+ text :name => :artist_name
382
+ integer :id => :artist_id
383
+ end
384
+ end
385
+ @clazz.mapper.mappings.should == [
386
+ { :type => :text, :xpath => "contributions/artist/name", :key => :artist_name, :options => {} },
387
+ { :type => :integer, :xpath => "contributions/artist/id", :key => :artist_id, :options => {} },
388
+ ]
389
+ end
390
+
391
+ it "allows multiple within blocks on same level" do
392
+ @clazz.within "artist" do
393
+ text :name => :artist_name
394
+ end
395
+ @clazz.within "file" do
396
+ text :file_name
397
+ end
398
+ @clazz.mapper.mappings.should == [
399
+ { :type => :text, :xpath => "artist/name", :key => :artist_name, :options => {} },
400
+ { :type => :text, :xpath => "file/file_name", :key => :file_name, :options => {} },
401
+ ]
402
+ end
403
+ end
404
+
405
+ describe "with mapper hierarchy" do
406
+ it "attributes_from_xml includes superclass mapper as well" do
407
+ @clazz.text(:artist_name)
408
+ subclazz = create_class(@clazz.name)
409
+ subclazz.text(:title)
410
+ xml = %(
411
+ <album>
412
+ <artist_name>Mos Def</artist_name>
413
+ <title>Black on Both Sides</title>
414
+ </album>
415
+ )
416
+ subclazz.attributes_from_xml(xml).should == {
417
+ :artist_name => "Mos Def",
418
+ :title => "Black on Both Sides"
419
+ }
420
+ end
421
+
422
+ it "overwrites superclass mapper" do
423
+ @clazz.text(:artist_name)
424
+ subclazz = create_class(@clazz.name)
425
+ subclazz.text(:title)
426
+ subclazz.text(:artist_name, :after_map => :upcase)
427
+ xml = %(
428
+ <album>
429
+ <artist_name>Mos Def</artist_name>
430
+ <title>Black on Both Sides</title>
431
+ </album>
432
+ )
433
+ subclazz.attributes_from_xml(xml).should == {
434
+ :artist_name => "MOS DEF",
435
+ :title => "Black on Both Sides"
436
+ }
437
+ end
438
+
439
+ it "attributes_from_xml_path includes superclass mapper as well" do
440
+ @clazz.text(:artist_name)
441
+ subclazz = create_class(@clazz.name)
442
+ subclazz.text(:title)
443
+ xml = %(
444
+ <album>
445
+ <artist_name>Mos Def</artist_name>
446
+ <title>Black on Both Sides</title>
447
+ </album>
448
+ )
449
+ File.stub!(:read).and_return xml
450
+ subclazz.attributes_from_xml_path("/some_path/album.xml").should == {
451
+ :artist_name => "Mos Def",
452
+ :title => "Black on Both Sides",
453
+ :xml_path => "/some_path/album.xml"
454
+ }
455
+ end
456
+ end
457
+
458
+ describe "defining a submapper" do
459
+ before(:each) do
460
+ @clazz.many("tracks/track" => :tracks) do
461
+ text :title
462
+ integer :track_number
463
+ end
464
+ end
465
+
466
+ it "sets the mapping type to many" do
467
+ @clazz.mapper.mappings.first[:type].should == :many
468
+ end
469
+
470
+ it "sets the mapping key to track" do
471
+ @clazz.mapper.mappings.first[:key].should == :tracks
472
+ end
473
+
474
+ it "sets the mapping xpath to tracks/track" do
475
+ @clazz.mapper.mappings.first[:xpath].should == "tracks/track"
476
+ end
477
+
478
+ it "sets the correct submapper" do
479
+ @clazz.mapper.mappings.first[:options][:mapper].mappings.should == [
480
+ { :type => :text, :key => :title, :xpath => "title", :options => {} },
481
+ { :type => :integer, :key => :track_number, :xpath => "track_number", :options => {} },
482
+ ]
483
+ end
484
+
485
+ it "attributes_from_xml returns the correct attributes" do
486
+ @clazz.text(:artist_name)
487
+ @clazz.text(:title)
488
+ xml = %(
489
+ <album>
490
+ <artist_name>Mos Def</artist_name>
491
+ <title>Black on Both Sides</title>
492
+ <tracks>
493
+ <track>
494
+ <title>Track 1</title>
495
+ <track_number>1</track_number>
496
+ </track>
497
+ <track>
498
+ <title>Track 2</title>
499
+ <track_number>2</track_number>
500
+ </track>
501
+ </tracks>
502
+ </album>
503
+ )
504
+ @clazz.attributes_from_xml(xml).should == {
505
+ :artist_name => "Mos Def", :title => "Black on Both Sides",
506
+ :tracks => [
507
+ { :title => "Track 1", :track_number => 1 },
508
+ { :title => "Track 2", :track_number => 2 }
509
+ ]
510
+ }
511
+ end
512
+ end
513
+
514
+ end
515
+ end
@@ -0,0 +1,90 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{xml_mapper}
8
+ s.version = "0.3.2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Tobias Schwab"]
12
+ s.date = %q{2010-11-26}
13
+ s.description = %q{Just check out the examples}
14
+ s.email = %q{tobias.schwab@dynport.de}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".rspec",
22
+ ".rvmrc",
23
+ "Gemfile",
24
+ "Gemfile.lock",
25
+ "LICENSE.txt",
26
+ "README.rdoc",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "autotest/discover.rb",
30
+ "features/step_definitions/xml_mapper_steps.rb",
31
+ "features/support/env.rb",
32
+ "features/xml_mapper.feature",
33
+ "lib/xml_mapper.rb",
34
+ "spec/example_spec.rb",
35
+ "spec/fixtures/base.xml",
36
+ "spec/my_mapper.rb",
37
+ "spec/spec_helper.rb",
38
+ "spec/xml_mapper_spec.rb",
39
+ "xml_mapper.gemspec"
40
+ ]
41
+ s.homepage = %q{http://github.com/tobstarr/xml_mapper}
42
+ s.licenses = ["MIT"]
43
+ s.require_paths = ["lib"]
44
+ s.rubygems_version = %q{1.3.7}
45
+ s.summary = %q{Declarative and clever XML to Ruby Mapping}
46
+ s.test_files = [
47
+ "spec/example_spec.rb",
48
+ "spec/my_mapper.rb",
49
+ "spec/spec_helper.rb",
50
+ "spec/xml_mapper_spec.rb"
51
+ ]
52
+
53
+ if s.respond_to? :specification_version then
54
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
55
+ s.specification_version = 3
56
+
57
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
58
+ s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
59
+ s.add_development_dependency(%q<autotest>, [">= 0"])
60
+ s.add_development_dependency(%q<autotest-growl>, [">= 0"])
61
+ s.add_development_dependency(%q<rspec>, ["~> 2.1.0"])
62
+ s.add_development_dependency(%q<ruby-debug>, [">= 0"])
63
+ s.add_development_dependency(%q<cucumber>, [">= 0"])
64
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
65
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.1"])
66
+ s.add_development_dependency(%q<rcov>, [">= 0"])
67
+ else
68
+ s.add_dependency(%q<nokogiri>, [">= 0"])
69
+ s.add_dependency(%q<autotest>, [">= 0"])
70
+ s.add_dependency(%q<autotest-growl>, [">= 0"])
71
+ s.add_dependency(%q<rspec>, ["~> 2.1.0"])
72
+ s.add_dependency(%q<ruby-debug>, [">= 0"])
73
+ s.add_dependency(%q<cucumber>, [">= 0"])
74
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
75
+ s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
76
+ s.add_dependency(%q<rcov>, [">= 0"])
77
+ end
78
+ else
79
+ s.add_dependency(%q<nokogiri>, [">= 0"])
80
+ s.add_dependency(%q<autotest>, [">= 0"])
81
+ s.add_dependency(%q<autotest-growl>, [">= 0"])
82
+ s.add_dependency(%q<rspec>, ["~> 2.1.0"])
83
+ s.add_dependency(%q<ruby-debug>, [">= 0"])
84
+ s.add_dependency(%q<cucumber>, [">= 0"])
85
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
86
+ s.add_dependency(%q<jeweler>, ["~> 1.5.1"])
87
+ s.add_dependency(%q<rcov>, [">= 0"])
88
+ end
89
+ end
90
+