jimmyz-happymapper 0.3.1

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