kdonovan-happymapper 0.3.4

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.
@@ -0,0 +1,694 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+ require File.dirname(__FILE__) + '/xml_helper.rb'
3
+ require 'pp'
4
+ require 'uri'
5
+
6
+ class Address
7
+ include HappyMapper
8
+
9
+ tag 'address'
10
+ element :street, String
11
+ element :postcode, String
12
+ element :housenumber, String
13
+ element :city, String
14
+ element :country, String
15
+ end
16
+
17
+ class Feature
18
+ include HappyMapper
19
+ element :name, String, :tag => '.|.//text()'
20
+ end
21
+
22
+ class FeatureBullet
23
+ include HappyMapper
24
+
25
+ tag 'features_bullets'
26
+ has_many :features, Feature
27
+ element :bug, String
28
+ end
29
+
30
+ class Product
31
+ include HappyMapper
32
+
33
+ element :title, String
34
+ has_one :feature_bullets, FeatureBullet
35
+ has_one :address, Address
36
+ end
37
+
38
+ module FamilySearch
39
+ class AlternateIds
40
+ include HappyMapper
41
+
42
+ namespace 'fsapi-v1' => 'http://api.familysearch.org/v1'
43
+ tag 'alternateIds'
44
+ has_many :ids, String, :tag => 'id'
45
+ end
46
+
47
+ class Information
48
+ include HappyMapper
49
+
50
+ namespace 'fsapi-v1' => 'http://api.familysearch.org/v1'
51
+ has_one :alternateIds, AlternateIds
52
+ element :gender, String
53
+ element :living, Boolean
54
+ end
55
+
56
+ class Person
57
+ include HappyMapper
58
+
59
+ namespace_url 'http://api.familysearch.org/familytree/v1'
60
+ attribute :version, String
61
+ attribute :modified, Time
62
+ attribute :id, String
63
+ has_one :information, Information
64
+ end
65
+
66
+ class Persons
67
+ include HappyMapper
68
+
69
+ namespace_url 'http://api.familysearch.org/familytree/v1'
70
+ has_many :person, Person
71
+ end
72
+
73
+ class FamilyTree
74
+ include HappyMapper
75
+
76
+ tag 'familytree'
77
+ namespace_url 'http://api.familysearch.org/familytree/v1'
78
+ attribute :version, String
79
+ attribute :status_message, String, :tag => 'statusMessage'
80
+ attribute :status_code, String, :tag => 'statusCode'
81
+ has_one :persons, Persons
82
+ end
83
+ end
84
+
85
+ module FedEx
86
+ class Address
87
+ include HappyMapper
88
+
89
+ tag 'Address'
90
+ namespace 'v2'
91
+ element :city, String, :tag => 'City'
92
+ element :state, String, :tag => 'StateOrProvinceCode'
93
+ element :zip, String, :tag => 'PostalCode'
94
+ element :countrycode, String, :tag => 'CountryCode'
95
+ element :residential, Boolean, :tag => 'Residential'
96
+ end
97
+
98
+ class Event
99
+ include HappyMapper
100
+
101
+ tag 'Events'
102
+ namespace 'v2'
103
+ element :timestamp, String, :tag => 'Timestamp'
104
+ element :eventtype, String, :tag => 'EventType'
105
+ element :eventdescription, String, :tag => 'EventDescription'
106
+ has_one :address, Address
107
+ end
108
+
109
+ class PackageWeight
110
+ include HappyMapper
111
+
112
+ tag 'PackageWeight'
113
+ namespace 'v2'
114
+ element :units, String, :tag => 'Units'
115
+ element :value, Integer, :tag => 'Value'
116
+ end
117
+
118
+ class TrackDetails
119
+ include HappyMapper
120
+
121
+ tag 'TrackDetails'
122
+ namespace 'v2'
123
+ element :tracking_number, String, :tag => 'TrackingNumber'
124
+ element :status_code, String, :tag => 'StatusCode'
125
+ element :status_desc, String, :tag => 'StatusDescription'
126
+ element :carrier_code, String, :tag => 'CarrierCode'
127
+ element :service_info, String, :tag => 'ServiceInfo'
128
+ has_one :weight, PackageWeight, :tag => 'PackageWeight'
129
+ element :est_delivery, String, :tag => 'EstimatedDeliveryTimestamp'
130
+ has_many :events, Event
131
+ end
132
+
133
+ class Notification
134
+ include HappyMapper
135
+
136
+ tag 'Notifications'
137
+ namespace 'v2'
138
+ element :severity, String, :tag => 'Severity'
139
+ element :source, String, :tag => 'Source'
140
+ element :code, Integer, :tag => 'Code'
141
+ element :message, String, :tag => 'Message'
142
+ element :localized_message, String, :tag => 'LocalizedMessage'
143
+ end
144
+
145
+ class TransactionDetail
146
+ include HappyMapper
147
+
148
+ tag 'TransactionDetail'
149
+ namespace 'v2'
150
+ element :cust_tran_id, String, :tag => 'CustomerTransactionId'
151
+ end
152
+
153
+ class TrackReply
154
+ include HappyMapper
155
+
156
+ tag 'TrackReply'
157
+ namespace 'v2'
158
+ element :highest_severity, String, :tag => 'HighestSeverity'
159
+ element :more_data, Boolean, :tag => 'MoreData'
160
+ has_many :notifications, Notification, :tag => 'Notifications'
161
+ has_many :trackdetails, TrackDetails, :tag => 'TrackDetails'
162
+ has_one :tran_detail, TransactionDetail, :tab => 'TransactionDetail'
163
+ end
164
+ end
165
+
166
+ class Place
167
+ include HappyMapper
168
+ element :name, String
169
+ end
170
+
171
+ class Radar
172
+ include HappyMapper
173
+ # places needs to be deep because it lives out in places/place
174
+ has_many :places, Place, :deep => true
175
+ end
176
+
177
+ class Post
178
+ include HappyMapper
179
+
180
+ attribute :href, String
181
+ attribute :hash, String
182
+ attribute :description, String
183
+ attribute :tag, String
184
+ attribute :time, Time
185
+ attribute :others, Integer
186
+ attribute :extended, String
187
+ end
188
+
189
+ class Posts
190
+ include HappyMapper
191
+
192
+ attribute :user, String
193
+ attribute :tag, String
194
+ has_many :post, Post
195
+ end
196
+
197
+ class User
198
+ include HappyMapper
199
+
200
+ element :id, Integer
201
+ element :name, String
202
+ element :screen_name, String
203
+ element :location, String
204
+ element :description, String
205
+ element :profile_image_url, String
206
+ element :url, String
207
+ element :protected, Boolean
208
+ element :followers_count, Integer
209
+ end
210
+
211
+ class Status
212
+ include HappyMapper
213
+
214
+ element :id, Integer
215
+ element :text, String
216
+ element :created_at, Time
217
+ element :source, String
218
+ element :truncated, Boolean
219
+ element :in_reply_to_status_id, Integer
220
+ element :in_reply_to_user_id, Integer
221
+ element :favorited, Boolean
222
+ element :non_existent, String, :tag => 'dummy', :namespace => 'fake'
223
+ has_one :user, User
224
+ end
225
+
226
+ class CurrentWeather
227
+ include HappyMapper
228
+
229
+ tag 'ob'
230
+ namespace 'aws'
231
+ element :temperature, Integer, :tag => 'temp'
232
+ element :feels_like, Integer, :tag => 'feels-like'
233
+ element :current_condition, String, :tag => 'current-condition', :attributes => {:icon => String}
234
+ end
235
+
236
+ # for type coercion
237
+ class ProductGroup < String; end
238
+
239
+ module PITA
240
+ class Item
241
+ include HappyMapper
242
+
243
+ tag 'Item' # if you put class in module you need tag
244
+ element :asin, String, :tag => 'ASIN'
245
+ element :detail_page_url, URI, :tag => 'DetailPageURL', :parser => :parse
246
+ element :manufacturer, String, :tag => 'Manufacturer', :deep => true
247
+ element :point, String, :tag => 'point', :namespace => 'georss'
248
+ element :product_group, ProductGroup, :tag => 'ProductGroup', :deep => true, :parser => :new, :raw => true
249
+ end
250
+
251
+ class Items
252
+ include HappyMapper
253
+
254
+ tag 'Items' # if you put class in module you need tag
255
+ element :total_results, Integer, :tag => 'TotalResults'
256
+ element :total_pages, Integer, :tag => 'TotalPages'
257
+ has_many :items, Item
258
+ end
259
+ end
260
+
261
+ module GitHub
262
+ class Commit
263
+ include HappyMapper
264
+
265
+ tag "commit"
266
+ element :url, String
267
+ element :tree, String
268
+ element :message, String
269
+ element :id, String
270
+ element :'committed-date', Date
271
+ end
272
+ end
273
+
274
+ describe HappyMapper do
275
+
276
+ describe "being included into another class" do
277
+ before do
278
+ Foo.instance_variable_set("@attributes", {})
279
+ Foo.instance_variable_set("@elements", {})
280
+ end
281
+ class Foo; include HappyMapper end
282
+
283
+ it "should set attributes to an array" do
284
+ Foo.attributes.should == []
285
+ end
286
+
287
+ it "should set @elements to a hash" do
288
+ Foo.elements.should == []
289
+ end
290
+
291
+ it "should allow adding an attribute" do
292
+ lambda {
293
+ Foo.attribute :name, String
294
+ }.should change(Foo, :attributes)
295
+ end
296
+
297
+ it "should allow adding an attribute containing a dash" do
298
+ lambda {
299
+ Foo.attribute :'bar-baz', String
300
+ }.should change(Foo, :attributes)
301
+ end
302
+
303
+ it "should be able to get all attributes in array" do
304
+ Foo.attribute :name, String
305
+ Foo.attributes.size.should == 1
306
+ end
307
+
308
+ it "should allow adding an element" do
309
+ lambda {
310
+ Foo.element :name, String
311
+ }.should change(Foo, :elements)
312
+ end
313
+
314
+ it "should allow adding an element containing a dash" do
315
+ lambda {
316
+ Foo.element :'bar-baz', String
317
+ }.should change(Foo, :elements)
318
+
319
+ end
320
+
321
+ it "should be able to get all elements in array" do
322
+ Foo.element(:name, String)
323
+ Foo.elements.size.should == 1
324
+ end
325
+
326
+ it "should allow has one association" do
327
+ Foo.has_one(:user, User)
328
+ element = Foo.elements.first
329
+ element.name.should == 'user'
330
+ element.type.should == User
331
+ element.options[:single] = true
332
+ end
333
+
334
+ it "should allow has many association" do
335
+ Foo.has_many(:users, User)
336
+ element = Foo.elements.first
337
+ element.name.should == 'users'
338
+ element.type.should == User
339
+ element.options[:single] = false
340
+ end
341
+
342
+ it "should default tag name to lowercase class" do
343
+ Foo.tag_name.should == 'foo'
344
+ end
345
+
346
+ it "should default tag name of class in modules to the last constant lowercase" do
347
+ module Bar; class Baz; include HappyMapper; end; end
348
+ Bar::Baz.tag_name.should == 'baz'
349
+ end
350
+
351
+ it "should allow setting tag name" do
352
+ Foo.tag('FooBar')
353
+ Foo.tag_name.should == 'FooBar'
354
+ end
355
+
356
+ it "should allow setting a namespace" do
357
+ Foo.namespace(namespace = "foo")
358
+ Foo.namespace.should == namespace
359
+ end
360
+
361
+ # This is important for allowing serializing of elements with namespaces
362
+ it "should allow setting a full namespace (prefix and url) via a hash" do
363
+ Foo.namespace(namespace = {'example-v1' => 'http://example.com/v1'})
364
+ Foo.namespace.should == 'example-v1'
365
+ Foo.namespace_url.should == 'http://example.com/v1'
366
+ end
367
+
368
+ # Prefixes might change for a given namespace. It is safer to namespace by url.
369
+ it "should allow setting a namespace url" do
370
+ Foo.namespace_url(url = 'http://example.com/namespace')
371
+ Foo.namespace_url.should == url
372
+ end
373
+
374
+ it "should provide #parse" do
375
+ Foo.should respond_to(:parse)
376
+ end
377
+ end
378
+
379
+ describe "#attributes" do
380
+ it "should only return attributes for the current class" do
381
+ Post.attributes.size.should == 7
382
+ Status.attributes.size.should == 0
383
+ end
384
+ end
385
+
386
+ describe "#elements" do
387
+ it "should only return elements for the current class" do
388
+ Post.elements.size.should == 0
389
+ Status.elements.size.should == 10
390
+ end
391
+ end
392
+
393
+ it "should parse xml attributes into ruby objects" do
394
+ posts = Post.parse(fixture_file('posts.xml'))
395
+ posts.size.should == 20
396
+ first = posts.first
397
+ first.href.should == 'http://roxml.rubyforge.org/'
398
+ first.hash.should == '19bba2ab667be03a19f67fb67dc56917'
399
+ first.description.should == 'ROXML - Ruby Object to XML Mapping Library'
400
+ first.tag.should == 'ruby xml gems mapping'
401
+ first.time.should == Time.utc(2008, 8, 9, 5, 24, 20)
402
+ first.others.should == 56
403
+ first.extended.should == 'ROXML is a Ruby library designed to make it easier for Ruby developers to work with XML. Using simple annotations, it enables Ruby classes to be custom-mapped to XML. ROXML takes care of the marshalling and unmarshalling of mapped attributes so that developers can focus on building first-class Ruby classes.'
404
+ end
405
+
406
+ it "should parse xml elements to ruby objcts" do
407
+ statuses = Status.parse(fixture_file('statuses.xml'))
408
+ statuses.size.should == 20
409
+ first = statuses.first
410
+ first.id.should == 882281424
411
+ first.created_at.should == Time.utc(2008, 8, 9, 5, 38, 12)
412
+ first.source.should == 'web'
413
+ first.truncated.should be_false
414
+ first.in_reply_to_status_id.should == 1234
415
+ first.in_reply_to_user_id.should == 12345
416
+ first.favorited.should be_false
417
+ first.user.id.should == 4243
418
+ first.user.name.should == 'John Nunemaker'
419
+ first.user.screen_name.should == 'jnunemaker'
420
+ first.user.location.should == 'Mishawaka, IN, US'
421
+ first.user.description.should == 'Loves his wife, ruby, notre dame football and iu basketball'
422
+ first.user.profile_image_url.should == 'http://s3.amazonaws.com/twitter_production/profile_images/53781608/Photo_75_normal.jpg'
423
+ first.user.url.should == 'http://addictedtonew.com'
424
+ first.user.protected.should be_false
425
+ first.user.followers_count.should == 486
426
+ end
427
+
428
+ it "should parse xml containing the desired element as root node" do
429
+ address = Address.parse(fixture_file('address.xml'), :single => true)
430
+ address.street.should == 'Milchstrasse'
431
+ address.postcode.should == '26131'
432
+ address.housenumber.should == '23'
433
+ address.city.should == 'Oldenburg'
434
+ address.country.should == 'Germany'
435
+ end
436
+
437
+ it "should parse xml with default namespace (amazon)" do
438
+ file_contents = fixture_file('pita.xml')
439
+ items = PITA::Items.parse(file_contents, :single => true)
440
+ items.total_results.should == 22
441
+ items.total_pages.should == 3
442
+ first = items.items[0]
443
+ second = items.items[1]
444
+ first.asin.should == '0321480791'
445
+ first.point.should == '38.5351715088 -121.7948684692'
446
+ first.detail_page_url.should be_a_kind_of(URI)
447
+ first.detail_page_url.to_s.should == 'http://www.amazon.com/gp/redirect.html%3FASIN=0321480791%26tag=ws%26lcode=xm2%26cID=2025%26ccmID=165953%26location=/o/ASIN/0321480791%253FSubscriptionId=dontbeaswoosh'
448
+ first.manufacturer.should == 'Addison-Wesley Professional'
449
+ first.product_group.should == '<ProductGroup>Book</ProductGroup>'
450
+ second.asin.should == '047022388X'
451
+ second.manufacturer.should == 'Wrox'
452
+ end
453
+
454
+ it "should parse xml that has attributes of elements" do
455
+ items = CurrentWeather.parse(fixture_file('current_weather.xml'))
456
+ first = items[0]
457
+ first.temperature.should == 51
458
+ first.feels_like.should == 51
459
+ first.current_condition.should == 'Sunny'
460
+ first.current_condition.icon.should == 'http://deskwx.weatherbug.com/images/Forecast/icons/cond007.gif'
461
+ end
462
+
463
+ it "should parse xml with nested elements" do
464
+ radars = Radar.parse(fixture_file('radar.xml'))
465
+ first = radars[0]
466
+ first.places.size.should == 1
467
+ first.places[0].name.should == 'Store'
468
+ second = radars[1]
469
+ second.places.size.should == 0
470
+ third = radars[2]
471
+ third.places.size.should == 2
472
+ third.places[0].name.should == 'Work'
473
+ third.places[1].name.should == 'Home'
474
+ end
475
+
476
+ it "should parse xml that has elements with dashes" do
477
+ commit = GitHub::Commit.parse(fixture_file('commit.xml'))
478
+ commit.message.should == "move commands.rb and helpers.rb into commands/ dir"
479
+ commit.url.should == "http://github.com/defunkt/github-gem/commit/c26d4ce9807ecf57d3f9eefe19ae64e75bcaaa8b"
480
+ commit.id.should == "c26d4ce9807ecf57d3f9eefe19ae64e75bcaaa8b"
481
+ commit.committed_date.should == Date.parse("2008-03-02T16:45:41-08:00")
482
+ commit.tree.should == "28a1a1ca3e663d35ba8bf07d3f1781af71359b76"
483
+ end
484
+
485
+ it "should parse xml with no namespace" do
486
+ product = Product.parse(fixture_file('product_no_namespace.xml'), :single => true)
487
+ product.title.should == "A Title"
488
+ product.feature_bullets.bug.should == 'This is a bug'
489
+ product.feature_bullets.features.size.should == 2
490
+ product.feature_bullets.features[0].name.should == 'This is feature text 1'
491
+ product.feature_bullets.features[1].name.should == 'This is feature text 2'
492
+ end
493
+
494
+ it "should parse xml with default namespace" do
495
+ product = Product.parse(fixture_file('product_default_namespace.xml'), :single => true)
496
+ product.title.should == "A Title"
497
+ product.feature_bullets.bug.should == 'This is a bug'
498
+ product.feature_bullets.features.size.should == 2
499
+ product.feature_bullets.features[0].name.should == 'This is feature text 1'
500
+ product.feature_bullets.features[1].name.should == 'This is feature text 2'
501
+ end
502
+
503
+ it "should parse xml with single namespace" do
504
+ product = Product.parse(fixture_file('product_single_namespace.xml'), :single => true)
505
+ product.title.should == "A Title"
506
+ product.feature_bullets.bug.should == 'This is a bug'
507
+ product.feature_bullets.features.size.should == 2
508
+ product.feature_bullets.features[0].name.should == 'This is feature text 1'
509
+ product.feature_bullets.features[1].name.should == 'This is feature text 2'
510
+ end
511
+
512
+ it "should parse xml with multiple namespaces" do
513
+ track = FedEx::TrackReply.parse(fixture_file('multiple_namespaces.xml'))
514
+ track.highest_severity.should == 'SUCCESS'
515
+ track.more_data.should be_false
516
+ notification = track.notifications.first
517
+ notification.code.should == 0
518
+ notification.localized_message.should == 'Request was successfully processed.'
519
+ notification.message.should == 'Request was successfully processed.'
520
+ notification.severity.should == 'SUCCESS'
521
+ notification.source.should == 'trck'
522
+ detail = track.trackdetails.first
523
+ detail.carrier_code.should == 'FDXG'
524
+ detail.est_delivery.should == '2009-01-02T00:00:00'
525
+ detail.service_info.should == 'Ground-Package Returns Program-Domestic'
526
+ detail.status_code.should == 'OD'
527
+ detail.status_desc.should == 'On FedEx vehicle for delivery'
528
+ detail.tracking_number.should == '9611018034267800045212'
529
+ detail.weight.units.should == 'LB'
530
+ detail.weight.value.should == 2
531
+ events = detail.events
532
+ events.size.should == 10
533
+ first_event = events[0]
534
+ first_event.eventdescription.should == 'On FedEx vehicle for delivery'
535
+ first_event.eventtype.should == 'OD'
536
+ first_event.timestamp.should == '2009-01-02T06:00:00'
537
+ first_event.address.city.should == 'WICHITA'
538
+ first_event.address.countrycode.should == 'US'
539
+ first_event.address.residential.should be_false
540
+ first_event.address.state.should == 'KS'
541
+ first_event.address.zip.should == '67226'
542
+ last_event = events[-1]
543
+ last_event.eventdescription.should == 'In FedEx possession'
544
+ last_event.eventtype.should == 'IP'
545
+ last_event.timestamp.should == '2008-12-27T09:40:00'
546
+ last_event.address.city.should == 'LONGWOOD'
547
+ last_event.address.countrycode.should == 'US'
548
+ last_event.address.residential.should be_false
549
+ last_event.address.state.should == 'FL'
550
+ last_event.address.zip.should == '327506398'
551
+ track.tran_detail.cust_tran_id.should == '20090102-111321'
552
+ end
553
+
554
+ it "should not parse deep for elements unless deep option is set" do
555
+ tree = FamilySearch::FamilyTree.parse(fixture_file('family_tree.xml'))
556
+ tree.persons.person.size.should_not == 2
557
+ tree.persons.person.size.should == 1
558
+ end
559
+
560
+ it "should parse family search xml" do
561
+ tree = FamilySearch::FamilyTree.parse(fixture_file('family_tree.xml'))
562
+ tree.version.should == '1.0.20071213.942'
563
+ tree.status_message.should == 'OK'
564
+ tree.status_code.should == '200'
565
+ tree.persons.person.size.should == 1
566
+ tree.persons.person.first.version.should == '1199378491000'
567
+ tree.persons.person.first.modified.should == Time.utc(2008, 1, 3, 16, 41, 31) # 2008-01-03T09:41:31-07:00
568
+ tree.persons.person.first.id.should == 'KWQS-BBQ'
569
+ # namespaced elements (by url)
570
+ tree.persons.person.first.information.alternateIds.ids.should_not be_kind_of(String)
571
+ tree.persons.person.first.information.alternateIds.ids.size.should == 8
572
+ tree.persons.person.first.information.gender.should == 'Male'
573
+ tree.persons.person.first.information.living.should == false
574
+ end
575
+
576
+ it "should not set values for missing attributes" do
577
+ posts = Post.parse(fixture_file("partial_posts.xml"))
578
+ first = posts.first
579
+ first.href.should be_nil
580
+ first.hash.should be_nil
581
+ first.description.should be_nil
582
+ first.tag.should be_nil
583
+ first.time.should be_nil
584
+ first.others.should be_nil
585
+ first.extended.should be_nil
586
+ end
587
+
588
+ describe "serializing to xml" do
589
+
590
+ it "should return xml with elements representing the objects" do
591
+ address = Address.parse(fixture_file('address.xml'), :single => true)
592
+ xml = address.to_xml
593
+ xml.should be_kind_of(String)
594
+ # Would love to use libxml-ruby if xml_helper.rb were converted
595
+ doc = REXML::Document.new xml
596
+ doc.should have_nodes("/address", 1)
597
+ doc.should have_nodes("/address/street", 1)
598
+ doc.should match_xpath("/address/street","Milchstrasse")
599
+ doc.should match_xpath("/address/housenumber","23")
600
+ doc.should match_xpath("/address/postcode","26131")
601
+ doc.should match_xpath("/address/city","Oldenburg")
602
+ doc.should match_xpath("/address/country","Germany")
603
+ end
604
+
605
+ it "should include an xml version/encoding header" do
606
+ address = Address.new
607
+ xml = address.to_xml
608
+ xml.should =~ /<?xml version=".*" encoding=".*"?>/
609
+ end
610
+
611
+ it "should return xml with non-primitive elements from ruby objects" do
612
+ posts = Posts.parse(fixture_file('posts.xml'))
613
+ xml = posts.to_xml
614
+ doc = REXML::Document.new xml
615
+ doc.should have_nodes("/posts",1)
616
+ doc.should have_nodes("/posts/post",20)
617
+ end
618
+
619
+ it "should return xml with attributes representing 'attribute' objects" do
620
+ posts = Posts.parse(fixture_file('posts.xml'))
621
+ xml = posts.to_xml
622
+ doc = REXML::Document.new xml
623
+ doc.should have_nodes("/posts/post",20)
624
+ doc.should match_xpath("/posts/post[1]/@href","http://roxml.rubyforge.org/")
625
+ doc.should match_xpath("/posts/post[1]/@hash","19bba2ab667be03a19f67fb67dc56917")
626
+ doc.should match_xpath("/posts/post[1]/@description","ROXML - Ruby Object to XML Mapping Library")
627
+ doc.should match_xpath("/posts/post[1]/@tag","ruby xml gems mapping")
628
+ doc.should match_xpath("/posts/post[1]/@others","56")
629
+ doc.should match_xpath("/posts/post[1]/@extended","ROXML is a Ruby library designed to make it easier for Ruby developers to work with XML. Using simple annotations, it enables Ruby classes to be custom-mapped to XML. ROXML takes care of the marshalling and unmarshalling of mapped attributes so that developers can focus on building first-class Ruby classes.")
630
+ end
631
+
632
+ it "should be able to create new objects and serialize to xml" do
633
+ posts = Posts.new
634
+ posts.user = 'jimmyz'
635
+ posts.tag = 'happymapper'
636
+ posts.post << Post.new
637
+ posts.post << Post.new
638
+ xml = posts.to_xml
639
+ doc = REXML::Document.new xml
640
+ doc.should have_nodes("/posts",1)
641
+ doc.should have_nodes("/posts/post", 2)
642
+ doc.should match_xpath("/posts/@user",'jimmyz')
643
+ doc.should match_xpath("/posts/@tag",'happymapper')
644
+ end
645
+
646
+ it "should not serialize attributes or elements that are nil" do
647
+ posts = Posts.new
648
+ xml = posts.to_xml
649
+ doc = REXML::Document.new xml
650
+ doc.should have_nodes("/posts",1)
651
+ doc.should have_nodes("/posts/post",0)
652
+ doc.should have_nodes("/posts/@user",0)
653
+ end
654
+
655
+ it "should not serialize elements that are nil" do
656
+ ft = FamilySearch::FamilyTree.new
657
+ #ft has nil persons element should not raise error
658
+ lambda{
659
+ ft.to_xml
660
+ }.should_not raise_error
661
+
662
+ # Check for nil elements of primitive type
663
+ info = FamilySearch::Information.new
664
+ namespace = {'fsapi-v1' => 'http://api.familysearch.org/v1'}
665
+ doc = REXML::Document.new info.to_xml
666
+ doc.should have_nodes("/fsapi-v1:information/fsapi-v1:gender",0,namespace)
667
+ doc.should have_nodes("/fsapi-v1:information/fsapi-v1:living",0,namespace)
668
+ end
669
+
670
+ describe "with namespace url and prefix" do
671
+ before(:all) do
672
+ ft = FamilySearch::FamilyTree.parse(fixture_file('family_tree.xml'))
673
+ xml = ft.to_xml
674
+ @doc = REXML::Document.new xml
675
+ @namespaces = {
676
+ 'fsapi-v1' => 'http://api.familysearch.org/v1',
677
+ 'fs' => 'http://api.familysearch.org/familytree/v1'
678
+ }
679
+ end
680
+
681
+ it "should add namespaces to the root element that is being serialized" do
682
+ @doc.should match_xpath("/fs:familytree/@xmlns:fsapi-v1",'http://api.familysearch.org/v1',@namespaces)
683
+ end
684
+
685
+ it "should serialize elements with a namespace url and prefix" do
686
+ base_xpath = "/fs:familytree/fs:persons/fs:person[1]"
687
+ @doc.should have_nodes("#{base_xpath}/fsapi-v1:information",1,@namespaces)
688
+ @doc.should have_nodes("#{base_xpath}/fsapi-v1:information/fsapi-v1:alternateIds",1,@namespaces)
689
+ @doc.should have_nodes("#{base_xpath}/fsapi-v1:information/fsapi-v1:alternateIds/fsapi-v1:id",8,@namespaces)
690
+ end
691
+ end
692
+
693
+ end
694
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --colour