legolin-happymapper 0.3.0

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.
Files changed (44) hide show
  1. data/HOW_TO_RELEASE +5 -0
  2. data/History +59 -0
  3. data/License +20 -0
  4. data/Manifest +42 -0
  5. data/README +61 -0
  6. data/Rakefile +43 -0
  7. data/TODO +0 -0
  8. data/examples/amazon.rb +34 -0
  9. data/examples/current_weather.rb +21 -0
  10. data/examples/dashed_elements.rb +20 -0
  11. data/examples/multi_street_address.rb +15 -0
  12. data/examples/post.rb +19 -0
  13. data/examples/twitter.rb +37 -0
  14. data/happymapper.gemspec +0 -0
  15. data/legolin-happymapper.gemspec +34 -0
  16. data/lib/happymapper.rb +161 -0
  17. data/lib/happymapper/attribute.rb +3 -0
  18. data/lib/happymapper/element.rb +3 -0
  19. data/lib/happymapper/item.rb +192 -0
  20. data/lib/happymapper/version.rb +3 -0
  21. data/spec/fixtures/address.xml +8 -0
  22. data/spec/fixtures/analytics.xml +61 -0
  23. data/spec/fixtures/commit.xml +52 -0
  24. data/spec/fixtures/current_weather.xml +89 -0
  25. data/spec/fixtures/family_tree.xml +7 -0
  26. data/spec/fixtures/multi_street_address.xml +9 -0
  27. data/spec/fixtures/multiple_namespaces.xml +170 -0
  28. data/spec/fixtures/nested_namespaces.xml +17 -0
  29. data/spec/fixtures/pita.xml +133 -0
  30. data/spec/fixtures/posts.xml +23 -0
  31. data/spec/fixtures/product_default_namespace.xml +10 -0
  32. data/spec/fixtures/product_no_namespace.xml +10 -0
  33. data/spec/fixtures/product_single_namespace.xml +10 -0
  34. data/spec/fixtures/radar.xml +21 -0
  35. data/spec/fixtures/statuses.xml +422 -0
  36. data/spec/happymapper_attribute_spec.rb +17 -0
  37. data/spec/happymapper_element_spec.rb +17 -0
  38. data/spec/happymapper_item_spec.rb +115 -0
  39. data/spec/happymapper_spec.rb +719 -0
  40. data/spec/spec.opts +1 -0
  41. data/spec/spec_helper.rb +13 -0
  42. data/website/css/common.css +47 -0
  43. data/website/index.html +98 -0
  44. metadata +129 -0
@@ -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,115 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ module Foo
4
+ class Bar; end
5
+ end
6
+
7
+ describe HappyMapper::Item do
8
+
9
+ describe "new instance" do
10
+ before do
11
+ @item = HappyMapper::Item.new(:foo, String, :tag => 'foobar')
12
+ end
13
+
14
+ it "should accept a name" do
15
+ @item.name.should == 'foo'
16
+ end
17
+
18
+ it 'should accept a type' do
19
+ @item.type.should == String
20
+ end
21
+
22
+ it 'should accept :tag as an option' do
23
+ @item.tag.should == 'foobar'
24
+ end
25
+
26
+ it "should have a method_name" do
27
+ @item.method_name.should == 'foo'
28
+ end
29
+ end
30
+
31
+ describe "#constant" do
32
+ it "should just use type if constant" do
33
+ item = HappyMapper::Item.new(:foo, String)
34
+ item.constant.should == String
35
+ end
36
+
37
+ it "should convert string type to constant" do
38
+ item = HappyMapper::Item.new(:foo, 'String')
39
+ item.constant.should == String
40
+ end
41
+
42
+ it "should convert string with :: to constant" do
43
+ item = HappyMapper::Item.new(:foo, 'Foo::Bar')
44
+ item.constant.should == Foo::Bar
45
+ end
46
+ end
47
+
48
+ describe "#method_name" do
49
+ it "should convert dashes to underscores" do
50
+ item = HappyMapper::Item.new(:'foo-bar', String, :tag => 'foobar')
51
+ item.method_name.should == 'foo_bar'
52
+ end
53
+ end
54
+
55
+ describe "#xpath" do
56
+ it "should default to tag" do
57
+ item = HappyMapper::Item.new(:foo, String, :tag => 'foobar')
58
+ item.xpath.should == 'foobar'
59
+ end
60
+
61
+ it "should prepend with .// if options[:deep] true" do
62
+ item = HappyMapper::Item.new(:foo, String, :tag => 'foobar', :deep => true)
63
+ item.xpath.should == './/foobar'
64
+ end
65
+
66
+ it "should prepend namespace if namespace exists" do
67
+ item = HappyMapper::Item.new(:foo, String, :tag => 'foobar')
68
+ item.namespace = 'http://example.com'
69
+ item.xpath.should == 'happymapper:foobar'
70
+ end
71
+ end
72
+
73
+ describe "typecasting" do
74
+ it "should work with Strings" do
75
+ item = HappyMapper::Item.new(:foo, String)
76
+ [21, '21'].each do |a|
77
+ item.typecast(a).should == '21'
78
+ end
79
+ end
80
+
81
+ it "should work with Integers" do
82
+ item = HappyMapper::Item.new(:foo, Integer)
83
+ [21, 21.0, '21'].each do |a|
84
+ item.typecast(a).should == 21
85
+ end
86
+ end
87
+
88
+ it "should work with Floats" do
89
+ item = HappyMapper::Item.new(:foo, Float)
90
+ [21, 21.0, '21'].each do |a|
91
+ item.typecast(a).should == 21.0
92
+ end
93
+ end
94
+
95
+ it "should work with Times" do
96
+ item = HappyMapper::Item.new(:foo, Time)
97
+ item.typecast('2000-01-01 01:01:01.123456').should == Time.local(2000, 1, 1, 1, 1, 1, 123456)
98
+ end
99
+
100
+ it "should work with Dates" do
101
+ item = HappyMapper::Item.new(:foo, Date)
102
+ item.typecast('2000-01-01').should == Date.new(2000, 1, 1)
103
+ end
104
+
105
+ it "should work with DateTimes" do
106
+ item = HappyMapper::Item.new(:foo, DateTime)
107
+ item.typecast('2000-01-01 00:00:00').should == DateTime.new(2000, 1, 1, 0, 0, 0)
108
+ end
109
+
110
+ it "should work with Boolean" do
111
+ item = HappyMapper::Item.new(:foo, Boolean)
112
+ item.typecast('false').should == false
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,719 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+ require 'pp'
3
+ require 'uri'
4
+
5
+ module Analytics
6
+ class Property
7
+ include HappyMapper
8
+
9
+ tag 'property'
10
+ namespace 'http://schemas.google.com/analytics/2009'
11
+ attribute :name, String
12
+ attribute :value, String
13
+ end
14
+
15
+ class Entry
16
+ include HappyMapper
17
+
18
+ tag 'entry'
19
+ element :id, String
20
+ element :updated, DateTime
21
+ element :title, String
22
+ element :table_id, String, :namespace => 'http://schemas.google.com/analytics/2009', :tag => 'tableId'
23
+ has_many :properties, Property
24
+ end
25
+
26
+ class Feed
27
+ include HappyMapper
28
+
29
+ tag 'feed'
30
+ element :id, String
31
+ element :updated, DateTime
32
+ element :title, String
33
+ has_many :entries, Entry
34
+ end
35
+ end
36
+
37
+ class Feature
38
+ include HappyMapper
39
+ element :name, String, :tag => '.|.//text()'
40
+ end
41
+
42
+ class FeatureBullet
43
+ include HappyMapper
44
+
45
+ tag 'features_bullets'
46
+ has_many :features, Feature
47
+ element :bug, String
48
+ end
49
+
50
+ class Product
51
+ include HappyMapper
52
+
53
+ element :title, String
54
+ has_one :feature_bullets, FeatureBullet
55
+ end
56
+
57
+ module FamilySearch
58
+ class Person
59
+ include HappyMapper
60
+
61
+ attribute :version, String
62
+ attribute :modified, Time
63
+ attribute :id, String
64
+ end
65
+
66
+ class Persons
67
+ include HappyMapper
68
+ has_many :person, Person
69
+ end
70
+
71
+ class FamilyTree
72
+ include HappyMapper
73
+
74
+ tag 'familytree'
75
+ attribute :version, String
76
+ attribute :status_message, String, :tag => 'statusMessage'
77
+ attribute :status_code, String, :tag => 'statusCode'
78
+ has_one :persons, Persons
79
+ end
80
+ end
81
+
82
+ module FedEx
83
+ class Address
84
+ include HappyMapper
85
+
86
+ tag 'Address'
87
+ namespace 'http://fedex.com/ws/track/v2'
88
+ element :city, String, :tag => 'City'
89
+ element :state, String, :tag => 'StateOrProvinceCode'
90
+ element :zip, String, :tag => 'PostalCode'
91
+ element :countrycode, String, :tag => 'CountryCode'
92
+ element :residential, Boolean, :tag => 'Residential'
93
+ end
94
+
95
+ class Event
96
+ include HappyMapper
97
+
98
+ tag 'Events'
99
+ namespace 'http://fedex.com/ws/track/v2'
100
+ element :timestamp, String, :tag => 'Timestamp'
101
+ element :eventtype, String, :tag => 'EventType'
102
+ element :eventdescription, String, :tag => 'EventDescription'
103
+ has_one :address, Address
104
+ end
105
+
106
+ class PackageWeight
107
+ include HappyMapper
108
+
109
+ tag 'PackageWeight'
110
+ namespace 'http://fedex.com/ws/track/v2'
111
+ element :units, String, :tag => 'Units'
112
+ element :value, Integer, :tag => 'Value'
113
+ end
114
+
115
+ class TrackDetails
116
+ include HappyMapper
117
+
118
+ tag 'TrackDetails'
119
+ namespace 'http://fedex.com/ws/track/v2'
120
+ element :tracking_number, String, :tag => 'TrackingNumber'
121
+ element :status_code, String, :tag => 'StatusCode'
122
+ element :status_desc, String, :tag => 'StatusDescription'
123
+ element :carrier_code, String, :tag => 'CarrierCode'
124
+ element :service_info, String, :tag => 'ServiceInfo'
125
+ has_one :weight, PackageWeight, :tag => 'PackageWeight'
126
+ element :est_delivery, String, :tag => 'EstimatedDeliveryTimestamp'
127
+ has_many :events, Event
128
+ end
129
+
130
+ class Notification
131
+ include HappyMapper
132
+
133
+ tag 'Notifications'
134
+ namespace 'http://fedex.com/ws/track/v2'
135
+ element :severity, String, :tag => 'Severity'
136
+ element :source, String, :tag => 'Source'
137
+ element :code, Integer, :tag => 'Code'
138
+ element :message, String, :tag => 'Message'
139
+ element :localized_message, String, :tag => 'LocalizedMessage'
140
+ end
141
+
142
+ class TransactionDetail
143
+ include HappyMapper
144
+
145
+ tag 'TransactionDetail'
146
+ namespace 'http://fedex.com/ws/track/v2'
147
+ element :cust_tran_id, String, :tag => 'CustomerTransactionId'
148
+ end
149
+
150
+ class TrackReply
151
+ include HappyMapper
152
+
153
+ tag 'TrackReply'
154
+ namespace 'http://fedex.com/ws/track/v2'
155
+ element :highest_severity, String, :tag => 'HighestSeverity'
156
+ element :more_data, Boolean, :tag => 'MoreData'
157
+ has_many :notifications, Notification, :tag => 'Notifications'
158
+ has_many :trackdetails, TrackDetails, :tag => 'TrackDetails'
159
+ has_one :tran_detail, TransactionDetail, :tab => 'TransactionDetail'
160
+ end
161
+ end
162
+
163
+ class Place
164
+ include HappyMapper
165
+ element :name, String
166
+ end
167
+
168
+ class Radar
169
+ include HappyMapper
170
+ has_many :places, Place
171
+ end
172
+
173
+ class Post
174
+ include HappyMapper
175
+
176
+ attribute :href, String
177
+ attribute :hash, String
178
+ attribute :description, String
179
+ attribute :tag, String
180
+ attribute :time, Time
181
+ attribute :others, Integer
182
+ attribute :extended, String
183
+ end
184
+
185
+ class User
186
+ include HappyMapper
187
+
188
+ element :id, Integer
189
+ element :name, String
190
+ element :screen_name, String
191
+ element :location, String
192
+ element :description, String
193
+ element :profile_image_url, String
194
+ element :url, String
195
+ element :protected, Boolean
196
+ element :followers_count, Integer
197
+ end
198
+
199
+ class Status
200
+ include HappyMapper
201
+
202
+ element :id, Integer
203
+ element :text, String
204
+ element :created_at, Time
205
+ element :source, String
206
+ element :truncated, Boolean
207
+ element :in_reply_to_status_id, Integer
208
+ element :in_reply_to_user_id, Integer
209
+ element :favorited, Boolean
210
+ element :non_existent, String, :tag => 'dummy', :namespace => 'fake'
211
+ has_one :user, User
212
+ end
213
+
214
+ class CurrentWeather
215
+ include HappyMapper
216
+
217
+ tag 'ob'
218
+ namespace 'http://www.aws.com/aws'
219
+ element :temperature, Integer, :tag => 'temp'
220
+ element :feels_like, Integer, :tag => 'feels-like'
221
+ element :current_condition, String, :tag => 'current-condition', :attributes => {:icon => String}
222
+ end
223
+
224
+ class Address
225
+ include HappyMapper
226
+
227
+ tag 'address'
228
+ element :street, String
229
+ element :postcode, String
230
+ element :housenumber, String
231
+ element :city, String
232
+ element :country, 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 => 'http://www.georss.org/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
+ module Groups
274
+ class GroupElement
275
+ include HappyMapper
276
+
277
+ attribute :id, String
278
+ end
279
+
280
+ class Group
281
+ include HappyMapper
282
+
283
+ has_many :GroupElements, GroupElement, :group_tag => "groupelements"
284
+ end
285
+ end
286
+
287
+
288
+ describe HappyMapper do
289
+
290
+ describe "being included into another class" do
291
+ before do
292
+ @klass = Class.new do
293
+ include HappyMapper
294
+
295
+ def self.to_s
296
+ 'Foo'
297
+ end
298
+ end
299
+ end
300
+
301
+ it "should set attributes to an array" do
302
+ @klass.attributes.should == []
303
+ end
304
+
305
+ it "should set @elements to a hash" do
306
+ @klass.elements.should == []
307
+ end
308
+
309
+ it "should allow adding an attribute" do
310
+ lambda {
311
+ @klass.attribute :name, String
312
+ }.should change(@klass, :attributes)
313
+ end
314
+
315
+ it "should allow adding an attribute containing a dash" do
316
+ lambda {
317
+ @klass.attribute :'bar-baz', String
318
+ }.should change(@klass, :attributes)
319
+ end
320
+
321
+ it "should be able to get all attributes in array" do
322
+ @klass.attribute :name, String
323
+ @klass.attributes.size.should == 1
324
+ end
325
+
326
+ it "should allow adding an element" do
327
+ lambda {
328
+ @klass.element :name, String
329
+ }.should change(@klass, :elements)
330
+ end
331
+
332
+ it "should allow adding an element containing a dash" do
333
+ lambda {
334
+ @klass.element :'bar-baz', String
335
+ }.should change(@klass, :elements)
336
+
337
+ end
338
+
339
+ it "should be able to get all elements in array" do
340
+ @klass.element(:name, String)
341
+ @klass.elements.size.should == 1
342
+ end
343
+
344
+ it "should allow has one association" do
345
+ @klass.has_one(:user, User)
346
+ element = @klass.elements.first
347
+ element.name.should == 'user'
348
+ element.type.should == User
349
+ element.options[:single] = true
350
+ end
351
+
352
+ it "should allow has many association" do
353
+ @klass.has_many(:users, User)
354
+ element = @klass.elements.first
355
+ element.name.should == 'users'
356
+ element.type.should == User
357
+ element.options[:single] = false
358
+ end
359
+
360
+ it "should default tag name to lowercase class" do
361
+ @klass.tag_name.should == 'foo'
362
+ end
363
+
364
+ it "should default tag name of class in modules to the last constant lowercase" do
365
+ module Bar; class Baz; include HappyMapper; end; end
366
+ Bar::Baz.tag_name.should == 'baz'
367
+ end
368
+
369
+ it "should allow setting tag name" do
370
+ @klass.tag('FooBar')
371
+ @klass.tag_name.should == 'FooBar'
372
+ end
373
+
374
+ it "should allow setting a namespace" do
375
+ @klass.namespace(namespace = "foo")
376
+ @klass.namespace.should == namespace
377
+ end
378
+
379
+ it "should provide #parse" do
380
+ @klass.should respond_to(:parse)
381
+ end
382
+ end
383
+
384
+ describe "#attributes" do
385
+ it "should only return attributes for the current class" do
386
+ Post.attributes.size.should == 7
387
+ Status.attributes.size.should == 0
388
+ end
389
+ end
390
+
391
+ describe "#elements" do
392
+ it "should only return elements for the current class" do
393
+ Post.elements.size.should == 0
394
+ Status.elements.size.should == 10
395
+ end
396
+ end
397
+
398
+ it "should parse xml attributes into ruby objects" do
399
+ posts = Post.parse(fixture_file('posts.xml'))
400
+ posts.size.should == 20
401
+ first = posts.first
402
+ first.href.should == 'http://roxml.rubyforge.org/'
403
+ first.hash.should == '19bba2ab667be03a19f67fb67dc56917'
404
+ first.description.should == 'ROXML - Ruby Object to XML Mapping Library'
405
+ first.tag.should == 'ruby xml gems mapping'
406
+ first.time.should == Time.utc(2008, 8, 9, 5, 24, 20)
407
+ first.others.should == 56
408
+ 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.'
409
+ end
410
+
411
+ it "should parse xml elements to ruby objcts" do
412
+ statuses = Status.parse(fixture_file('statuses.xml'))
413
+ statuses.size.should == 20
414
+ first = statuses.first
415
+ first.id.should == 882281424
416
+ first.created_at.should == Time.utc(2008, 8, 9, 5, 38, 12)
417
+ first.source.should == 'web'
418
+ first.truncated.should be_false
419
+ first.in_reply_to_status_id.should == 1234
420
+ first.in_reply_to_user_id.should == 12345
421
+ first.favorited.should be_false
422
+ first.user.id.should == 4243
423
+ first.user.name.should == 'John Nunemaker'
424
+ first.user.screen_name.should == 'jnunemaker'
425
+ first.user.location.should == 'Mishawaka, IN, US'
426
+ first.user.description.should == 'Loves his wife, ruby, notre dame football and iu basketball'
427
+ first.user.profile_image_url.should == 'http://s3.amazonaws.com/twitter_production/profile_images/53781608/Photo_75_normal.jpg'
428
+ first.user.url.should == 'http://addictedtonew.com'
429
+ first.user.protected.should be_false
430
+ first.user.followers_count.should == 486
431
+ end
432
+
433
+ it "should parse xml containing the desired element as root node" do
434
+ address = Address.parse(fixture_file('address.xml'), :single => true)
435
+ address.street.should == 'Milchstrasse'
436
+ address.postcode.should == '26131'
437
+ address.housenumber.should == '23'
438
+ address.city.should == 'Oldenburg'
439
+ address.country.should == 'Germany'
440
+ end
441
+
442
+ it "should parse xml with default namespace (amazon)" do
443
+ file_contents = fixture_file('pita.xml')
444
+ items = PITA::Items.parse(file_contents, :single => true)
445
+ items.total_results.should == 22
446
+ items.total_pages.should == 3
447
+ first = items.items[0]
448
+ second = items.items[1]
449
+ first.asin.should == '0321480791'
450
+ first.point.should == '38.5351715088 -121.7948684692'
451
+ first.detail_page_url.should be_a_kind_of(URI)
452
+ 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'
453
+ first.manufacturer.should == 'Addison-Wesley Professional'
454
+ first.product_group.should == '<ProductGroup>Book</ProductGroup>'
455
+ second.asin.should == '047022388X'
456
+ second.manufacturer.should == 'Wrox'
457
+ end
458
+
459
+ it "should parse xml that has attributes of elements" do
460
+ items = CurrentWeather.parse(fixture_file('current_weather.xml'))
461
+ first = items[0]
462
+ first.temperature.should == 51
463
+ first.feels_like.should == 51
464
+ first.current_condition.should == 'Sunny'
465
+ first.current_condition.icon.should == 'http://deskwx.weatherbug.com/images/Forecast/icons/cond007.gif'
466
+ end
467
+
468
+ it "should parse xml with nested elements" do
469
+ radars = Radar.parse(fixture_file('radar.xml'))
470
+ first = radars[0]
471
+ first.places.size.should == 1
472
+ first.places[0].name.should == 'Store'
473
+ second = radars[1]
474
+ second.places.size.should == 0
475
+ third = radars[2]
476
+ third.places.size.should == 2
477
+ third.places[0].name.should == 'Work'
478
+ third.places[1].name.should == 'Home'
479
+ end
480
+
481
+ it "should parse xml that has elements with dashes" do
482
+ commit = GitHub::Commit.parse(fixture_file('commit.xml'))
483
+ commit.message.should == "move commands.rb and helpers.rb into commands/ dir"
484
+ commit.url.should == "http://github.com/defunkt/github-gem/commit/c26d4ce9807ecf57d3f9eefe19ae64e75bcaaa8b"
485
+ commit.id.should == "c26d4ce9807ecf57d3f9eefe19ae64e75bcaaa8b"
486
+ commit.committed_date.should == Date.parse("2008-03-02T16:45:41-08:00")
487
+ commit.tree.should == "28a1a1ca3e663d35ba8bf07d3f1781af71359b76"
488
+ end
489
+
490
+ it "should parse xml with no namespace" do
491
+ product = Product.parse(fixture_file('product_no_namespace.xml'), :single => true)
492
+ product.title.should == "A Title"
493
+ product.feature_bullets.bug.should == 'This is a bug'
494
+ product.feature_bullets.features.size.should == 2
495
+ product.feature_bullets.features[0].name.should == 'This is feature text 1'
496
+ product.feature_bullets.features[1].name.should == 'This is feature text 2'
497
+ end
498
+
499
+ it "should parse xml with default namespace" do
500
+ product = Product.parse(fixture_file('product_default_namespace.xml'), :single => true)
501
+ product.title.should == "A Title"
502
+ product.feature_bullets.bug.should == 'This is a bug'
503
+ product.feature_bullets.features.size.should == 2
504
+ product.feature_bullets.features[0].name.should == 'This is feature text 1'
505
+ product.feature_bullets.features[1].name.should == 'This is feature text 2'
506
+ end
507
+
508
+ it "should parse xml with single namespace" do
509
+ product = Product.parse(fixture_file('product_single_namespace.xml'), :single => true)
510
+ product.title.should == "A Title"
511
+ product.feature_bullets.bug.should == 'This is a bug'
512
+ product.feature_bullets.features.size.should == 2
513
+ product.feature_bullets.features[0].name.should == 'This is feature text 1'
514
+ product.feature_bullets.features[1].name.should == 'This is feature text 2'
515
+ end
516
+
517
+ it "should parse xml with multiple namespaces" do
518
+ track = FedEx::TrackReply.parse(fixture_file('multiple_namespaces.xml'))
519
+ track.highest_severity.should == 'SUCCESS'
520
+ track.more_data.should be_false
521
+ notification = track.notifications.first
522
+ notification.code.should == 0
523
+ notification.localized_message.should == 'Request was successfully processed.'
524
+ notification.message.should == 'Request was successfully processed.'
525
+ notification.severity.should == 'SUCCESS'
526
+ notification.source.should == 'trck'
527
+ detail = track.trackdetails.first
528
+ detail.carrier_code.should == 'FDXG'
529
+ detail.est_delivery.should == '2009-01-02T00:00:00'
530
+ detail.service_info.should == 'Ground-Package Returns Program-Domestic'
531
+ detail.status_code.should == 'OD'
532
+ detail.status_desc.should == 'On FedEx vehicle for delivery'
533
+ detail.tracking_number.should == '9611018034267800045212'
534
+ detail.weight.units.should == 'LB'
535
+ detail.weight.value.should == 2
536
+ events = detail.events
537
+ events.size.should == 10
538
+ first_event = events[0]
539
+ first_event.eventdescription.should == 'On FedEx vehicle for delivery'
540
+ first_event.eventtype.should == 'OD'
541
+ first_event.timestamp.should == '2009-01-02T06:00:00'
542
+ first_event.address.city.should == 'WICHITA'
543
+ first_event.address.countrycode.should == 'US'
544
+ first_event.address.residential.should be_false
545
+ first_event.address.state.should == 'KS'
546
+ first_event.address.zip.should == '67226'
547
+ last_event = events[-1]
548
+ last_event.eventdescription.should == 'In FedEx possession'
549
+ last_event.eventtype.should == 'IP'
550
+ last_event.timestamp.should == '2008-12-27T09:40:00'
551
+ last_event.address.city.should == 'LONGWOOD'
552
+ last_event.address.countrycode.should == 'US'
553
+ last_event.address.residential.should be_false
554
+ last_event.address.state.should == 'FL'
555
+ last_event.address.zip.should == '327506398'
556
+ track.tran_detail.cust_tran_id.should == '20090102-111321'
557
+ end
558
+
559
+ it "should be able to parse google analytics api xml" do
560
+ data = Analytics::Feed.parse(fixture_file('analytics.xml'))
561
+ data.id.should == 'http://www.google.com/analytics/feeds/accounts/nunemaker@gmail.com'
562
+ data.entries.size.should == 4
563
+
564
+ entry = data.entries[0]
565
+ entry.title.should == 'addictedtonew.com'
566
+ entry.properties.size.should == 4
567
+
568
+ property = entry.properties[0]
569
+ property.name.should == 'ga:accountId'
570
+ property.value.should == '85301'
571
+ end
572
+
573
+ it "should allow instantiating with a string" do
574
+ module StringFoo
575
+ class Bar
576
+ include HappyMapper
577
+ has_many :things, 'StringFoo::Thing'
578
+ end
579
+
580
+ class Thing
581
+ include HappyMapper
582
+ end
583
+ end
584
+ end
585
+
586
+ xit "should parse family search xml" do
587
+ tree = FamilySearch::FamilyTree.parse(fixture_file('family_tree.xml'))
588
+ tree.version.should == '1.0.20071213.942'
589
+ tree.status_message.should == 'OK'
590
+ tree.status_code.should == '200'
591
+ # tree.people.size.should == 1
592
+ # tree.people.first.version.should == '1199378491000'
593
+ # tree.people.first.modified.should == Time.utc(2008, 1, 3, 16, 41, 31) # 2008-01-03T09:41:31-07:00
594
+ # tree.people.first.id.should == 'KWQS-BBQ'
595
+ end
596
+
597
+ describe 'nested elements with namespaces' do
598
+ module Namespaces
599
+ class Info
600
+ include HappyMapper
601
+ namespace 'http://schemas.google.com/analytics/2009'
602
+ element :category, String
603
+ end
604
+
605
+ class Alert
606
+ include HappyMapper
607
+ namespace 'http://schemas.google.com/analytics/2009'
608
+
609
+ element :identifier, String
610
+ element :severity, String, :namespace => false
611
+ has_one :info, Info
612
+ end
613
+ class Distribution
614
+ include HappyMapper
615
+
616
+ tag 'EDXLDistribution'
617
+ has_one :alert, Alert
618
+ end
619
+ end
620
+
621
+ def mapping
622
+ @mapping ||= Namespaces::Distribution.parse(fixture_file('nested_namespaces.xml'))
623
+ end
624
+
625
+ it "should parse elements with inline namespace" do
626
+ lambda { mapping }.should_not raise_error
627
+ end
628
+
629
+ it "should map elements with inline namespace" do
630
+ mapping.alert.identifier.should == 'CDC-2006-183'
631
+ end
632
+
633
+ it "should map sub elements of with nested namespace" do
634
+ mapping.alert.info.category.should == 'Health'
635
+ end
636
+
637
+ it "should map elements without a namespace" do
638
+ mapping.alert.severity.should == 'Severe'
639
+ end
640
+ end
641
+
642
+ describe "" do
643
+ before do
644
+ @user = User.new
645
+ end
646
+
647
+ it "should provide #to_xml_node" do
648
+ @user.should respond_to(:to_xml_node)
649
+ end
650
+
651
+ it "should generate XML tags" do
652
+ @user.to_xml_node.to_s(:indent => false).should == "<user/>"
653
+ end
654
+
655
+ it "should generate XML tags for elements" do
656
+ @user.name = "Maxim"
657
+ @user.to_xml_node.to_s(:indent => false).should == "<user><name>Maxim</name></user>"
658
+ end
659
+
660
+ it "should generate XML attributes for attributes" do
661
+ obj = FamilySearch::Person.new
662
+ obj.version = "1.2.3.4"
663
+ obj.to_xml_node.to_s(:indent => false).should == '<person version="1.2.3.4"/>'
664
+ end
665
+
666
+ it "should generate XML for has_one relationship" do
667
+ obj = FedEx::Event.new
668
+ obj.address = FedEx::Address.new
669
+ obj.to_xml_node.to_s(:indent => false).should == '<Events><Address/></Events>'
670
+ end
671
+
672
+ it "should generate XML for has_many relationship" do
673
+ obj = FeatureBullet.new
674
+ f1 = Feature.new
675
+ f1.name = "name1"
676
+ f2 = Feature.new
677
+ f2.name = "name2"
678
+ obj.features = [f1, f2]
679
+ obj.to_xml_node.to_s(:indent => false).should == '<features_bullets><feature><name>name1</name></feature><feature><name>name2</name></feature></features_bullets>'
680
+ end
681
+
682
+ it "should generate XML for has_many relationship and remove empty elements (without children and attributes)" do
683
+ obj = FeatureBullet.new
684
+ f1 = Feature.new
685
+ f2 = Feature.new
686
+ obj.features = [f1, f2]
687
+ obj.to_xml_node.to_s(:indent => false).should == '<features_bullets/>'
688
+ end
689
+
690
+ it "should generate helper tag for has_many if :group_tag option is true" do
691
+ obj = Groups::Group.new
692
+ ge1 = Groups::GroupElement.new
693
+ ge1.id = "id1"
694
+ ge2 = Groups::GroupElement.new
695
+ ge2.id = "id2"
696
+ obj.GroupElements = [ge1, ge2]
697
+ obj.to_xml_node.to_s(:indent => false).should == '<group><groupelements><groupelement id="id1"/><groupelement id="id2"/></groupelements></group>'
698
+ end
699
+
700
+ it "should generate helper tag for has_many if :group_tag option is true and remove empty elements (without children and attributes)" do
701
+ obj = Groups::Group.new
702
+ ge1 = Groups::GroupElement.new
703
+ ge2 = Groups::GroupElement.new
704
+ obj.GroupElements = [ge1, ge2]
705
+ obj.to_xml_node.to_s(:indent => false).should == '<group/>'
706
+ end
707
+
708
+ it "should provide #to_xml" do
709
+ @user.should respond_to(:to_xml)
710
+ end
711
+
712
+ it "should generate XML document" do
713
+ @user.name = "Maxim"
714
+ @user.to_xml.should == %Q(<?xml version="1.0" encoding="UTF-8"?>\n<user>\n <name>Maxim</name>\n</user>\n)
715
+ end
716
+
717
+ end
718
+
719
+ end