nokogiri-happymapper 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/History +59 -0
  2. data/License +20 -0
  3. data/Manifest +45 -0
  4. data/README +61 -0
  5. data/Rakefile +28 -0
  6. data/TODO +0 -0
  7. data/examples/amazon.rb +34 -0
  8. data/examples/current_weather.rb +21 -0
  9. data/examples/dashed_elements.rb +20 -0
  10. data/examples/family_tree.rb +48 -0
  11. data/examples/post.rb +19 -0
  12. data/examples/twitter.rb +37 -0
  13. data/lib/happymapper.rb +157 -0
  14. data/lib/happymapper/attribute.rb +3 -0
  15. data/lib/happymapper/element.rb +3 -0
  16. data/lib/happymapper/item.rb +198 -0
  17. data/lib/happymapper/text_node.rb +3 -0
  18. data/lib/happymapper/version.rb +3 -0
  19. data/nokogiri-happymapper.gemspec +34 -0
  20. data/spec/fixtures/address.xml +8 -0
  21. data/spec/fixtures/analytics.xml +61 -0
  22. data/spec/fixtures/commit.xml +52 -0
  23. data/spec/fixtures/current_weather.xml +89 -0
  24. data/spec/fixtures/dictionary.xml +20 -0
  25. data/spec/fixtures/family_tree.xml +21 -0
  26. data/spec/fixtures/lastfm.xml +355 -0
  27. data/spec/fixtures/multiple_namespaces.xml +170 -0
  28. data/spec/fixtures/multiple_primitives.xml +5 -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 +17 -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/quarters.xml +19 -0
  35. data/spec/fixtures/radar.xml +21 -0
  36. data/spec/fixtures/statuses.xml +422 -0
  37. data/spec/happymapper_attribute_spec.rb +21 -0
  38. data/spec/happymapper_element_spec.rb +21 -0
  39. data/spec/happymapper_item_spec.rb +115 -0
  40. data/spec/happymapper_spec.rb +735 -0
  41. data/spec/happymapper_text_node_spec.rb +21 -0
  42. data/spec/spec.opts +1 -0
  43. data/spec/spec_helper.rb +13 -0
  44. data/website/css/common.css +47 -0
  45. data/website/index.html +98 -0
  46. metadata +120 -0
@@ -0,0 +1,21 @@
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
+
17
+ it 'should know that it is NOT a text node' do
18
+ @attr.text_node?.should be_false
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
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
+
17
+ it 'should know that it is NOT a text node' do
18
+ @attr.text_node?.should be_false
19
+ end
20
+ end
21
+ 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 = 'v2'
69
+ item.xpath.should == 'v2: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,735 @@
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 'dxp'
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 => 'dxp', :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 Address
38
+ include HappyMapper
39
+
40
+ tag 'address'
41
+ element :street, String
42
+ element :postcode, String
43
+ element :housenumber, String
44
+ element :city, String
45
+ element :country, String
46
+ end
47
+
48
+ class Feature
49
+ include HappyMapper
50
+ element :name, String, :tag => '.|.//text()'
51
+ end
52
+
53
+ class FeatureBullet
54
+ include HappyMapper
55
+
56
+ tag 'features_bullets'
57
+ has_many :features, Feature
58
+ element :bug, String
59
+ end
60
+
61
+ class Product
62
+ include HappyMapper
63
+
64
+ element :title, String
65
+ has_one :feature_bullets, FeatureBullet
66
+ has_one :address, Address
67
+ end
68
+
69
+ module FamilySearch
70
+ class AlternateIds
71
+ include HappyMapper
72
+
73
+ tag 'alternateIds'
74
+ has_many :ids, String, :tag => 'id'
75
+ end
76
+
77
+ class Information
78
+ include HappyMapper
79
+
80
+ has_one :alternateIds, AlternateIds
81
+ end
82
+
83
+ class Person
84
+ include HappyMapper
85
+
86
+ attribute :version, String
87
+ attribute :modified, Time
88
+ attribute :id, String
89
+ has_one :information, Information
90
+ end
91
+
92
+ class Persons
93
+ include HappyMapper
94
+ has_many :person, Person
95
+ end
96
+
97
+ class FamilyTree
98
+ include HappyMapper
99
+
100
+ tag 'familytree'
101
+ attribute :version, String
102
+ attribute :status_message, String, :tag => 'statusMessage'
103
+ attribute :status_code, String, :tag => 'statusCode'
104
+ has_one :persons, Persons
105
+ end
106
+ end
107
+
108
+ module FedEx
109
+ class Address
110
+ include HappyMapper
111
+
112
+ tag 'Address'
113
+ namespace 'v2'
114
+ element :city, String, :tag => 'City'
115
+ element :state, String, :tag => 'StateOrProvinceCode'
116
+ element :zip, String, :tag => 'PostalCode'
117
+ element :countrycode, String, :tag => 'CountryCode'
118
+ element :residential, Boolean, :tag => 'Residential'
119
+ end
120
+
121
+ class Event
122
+ include HappyMapper
123
+
124
+ tag 'Events'
125
+ namespace 'v2'
126
+ element :timestamp, String, :tag => 'Timestamp'
127
+ element :eventtype, String, :tag => 'EventType'
128
+ element :eventdescription, String, :tag => 'EventDescription'
129
+ has_one :address, Address
130
+ end
131
+
132
+ class PackageWeight
133
+ include HappyMapper
134
+
135
+ tag 'PackageWeight'
136
+ namespace 'v2'
137
+ element :units, String, :tag => 'Units'
138
+ element :value, Integer, :tag => 'Value'
139
+ end
140
+
141
+ class TrackDetails
142
+ include HappyMapper
143
+
144
+ tag 'TrackDetails'
145
+ namespace 'v2'
146
+ element :tracking_number, String, :tag => 'TrackingNumber'
147
+ element :status_code, String, :tag => 'StatusCode'
148
+ element :status_desc, String, :tag => 'StatusDescription'
149
+ element :carrier_code, String, :tag => 'CarrierCode'
150
+ element :service_info, String, :tag => 'ServiceInfo'
151
+ has_one :weight, PackageWeight, :tag => 'PackageWeight'
152
+ element :est_delivery, String, :tag => 'EstimatedDeliveryTimestamp'
153
+ has_many :events, Event
154
+ end
155
+
156
+ class Notification
157
+ include HappyMapper
158
+
159
+ tag 'Notifications'
160
+ namespace 'v2'
161
+ element :severity, String, :tag => 'Severity'
162
+ element :source, String, :tag => 'Source'
163
+ element :code, Integer, :tag => 'Code'
164
+ element :message, String, :tag => 'Message'
165
+ element :localized_message, String, :tag => 'LocalizedMessage'
166
+ end
167
+
168
+ class TransactionDetail
169
+ include HappyMapper
170
+
171
+ tag 'TransactionDetail'
172
+ namespace 'v2'
173
+ element :cust_tran_id, String, :tag => 'CustomerTransactionId'
174
+ end
175
+
176
+ class TrackReply
177
+ include HappyMapper
178
+
179
+ tag 'TrackReply'
180
+ namespace 'v2'
181
+ element :highest_severity, String, :tag => 'HighestSeverity'
182
+ element :more_data, Boolean, :tag => 'MoreData'
183
+ has_many :notifications, Notification, :tag => 'Notifications'
184
+ has_many :trackdetails, TrackDetails, :tag => 'TrackDetails'
185
+ has_one :tran_detail, TransactionDetail, :tab => 'TransactionDetail'
186
+ end
187
+ end
188
+
189
+ class Place
190
+ include HappyMapper
191
+ element :name, String
192
+ end
193
+
194
+ class Radar
195
+ include HappyMapper
196
+ has_many :places, Place, :tag => :place
197
+ end
198
+
199
+ class Post
200
+ include HappyMapper
201
+
202
+ attribute :href, String
203
+ attribute :hash, String
204
+ attribute :description, String
205
+ attribute :tag, String
206
+ attribute :time, Time
207
+ attribute :others, Integer
208
+ attribute :extended, String
209
+ end
210
+
211
+ class User
212
+ include HappyMapper
213
+
214
+ element :id, Integer
215
+ element :name, String
216
+ element :screen_name, String
217
+ element :location, String
218
+ element :description, String
219
+ element :profile_image_url, String
220
+ element :url, String
221
+ element :protected, Boolean
222
+ element :followers_count, Integer
223
+ end
224
+
225
+ class Status
226
+ include HappyMapper
227
+
228
+ element :id, Integer
229
+ element :text, String
230
+ element :created_at, Time
231
+ element :source, String
232
+ element :truncated, Boolean
233
+ element :in_reply_to_status_id, Integer
234
+ element :in_reply_to_user_id, Integer
235
+ element :favorited, Boolean
236
+ element :non_existent, String, :tag => 'dummy', :namespace => 'fake'
237
+ has_one :user, User
238
+ end
239
+
240
+ class CurrentWeather
241
+ include HappyMapper
242
+
243
+ tag 'ob'
244
+ namespace 'aws'
245
+ element :temperature, Integer, :tag => 'temp'
246
+ element :feels_like, Integer, :tag => 'feels-like'
247
+ element :current_condition, String, :tag => 'current-condition', :attributes => {:icon => String}
248
+ end
249
+
250
+ class Country
251
+ include HappyMapper
252
+
253
+ attribute :code, String
254
+ text_node :name, String
255
+ end
256
+
257
+ class Address
258
+ include HappyMapper
259
+
260
+ tag 'address'
261
+ element :street, String
262
+ element :postcode, String
263
+ element :housenumber, String
264
+ element :city, String
265
+ has_one :country, Country
266
+ end
267
+
268
+ # for type coercion
269
+ class ProductGroup < String; end
270
+
271
+ module PITA
272
+ class Item
273
+ include HappyMapper
274
+
275
+ tag 'Item' # if you put class in module you need tag
276
+ element :asin, String, :tag => 'ASIN'
277
+ element :detail_page_url, URI, :tag => 'DetailPageURL', :parser => :parse
278
+ element :manufacturer, String, :tag => 'Manufacturer', :deep => true
279
+ element :point, String, :tag => 'point', :namespace => 'georss'
280
+ element :product_group, ProductGroup, :tag => 'ProductGroup', :deep => true, :parser => :new, :raw => true
281
+ end
282
+
283
+ class Items
284
+ include HappyMapper
285
+
286
+ tag 'Items' # if you put class in module you need tag
287
+ element :total_results, Integer, :tag => 'TotalResults'
288
+ element :total_pages, Integer, :tag => 'TotalPages'
289
+ has_many :items, Item
290
+ end
291
+ end
292
+
293
+ module GitHub
294
+ class Commit
295
+ include HappyMapper
296
+
297
+ tag "commit"
298
+ element :url, String
299
+ element :tree, String
300
+ element :message, String
301
+ element :id, String
302
+ element :'committed-date', Date
303
+ end
304
+ end
305
+
306
+ module QuarterTest
307
+ class Quarter
308
+ include HappyMapper
309
+
310
+ element :start, String
311
+ end
312
+
313
+ class Details
314
+ include HappyMapper
315
+
316
+ element :round, Integer
317
+ element :quarter, Integer
318
+ end
319
+
320
+ class Game
321
+ include HappyMapper
322
+
323
+ # in an ideal world, the following elements would all be
324
+ # called 'quarter' with an attribute indicating which quarter
325
+ # it represented, but the refactoring that allows a single class
326
+ # to be used for all these differently named elements is the next
327
+ # best thing
328
+ has_one :details, QuarterTest::Details
329
+ has_one :q1, QuarterTest::Quarter, :tag => 'q1'
330
+ has_one :q2, QuarterTest::Quarter, :tag => 'q2'
331
+ has_one :q3, QuarterTest::Quarter, :tag => 'q3'
332
+ has_one :q4, QuarterTest::Quarter, :tag => 'q4'
333
+ end
334
+ end
335
+
336
+ # To check for multiple primitives
337
+ class Artist
338
+ include HappyMapper
339
+
340
+ tag 'artist'
341
+ element :images, String, :tag => "image", :single => false
342
+ element :name, String
343
+ end
344
+
345
+ class Location
346
+ include HappyMapper
347
+
348
+ tag 'point'
349
+ namespace "geo"
350
+ element :latitude, String, :tag => "lat"
351
+ end
352
+
353
+ # Testing the XmlContent type
354
+ module Dictionary
355
+ class Variant
356
+ include HappyMapper
357
+ tag 'var'
358
+ has_xml_content
359
+
360
+ def to_html
361
+ xml_content.gsub('<tag>','<em>').gsub('</tag>','</em>')
362
+ end
363
+ end
364
+
365
+ class Definition
366
+ include HappyMapper
367
+
368
+ tag 'def'
369
+ element :text, XmlContent, :tag => 'dtext'
370
+ end
371
+
372
+ class Record
373
+ include HappyMapper
374
+
375
+ tag 'record'
376
+ has_many :definitions, Definition
377
+ has_many :variants, Variant, :tag => 'var'
378
+ end
379
+ end
380
+
381
+ describe HappyMapper do
382
+
383
+ describe "being included into another class" do
384
+ before do
385
+ @klass = Class.new do
386
+ include HappyMapper
387
+
388
+ def self.to_s
389
+ 'Foo'
390
+ end
391
+ end
392
+ end
393
+
394
+ class Foo; include HappyMapper end
395
+
396
+ it "should set attributes to an array" do
397
+ @klass.attributes.should == []
398
+ end
399
+
400
+ it "should set @elements to a hash" do
401
+ @klass.elements.should == []
402
+ end
403
+
404
+ it "should allow adding an attribute" do
405
+ lambda {
406
+ @klass.attribute :name, String
407
+ }.should change(@klass, :attributes)
408
+ end
409
+
410
+ it "should allow adding an attribute containing a dash" do
411
+ lambda {
412
+ @klass.attribute :'bar-baz', String
413
+ }.should change(@klass, :attributes)
414
+ end
415
+
416
+ it "should be able to get all attributes in array" do
417
+ @klass.attribute :name, String
418
+ @klass.attributes.size.should == 1
419
+ end
420
+
421
+ it "should allow adding an element" do
422
+ lambda {
423
+ @klass.element :name, String
424
+ }.should change(@klass, :elements)
425
+ end
426
+
427
+ it "should allow adding an element containing a dash" do
428
+ lambda {
429
+ @klass.element :'bar-baz', String
430
+ }.should change(@klass, :elements)
431
+
432
+ end
433
+
434
+ it "should be able to get all elements in array" do
435
+ @klass.element(:name, String)
436
+ @klass.elements.size.should == 1
437
+ end
438
+
439
+ it "should allow has one association" do
440
+ @klass.has_one(:user, User)
441
+ element = @klass.elements.first
442
+ element.name.should == 'user'
443
+ element.type.should == User
444
+ element.options[:single] = true
445
+ end
446
+
447
+ it "should allow has many association" do
448
+ @klass.has_many(:users, User)
449
+ element = @klass.elements.first
450
+ element.name.should == 'users'
451
+ element.type.should == User
452
+ element.options[:single] = false
453
+ end
454
+
455
+ it "should default tag name to lowercase class" do
456
+ @klass.tag_name.should == 'foo'
457
+ end
458
+
459
+ it "should default tag name of class in modules to the last constant lowercase" do
460
+ module Bar; class Baz; include HappyMapper; end; end
461
+ Bar::Baz.tag_name.should == 'baz'
462
+ end
463
+
464
+ it "should allow setting tag name" do
465
+ @klass.tag('FooBar')
466
+ @klass.tag_name.should == 'FooBar'
467
+ end
468
+
469
+ it "should allow setting a namespace" do
470
+ @klass.namespace(namespace = "foo")
471
+ @klass.namespace.should == namespace
472
+ end
473
+
474
+ it "should provide #parse" do
475
+ @klass.should respond_to(:parse)
476
+ end
477
+ end
478
+
479
+ describe "#attributes" do
480
+ it "should only return attributes for the current class" do
481
+ Post.attributes.size.should == 7
482
+ Status.attributes.size.should == 0
483
+ end
484
+ end
485
+
486
+ describe "#elements" do
487
+ it "should only return elements for the current class" do
488
+ Post.elements.size.should == 0
489
+ Status.elements.size.should == 10
490
+ end
491
+ end
492
+
493
+ it "should parse xml attributes into ruby objects" do
494
+ posts = Post.parse(fixture_file('posts.xml'))
495
+ posts.size.should == 20
496
+ first = posts.first
497
+ first.href.should == 'http://roxml.rubyforge.org/'
498
+ first.hash.should == '19bba2ab667be03a19f67fb67dc56917'
499
+ first.description.should == 'ROXML - Ruby Object to XML Mapping Library'
500
+ first.tag.should == 'ruby xml gems mapping'
501
+ first.time.should == Time.utc(2008, 8, 9, 5, 24, 20)
502
+ first.others.should == 56
503
+ 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.'
504
+ end
505
+
506
+ it "should parse xml elements to ruby objcts" do
507
+ statuses = Status.parse(fixture_file('statuses.xml'))
508
+ statuses.size.should == 20
509
+ first = statuses.first
510
+ first.id.should == 882281424
511
+ first.created_at.should == Time.utc(2008, 8, 9, 5, 38, 12)
512
+ first.source.should == 'web'
513
+ first.truncated.should be_false
514
+ first.in_reply_to_status_id.should == 1234
515
+ first.in_reply_to_user_id.should == 12345
516
+ first.favorited.should be_false
517
+ first.user.id.should == 4243
518
+ first.user.name.should == 'John Nunemaker'
519
+ first.user.screen_name.should == 'jnunemaker'
520
+ first.user.location.should == 'Mishawaka, IN, US'
521
+ first.user.description.should == 'Loves his wife, ruby, notre dame football and iu basketball'
522
+ first.user.profile_image_url.should == 'http://s3.amazonaws.com/twitter_production/profile_images/53781608/Photo_75_normal.jpg'
523
+ first.user.url.should == 'http://addictedtonew.com'
524
+ first.user.protected.should be_false
525
+ first.user.followers_count.should == 486
526
+ end
527
+
528
+ it "should parse xml containing the desired element as root node" do
529
+ address = Address.parse(fixture_file('address.xml'), :single => true)
530
+ address.street.should == 'Milchstrasse'
531
+ address.postcode.should == '26131'
532
+ address.housenumber.should == '23'
533
+ address.city.should == 'Oldenburg'
534
+ address.country.class.should == Country
535
+ end
536
+
537
+ it "should parse text node correctly" do
538
+ address = Address.parse(fixture_file('address.xml'), :single => true)
539
+ address.country.name.should == 'Germany'
540
+ address.country.code.should == 'de'
541
+ end
542
+
543
+ it "should parse xml with default namespace (amazon)" do
544
+ file_contents = fixture_file('pita.xml')
545
+ items = PITA::Items.parse(file_contents, :single => true)
546
+ items.total_results.should == 22
547
+ items.total_pages.should == 3
548
+ first = items.items[0]
549
+ second = items.items[1]
550
+ first.asin.should == '0321480791'
551
+ first.point.should == '38.5351715088 -121.7948684692'
552
+ first.detail_page_url.should be_a_kind_of(URI)
553
+ 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'
554
+ first.manufacturer.should == 'Addison-Wesley Professional'
555
+ first.product_group.should == '<ProductGroup>Book</ProductGroup>'
556
+ second.asin.should == '047022388X'
557
+ second.manufacturer.should == 'Wrox'
558
+ end
559
+
560
+ it "should parse xml that has attributes of elements" do
561
+ items = CurrentWeather.parse(fixture_file('current_weather.xml'))
562
+ first = items[0]
563
+ first.temperature.should == 51
564
+ first.feels_like.should == 51
565
+ first.current_condition.should == 'Sunny'
566
+ first.current_condition.icon.should == 'http://deskwx.weatherbug.com/images/Forecast/icons/cond007.gif'
567
+ end
568
+
569
+ it "should parse xml with nested elements" do
570
+ radars = Radar.parse(fixture_file('radar.xml'))
571
+ first = radars[0]
572
+ first.places.size.should == 1
573
+ first.places[0].name.should == 'Store'
574
+ second = radars[1]
575
+ second.places.size.should == 0
576
+ third = radars[2]
577
+ third.places.size.should == 2
578
+ third.places[0].name.should == 'Work'
579
+ third.places[1].name.should == 'Home'
580
+ end
581
+
582
+ it "should parse xml with element name different to class name" do
583
+ game = QuarterTest::Game.parse(fixture_file('quarters.xml'))
584
+ game.q1.start.should == '4:40:15 PM'
585
+ game.q2.start.should == '5:18:53 PM'
586
+ end
587
+
588
+ it "should parse xml that has elements with dashes" do
589
+ commit = GitHub::Commit.parse(fixture_file('commit.xml'))
590
+ commit.message.should == "move commands.rb and helpers.rb into commands/ dir"
591
+ commit.url.should == "http://github.com/defunkt/github-gem/commit/c26d4ce9807ecf57d3f9eefe19ae64e75bcaaa8b"
592
+ commit.id.should == "c26d4ce9807ecf57d3f9eefe19ae64e75bcaaa8b"
593
+ commit.committed_date.should == Date.parse("2008-03-02T16:45:41-08:00")
594
+ commit.tree.should == "28a1a1ca3e663d35ba8bf07d3f1781af71359b76"
595
+ end
596
+
597
+ it "should parse xml with no namespace" do
598
+ product = Product.parse(fixture_file('product_no_namespace.xml'), :single => true)
599
+ product.title.should == "A Title"
600
+ product.feature_bullets.bug.should == 'This is a bug'
601
+ product.feature_bullets.features.size.should == 2
602
+ product.feature_bullets.features[0].name.should == 'This is feature text 1'
603
+ product.feature_bullets.features[1].name.should == 'This is feature text 2'
604
+ end
605
+
606
+ it "should parse xml with default namespace" do
607
+ product = Product.parse(fixture_file('product_default_namespace.xml'), :single => true)
608
+ product.title.should == "A Title"
609
+ product.feature_bullets.bug.should == 'This is a bug'
610
+ product.feature_bullets.features.size.should == 2
611
+ product.feature_bullets.features[0].name.should == 'This is feature text 1'
612
+ product.feature_bullets.features[1].name.should == 'This is feature text 2'
613
+ end
614
+
615
+ it "should parse xml with single namespace" do
616
+ product = Product.parse(fixture_file('product_single_namespace.xml'), :single => true)
617
+ product.title.should == "A Title"
618
+ product.feature_bullets.bug.should == 'This is a bug'
619
+ product.feature_bullets.features.size.should == 2
620
+ product.feature_bullets.features[0].name.should == 'This is feature text 1'
621
+ product.feature_bullets.features[1].name.should == 'This is feature text 2'
622
+ end
623
+
624
+ it "should parse xml with multiple namespaces" do
625
+ track = FedEx::TrackReply.parse(fixture_file('multiple_namespaces.xml'))
626
+ track.highest_severity.should == 'SUCCESS'
627
+ track.more_data.should be_false
628
+ notification = track.notifications.first
629
+ notification.code.should == 0
630
+ notification.localized_message.should == 'Request was successfully processed.'
631
+ notification.message.should == 'Request was successfully processed.'
632
+ notification.severity.should == 'SUCCESS'
633
+ notification.source.should == 'trck'
634
+ detail = track.trackdetails.first
635
+ detail.carrier_code.should == 'FDXG'
636
+ detail.est_delivery.should == '2009-01-02T00:00:00'
637
+ detail.service_info.should == 'Ground-Package Returns Program-Domestic'
638
+ detail.status_code.should == 'OD'
639
+ detail.status_desc.should == 'On FedEx vehicle for delivery'
640
+ detail.tracking_number.should == '9611018034267800045212'
641
+ detail.weight.units.should == 'LB'
642
+ detail.weight.value.should == 2
643
+ events = detail.events
644
+ events.size.should == 10
645
+ first_event = events[0]
646
+ first_event.eventdescription.should == 'On FedEx vehicle for delivery'
647
+ first_event.eventtype.should == 'OD'
648
+ first_event.timestamp.should == '2009-01-02T06:00:00'
649
+ first_event.address.city.should == 'WICHITA'
650
+ first_event.address.countrycode.should == 'US'
651
+ first_event.address.residential.should be_false
652
+ first_event.address.state.should == 'KS'
653
+ first_event.address.zip.should == '67226'
654
+ last_event = events[-1]
655
+ last_event.eventdescription.should == 'In FedEx possession'
656
+ last_event.eventtype.should == 'IP'
657
+ last_event.timestamp.should == '2008-12-27T09:40:00'
658
+ last_event.address.city.should == 'LONGWOOD'
659
+ last_event.address.countrycode.should == 'US'
660
+ last_event.address.residential.should be_false
661
+ last_event.address.state.should == 'FL'
662
+ last_event.address.zip.should == '327506398'
663
+ track.tran_detail.cust_tran_id.should == '20090102-111321'
664
+ end
665
+
666
+ it "should be able to parse google analytics api xml" do
667
+ data = Analytics::Feed.parse(fixture_file('analytics.xml'))
668
+ data.id.should == 'http://www.google.com/analytics/feeds/accounts/nunemaker@gmail.com'
669
+ data.entries.size.should == 4
670
+
671
+ entry = data.entries[0]
672
+ entry.title.should == 'addictedtonew.com'
673
+ entry.properties.size.should == 4
674
+
675
+ property = entry.properties[0]
676
+ property.name.should == 'ga:accountId'
677
+ property.value.should == '85301'
678
+ end
679
+
680
+ it "should allow instantiating with a string" do
681
+ module StringFoo
682
+ class Bar
683
+ include HappyMapper
684
+ has_many :things, 'StringFoo::Thing'
685
+ end
686
+
687
+ class Thing
688
+ include HappyMapper
689
+ end
690
+ end
691
+ end
692
+
693
+ xit "should parse family search xml" do
694
+ tree = FamilySearch::FamilyTree.parse(fixture_file('family_tree.xml'))
695
+ tree.version.should == '1.0.20071213.942'
696
+ tree.status_message.should == 'OK'
697
+ tree.status_code.should == '200'
698
+ tree.persons.person.size.should == 1
699
+ tree.persons.person.first.version.should == '1199378491000'
700
+ tree.persons.person.first.modified.should == Time.utc(2008, 1, 3, 16, 41, 31) # 2008-01-03T09:41:31-07:00
701
+ tree.persons.person.first.id.should == 'KWQS-BBQ'
702
+ tree.persons.person.first.information.alternateIds.ids.should_not be_kind_of(String)
703
+ tree.persons.person.first.information.alternateIds.ids.size.should == 8
704
+ end
705
+
706
+ it "should parse multiple images" do
707
+ artist = Artist.parse(fixture_file('multiple_primitives.xml'))
708
+ artist.name.should == "value"
709
+ artist.images.size.should == 2
710
+ end
711
+
712
+ it "should parse lastfm namespaces" do
713
+ l = Location.parse(fixture_file('lastfm.xml'))
714
+ l.first.latitude.should == "51.53469"
715
+ end
716
+
717
+ describe 'Xml Content' do
718
+ before(:each) do
719
+ file_contents = fixture_file('dictionary.xml')
720
+ @records = Dictionary::Record.parse(file_contents)
721
+ end
722
+
723
+ it "should parse XmlContent" do
724
+ @records.first.definitions.first.text.should ==
725
+ 'a large common parrot, <bn>Cacatua galerita</bn>, predominantly white, with yellow on the undersides of wings and tail and a forward curving yellow crest, found in Australia, New Guinea and nearby islands.'
726
+ end
727
+
728
+ it "should save object's xml content" do
729
+ @records.first.variants.first.xml_content.should ==
730
+ 'white <tag>cockatoo</tag>'
731
+ @records.first.variants.last.to_html.should ==
732
+ '<em>white</em> cockatoo'
733
+ end
734
+ end
735
+ end