nokogiri-happymapper 0.3.6 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +521 -0
- data/lib/happymapper.rb +493 -43
- data/lib/happymapper/item.rb +61 -8
- data/spec/fixtures/ambigous_items.xml +1 -1
- data/spec/fixtures/atom.xml +19 -0
- data/spec/fixtures/inagy.xml +85 -0
- data/spec/fixtures/optional_attributes.xml +6 -0
- data/spec/fixtures/subclass_namespace.xml +50 -0
- data/spec/happymapper_spec.rb +220 -6
- data/spec/happymapper_to_xml_namespaces_spec.rb +196 -0
- data/spec/happymapper_to_xml_spec.rb +196 -0
- data/spec/ignay_spec.rb +95 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/xpath_spec.rb +88 -0
- metadata +70 -68
- data/README +0 -62
- data/spec/spec.opts +0 -1
data/lib/happymapper/item.rb
CHANGED
@@ -26,14 +26,25 @@ module HappyMapper
|
|
26
26
|
def constant
|
27
27
|
@constant ||= constantize(type)
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
|
+
#
|
31
|
+
# @param [XMLNode] node the xml node that is being parsed
|
32
|
+
# @param [String] namespace the name of the namespace
|
33
|
+
# @param [Hash] xpath_options additional xpath options
|
34
|
+
#
|
30
35
|
def from_xml_node(node, namespace, xpath_options)
|
36
|
+
|
37
|
+
# If the item is defined as a primitive type then cast the value to that type
|
38
|
+
# else if the type is XMLContent then store the xml value
|
39
|
+
# else the type, specified, needs to handle the parsing.
|
40
|
+
#
|
41
|
+
|
31
42
|
if primitive?
|
32
43
|
find(node, namespace, xpath_options) do |n|
|
33
44
|
if n.respond_to?(:content)
|
34
45
|
typecast(n.content)
|
35
46
|
else
|
36
|
-
typecast(n
|
47
|
+
typecast(n)
|
37
48
|
end
|
38
49
|
end
|
39
50
|
elsif constant == XmlContent
|
@@ -42,6 +53,11 @@ module HappyMapper
|
|
42
53
|
n.respond_to?(:to_xml) ? n.to_xml : n.to_s
|
43
54
|
end
|
44
55
|
else
|
56
|
+
|
57
|
+
# When not a primitive type or XMLContent then default to using the
|
58
|
+
# class method #parse of the type class. If the option 'parser' has been
|
59
|
+
# defined then call that method on the type class instead of #parse
|
60
|
+
|
45
61
|
if options[:parser]
|
46
62
|
find(node, namespace, xpath_options) do |n|
|
47
63
|
if n.respond_to?(:content) && !options[:raw]
|
@@ -67,10 +83,12 @@ module HappyMapper
|
|
67
83
|
xpath += './/' if options[:deep]
|
68
84
|
xpath += "#{namespace}:" if namespace
|
69
85
|
xpath += tag
|
70
|
-
#
|
86
|
+
#puts "xpath: #{xpath}"
|
71
87
|
xpath
|
72
88
|
end
|
73
|
-
|
89
|
+
|
90
|
+
# @return [Boolean] true if the type defined for the item is defined in the
|
91
|
+
# list of primite types {Types}.
|
74
92
|
def primitive?
|
75
93
|
Types.include?(constant)
|
76
94
|
end
|
@@ -91,6 +109,17 @@ module HappyMapper
|
|
91
109
|
@method_name ||= name.tr('-', '_')
|
92
110
|
end
|
93
111
|
|
112
|
+
#
|
113
|
+
# When the type of the item is a primitive type, this will convert value specifed
|
114
|
+
# to the particular primitive type. If it fails during this process it will
|
115
|
+
# return the original String value.
|
116
|
+
#
|
117
|
+
# @param [String] value the string value parsed from the XML value that will
|
118
|
+
# be converted to the particular primitive type.
|
119
|
+
#
|
120
|
+
# @return [String,Float,Time,Date,DateTime,Boolean,Integer] the converted value
|
121
|
+
# to the new type.
|
122
|
+
#
|
94
123
|
def typecast(value)
|
95
124
|
return value if value.kind_of?(constant) || value.nil?
|
96
125
|
begin
|
@@ -123,6 +152,14 @@ module HappyMapper
|
|
123
152
|
|
124
153
|
private
|
125
154
|
|
155
|
+
#
|
156
|
+
# Convert any String defined types into their constant version so that
|
157
|
+
# the method #parse or the custom defined parser method would be used.
|
158
|
+
#
|
159
|
+
# @param [String,Constant] type is the name of the class or the constant
|
160
|
+
# for the class.
|
161
|
+
# @return [Constant] the constant of the type
|
162
|
+
#
|
126
163
|
def constantize(type)
|
127
164
|
if type.is_a?(String)
|
128
165
|
names = type.split('::')
|
@@ -152,7 +189,14 @@ module HappyMapper
|
|
152
189
|
|
153
190
|
if element?
|
154
191
|
if options[:single]
|
155
|
-
|
192
|
+
|
193
|
+
result = nil
|
194
|
+
|
195
|
+
if options[:xpath]
|
196
|
+
result = node.xpath(options[:xpath], xpath_options)
|
197
|
+
else
|
198
|
+
result = node.xpath(xpath(namespace), xpath_options)
|
199
|
+
end
|
156
200
|
|
157
201
|
if result
|
158
202
|
value = options[:single] ? yield(result.first) : result.map {|r| yield r }
|
@@ -161,7 +205,10 @@ module HappyMapper
|
|
161
205
|
value
|
162
206
|
end
|
163
207
|
else
|
164
|
-
|
208
|
+
|
209
|
+
target_path = options[:xpath] ? options[:xpath] : xpath(namespace)
|
210
|
+
|
211
|
+
results = node.xpath(target_path, xpath_options).collect do |result|
|
165
212
|
value = yield(result)
|
166
213
|
handle_attributes_option(result, value, xpath_options)
|
167
214
|
value
|
@@ -169,7 +216,13 @@ module HappyMapper
|
|
169
216
|
results
|
170
217
|
end
|
171
218
|
elsif attribute?
|
172
|
-
|
219
|
+
|
220
|
+
if options[:xpath]
|
221
|
+
yield(node.xpath(options[:xpath],xpath_options))
|
222
|
+
else
|
223
|
+
yield(node[tag])
|
224
|
+
end
|
225
|
+
|
173
226
|
else # text node
|
174
227
|
yield(node.children.detect{|c| c.text?})
|
175
228
|
end
|
@@ -177,7 +230,7 @@ module HappyMapper
|
|
177
230
|
|
178
231
|
def handle_attributes_option(result, value, xpath_options)
|
179
232
|
if options[:attributes].is_a?(Hash)
|
180
|
-
result = result.first
|
233
|
+
result = result.first unless result.respond_to?(:attribute_nodes)
|
181
234
|
|
182
235
|
result.attribute_nodes.each do |xml_attribute|
|
183
236
|
if attribute_options = options[:attributes][xml_attribute.name.to_sym]
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
|
3
|
+
<id>tag:www.example.com,2005:/tv_shows</id>
|
4
|
+
<link rel="alternate" type="text/html" href="http://www.example.com"/>
|
5
|
+
<link rel="self" type="application/atom+xml" href="http://www.example.com/tv_shows.atom"/>
|
6
|
+
<title>TV Shows</title>
|
7
|
+
<updated>2011-07-08T13:47:01Z</updated>
|
8
|
+
<entry>
|
9
|
+
<id>tag:www.example.com,2005:TvShow/17</id>
|
10
|
+
<published>2011-07-08T13:47:01Z</published>
|
11
|
+
<updated>2011-07-08T13:47:01Z</updated>
|
12
|
+
<link rel="alternate" type="text/html" href="http://www.example.com/sources/channel-twenty-seven/tv_shows/name-goes-here.atom"/>
|
13
|
+
<title>Name goes here</title>
|
14
|
+
<content type="html">Name goes here (0 episodes)</content>
|
15
|
+
<author>
|
16
|
+
<name>Source URL goes here</name>
|
17
|
+
</author>
|
18
|
+
</entry>
|
19
|
+
</feed>
|
@@ -0,0 +1,85 @@
|
|
1
|
+
<?xml version="1.0" encoding="ISO-8859-1"?>
|
2
|
+
<CatalogTree code="NLD" xmlns="urn:eventis:prodis:onlineapi:1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
3
|
+
<Node vodBackOfficeId="69fcb5eb-1020-4053-8fce-6bce030cac5b">
|
4
|
+
<Name>Parent 1</Name>
|
5
|
+
<Description>
|
6
|
+
111#111#120#NPO-BrowseMode.jpg#NPO-ExtendedMode.jpg
|
7
|
+
</Description>
|
8
|
+
<Translations>
|
9
|
+
<Translation Language="en-GB">
|
10
|
+
<Name>Parent 1 en</Name>
|
11
|
+
</Translation>
|
12
|
+
<Translation Language="de-DE">
|
13
|
+
<Name>Parent 1 de</Name>
|
14
|
+
</Translation>
|
15
|
+
</Translations>
|
16
|
+
<Rating>0</Rating>
|
17
|
+
<Adult>false</Adult>
|
18
|
+
<CatalogCode>NLD</CatalogCode>
|
19
|
+
<Position>1</Position>
|
20
|
+
<SubNodes>
|
21
|
+
<Node vodBackOfficeId="27feedad-dead-4721-ba61-c3f9f17d0956">
|
22
|
+
<Name>First</Name>
|
23
|
+
<Translations>
|
24
|
+
<Translation Language="en-GB">
|
25
|
+
<Name>First en</Name>
|
26
|
+
</Translation>
|
27
|
+
</Translations>
|
28
|
+
<Rating>0</Rating>
|
29
|
+
<Adult>false</Adult>
|
30
|
+
<CatalogCode>NLD</CatalogCode>
|
31
|
+
<ParentId>69fcb5eb-1020-4053-8fce-6bce030cac5b</ParentId>
|
32
|
+
<Position>0</Position>
|
33
|
+
<SubNodes>
|
34
|
+
<Node vodBackOfficeId="7edbcc13-2d03-4254-aaf6-307ba553d4fd">
|
35
|
+
<Name>Second</Name>
|
36
|
+
<Translations>
|
37
|
+
<Translation Language="en-GB">
|
38
|
+
<Name>Second en</Name>
|
39
|
+
</Translation>
|
40
|
+
</Translations>
|
41
|
+
<Rating>0</Rating>
|
42
|
+
<Adult>false</Adult>
|
43
|
+
<CatalogCode>NLD</CatalogCode>
|
44
|
+
<ParentId>b17dd22b-c028-44ad-8e75-e676e2619ceb</ParentId>
|
45
|
+
<Position>0</Position>
|
46
|
+
<SubNodes>
|
47
|
+
<Node vodBackOfficeId="27feedad-dead-4721-ba61-c3f9f17d1111">
|
48
|
+
<Name>Third</Name>
|
49
|
+
<Translations>
|
50
|
+
<Translation Language="en-GB">
|
51
|
+
<Name>Third en</Name>
|
52
|
+
</Translation>
|
53
|
+
</Translations>
|
54
|
+
<Rating>0</Rating>
|
55
|
+
<Adult>false</Adult>
|
56
|
+
<CatalogCode>NLD</CatalogCode>
|
57
|
+
<ParentId>69fcb5eb-1020-4053-8fce-6bce030cac5b</ParentId>
|
58
|
+
<Position>0</Position>
|
59
|
+
</Node>
|
60
|
+
</SubNodes>
|
61
|
+
</Node>
|
62
|
+
</SubNodes>
|
63
|
+
</Node>
|
64
|
+
</SubNodes>
|
65
|
+
</Node>
|
66
|
+
<Node vodBackOfficeId="69fcb5eb-1020-4053-8fce-12312312312">
|
67
|
+
<Name>Parent 2</Name>
|
68
|
+
<Description>
|
69
|
+
111#111#120#NPO-BrowseMode.jpg#NPO-ExtendedMode.jpg
|
70
|
+
</Description>
|
71
|
+
<Translations>
|
72
|
+
<Translation Language="en-GB">
|
73
|
+
<Name>Parent 2 en</Name>
|
74
|
+
</Translation>
|
75
|
+
<Translation Language="de-DE">
|
76
|
+
<Name>Parent 2 de</Name>
|
77
|
+
</Translation>
|
78
|
+
</Translations>
|
79
|
+
<Rating>0</Rating>
|
80
|
+
<Adult>false</Adult>
|
81
|
+
<CatalogCode>NLD</CatalogCode>
|
82
|
+
<Position>1</Position>
|
83
|
+
<SubNodes/>
|
84
|
+
</Node>
|
85
|
+
</CatalogTree>
|
@@ -0,0 +1,50 @@
|
|
1
|
+
<article:Article xmlns:article="http://www.wetpaint.com/alfresco/article"
|
2
|
+
xmlns:photo="http://www.wetpaint.com/alfresco/photo"
|
3
|
+
xmlns:gallery="http://www.wetpaint.com/alfresco/gallery"
|
4
|
+
xmlns:alf="http://www.alfresco.org"
|
5
|
+
alf:name="title">
|
6
|
+
<article:title>article title</article:title>
|
7
|
+
<article:text>article text</article:text>
|
8
|
+
<article:publishOptions>
|
9
|
+
<article:author>Nathan</article:author>
|
10
|
+
<article:draft>false</article:draft>
|
11
|
+
<article:scheduledDay>2011-01-14</article:scheduledDay>
|
12
|
+
<article:scheduledTime>11:31:45.0</article:scheduledTime>
|
13
|
+
<article:publishDisplayDay>2011-01-14</article:publishDisplayDay>
|
14
|
+
<article:publishDisplayTime>11:31:45.0</article:publishDisplayTime>
|
15
|
+
<article:createdDay>2011-01-14</article:createdDay>
|
16
|
+
<article:createdTime>11:52:24.0</article:createdTime>
|
17
|
+
</article:publishOptions>
|
18
|
+
|
19
|
+
<photo:Photo>
|
20
|
+
<photo:title>photo title</photo:title>
|
21
|
+
<photo:publishOptions>
|
22
|
+
<photo:author>Stephanie</photo:author>
|
23
|
+
<photo:draft>false</photo:draft>
|
24
|
+
<photo:scheduledDay>2011-01-13</photo:scheduledDay>
|
25
|
+
<photo:scheduledTime>15:47:30.0</photo:scheduledTime>
|
26
|
+
<photo:publishDisplayDay>2011-01-13</photo:publishDisplayDay>
|
27
|
+
<photo:publishDisplayTime>15:47:30.0</photo:publishDisplayTime>
|
28
|
+
<photo:createdDay>2011-01-13</photo:createdDay>
|
29
|
+
<photo:createdTime>15:51:47.0</photo:createdTime>
|
30
|
+
</photo:publishOptions>
|
31
|
+
</photo:Photo>
|
32
|
+
|
33
|
+
<gallery:Gallery>
|
34
|
+
<gallery:title>gallery title</gallery:title>
|
35
|
+
<photo:Photo>
|
36
|
+
<photo:title>photo title</photo:title>
|
37
|
+
<photo:publishOptions>
|
38
|
+
<photo:author>Stephanie</photo:author>
|
39
|
+
<photo:draft>false</photo:draft>
|
40
|
+
<photo:scheduledDay>2011-01-13</photo:scheduledDay>
|
41
|
+
<photo:scheduledTime>15:47:30.0</photo:scheduledTime>
|
42
|
+
<photo:publishDisplayDay>2011-01-13</photo:publishDisplayDay>
|
43
|
+
<photo:publishDisplayTime>15:47:30.0</photo:publishDisplayTime>
|
44
|
+
<photo:createdDay>2011-01-13</photo:createdDay>
|
45
|
+
<photo:createdTime>15:51:47.0</photo:createdTime>
|
46
|
+
</photo:publishOptions>
|
47
|
+
</photo:Photo>
|
48
|
+
</gallery:Gallery>
|
49
|
+
|
50
|
+
</article:Article>
|
data/spec/happymapper_spec.rb
CHANGED
@@ -69,6 +69,24 @@ module Analytics
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
+
module Atom
|
73
|
+
class Feed
|
74
|
+
include HappyMapper
|
75
|
+
tag 'feed'
|
76
|
+
|
77
|
+
attribute :xmlns, String, :single => true
|
78
|
+
element :id, String, :single => true
|
79
|
+
element :title, String, :single => true
|
80
|
+
element :updated, DateTime, :single => true
|
81
|
+
element :link, String, :single => false, :attributes => {
|
82
|
+
:rel => String,
|
83
|
+
:type => String,
|
84
|
+
:href => String
|
85
|
+
}
|
86
|
+
# has_many :entries, Entry # nothing interesting in the entries
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
72
90
|
class Address
|
73
91
|
include HappyMapper
|
74
92
|
|
@@ -82,7 +100,7 @@ end
|
|
82
100
|
|
83
101
|
class Feature
|
84
102
|
include HappyMapper
|
85
|
-
element :name, String, :
|
103
|
+
element :name, String, :xpath => './/text()'
|
86
104
|
end
|
87
105
|
|
88
106
|
class FeatureBullet
|
@@ -286,7 +304,7 @@ class Country
|
|
286
304
|
include HappyMapper
|
287
305
|
|
288
306
|
attribute :code, String
|
289
|
-
|
307
|
+
content :name, String
|
290
308
|
end
|
291
309
|
|
292
310
|
class Address
|
@@ -423,6 +441,103 @@ module AmbigousItems
|
|
423
441
|
end
|
424
442
|
end
|
425
443
|
|
444
|
+
class PublishOptions
|
445
|
+
include HappyMapper
|
446
|
+
|
447
|
+
tag 'publishOptions'
|
448
|
+
|
449
|
+
element :author, String, :tag => 'author'
|
450
|
+
|
451
|
+
element :draft, Boolean, :tag => 'draft'
|
452
|
+
element :scheduled_day, String, :tag => 'scheduledDay'
|
453
|
+
element :scheduled_time, String, :tag => 'scheduledTime'
|
454
|
+
element :published_day, String, :tag => 'publishDisplayDay'
|
455
|
+
element :published_time, String, :tag => 'publishDisplayTime'
|
456
|
+
element :created_day, String, :tag => 'publishDisplayDay'
|
457
|
+
element :created_time, String, :tag => 'publishDisplayTime'
|
458
|
+
|
459
|
+
end
|
460
|
+
|
461
|
+
class Article
|
462
|
+
include HappyMapper
|
463
|
+
|
464
|
+
tag 'Article'
|
465
|
+
namespace 'article'
|
466
|
+
|
467
|
+
attr_writer :xml_value
|
468
|
+
|
469
|
+
element :title, String
|
470
|
+
element :text, String
|
471
|
+
has_many :photos, 'Photo', :tag => 'Photo', :namespace => 'photo', :xpath => '/article:Article'
|
472
|
+
has_many :galleries, 'Gallery', :tag => 'Gallery', :namespace => 'gallery'
|
473
|
+
|
474
|
+
element :publish_options, PublishOptions, :tag => 'publishOptions', :namespace => 'article'
|
475
|
+
|
476
|
+
end
|
477
|
+
|
478
|
+
class PartiallyBadArticle
|
479
|
+
include HappyMapper
|
480
|
+
|
481
|
+
attr_writer :xml_value
|
482
|
+
|
483
|
+
tag 'Article'
|
484
|
+
namespace 'article'
|
485
|
+
|
486
|
+
element :title, String
|
487
|
+
element :text, String
|
488
|
+
has_many :photos, 'Photo', :tag => 'Photo', :namespace => 'photo', :xpath => '/article:Article'
|
489
|
+
has_many :videos, 'Video', :tag => 'Video', :namespace => 'video'
|
490
|
+
|
491
|
+
element :publish_options, PublishOptions, :tag => 'publishOptions', :namespace => 'article'
|
492
|
+
|
493
|
+
end
|
494
|
+
|
495
|
+
class Photo
|
496
|
+
include HappyMapper
|
497
|
+
|
498
|
+
tag 'Photo'
|
499
|
+
namespace 'photo'
|
500
|
+
|
501
|
+
attr_writer :xml_value
|
502
|
+
|
503
|
+
element :title, String
|
504
|
+
element :publish_options, PublishOptions, :tag => 'publishOptions', :namespace => 'photo'
|
505
|
+
|
506
|
+
end
|
507
|
+
|
508
|
+
class Gallery
|
509
|
+
include HappyMapper
|
510
|
+
|
511
|
+
tag 'Gallery'
|
512
|
+
namespace 'gallery'
|
513
|
+
|
514
|
+
attr_writer :xml_value
|
515
|
+
|
516
|
+
element :title, String
|
517
|
+
|
518
|
+
end
|
519
|
+
|
520
|
+
class Video
|
521
|
+
include HappyMapper
|
522
|
+
|
523
|
+
tag 'Video'
|
524
|
+
namespace 'video'
|
525
|
+
|
526
|
+
attr_writer :xml_value
|
527
|
+
|
528
|
+
element :title, String
|
529
|
+
element :publish_options, PublishOptions, :tag => 'publishOptions', :namespace => 'video'
|
530
|
+
|
531
|
+
end
|
532
|
+
|
533
|
+
class OptionalAttribute
|
534
|
+
include HappyMapper
|
535
|
+
tag 'address'
|
536
|
+
|
537
|
+
attribute :street, String
|
538
|
+
end
|
539
|
+
|
540
|
+
|
426
541
|
describe HappyMapper do
|
427
542
|
|
428
543
|
describe "being included into another class" do
|
@@ -486,7 +601,7 @@ describe HappyMapper do
|
|
486
601
|
element = @klass.elements.first
|
487
602
|
element.name.should == 'user'
|
488
603
|
element.type.should == User
|
489
|
-
element.options[:single]
|
604
|
+
element.options[:single].should == true
|
490
605
|
end
|
491
606
|
|
492
607
|
it "should allow has many association" do
|
@@ -494,7 +609,7 @@ describe HappyMapper do
|
|
494
609
|
element = @klass.elements.first
|
495
610
|
element.name.should == 'users'
|
496
611
|
element.type.should == User
|
497
|
-
element.options[:single]
|
612
|
+
element.options[:single].should == false
|
498
613
|
end
|
499
614
|
|
500
615
|
it "should default tag name to lowercase class" do
|
@@ -585,6 +700,12 @@ describe HappyMapper do
|
|
585
700
|
address.country.code.should == 'de'
|
586
701
|
end
|
587
702
|
|
703
|
+
it "should treat Nokogiri::XML::Document as root" do
|
704
|
+
doc = Nokogiri::XML(fixture_file('address.xml'))
|
705
|
+
address = Address.parse(doc)
|
706
|
+
address.class.should == Address
|
707
|
+
end
|
708
|
+
|
588
709
|
it "should parse xml with default namespace (amazon)" do
|
589
710
|
file_contents = fixture_file('pita.xml')
|
590
711
|
items = PITA::Items.parse(file_contents, :single => true)
|
@@ -611,6 +732,23 @@ describe HappyMapper do
|
|
611
732
|
first.current_condition.icon.should == 'http://deskwx.weatherbug.com/images/Forecast/icons/cond007.gif'
|
612
733
|
end
|
613
734
|
|
735
|
+
it "parses xml with attributes of elements that aren't :single => true" do
|
736
|
+
feed = Atom::Feed.parse(fixture_file('atom.xml'))
|
737
|
+
feed.link.first.href.should == 'http://www.example.com'
|
738
|
+
feed.link.last.href.should == 'http://www.example.com/tv_shows.atom'
|
739
|
+
end
|
740
|
+
|
741
|
+
it "returns nil rather than empty array for absent values when :single => true" do
|
742
|
+
address = Address.parse('<?xml version="1.0" encoding="UTF-8"?><foo/>', :single => true)
|
743
|
+
address.should be_nil
|
744
|
+
end
|
745
|
+
|
746
|
+
it "should return same result for absent values when :single => true, regardless of :in_groups_of" do
|
747
|
+
addr1 = Address.parse('<?xml version="1.0" encoding="UTF-8"?><foo/>', :single => true)
|
748
|
+
addr2 = Address.parse('<?xml version="1.0" encoding="UTF-8"?><foo/>', :single => true, :in_groups_of => 10)
|
749
|
+
addr1.should == addr2
|
750
|
+
end
|
751
|
+
|
614
752
|
it "should parse xml with nested elements" do
|
615
753
|
radars = Radar.parse(fixture_file('radar.xml'))
|
616
754
|
first = radars[0]
|
@@ -768,6 +906,25 @@ describe HappyMapper do
|
|
768
906
|
l = Location.parse(fixture_file('lastfm.xml'))
|
769
907
|
l.first.latitude.should == "51.53469"
|
770
908
|
end
|
909
|
+
|
910
|
+
describe "Parse optional attributes" do
|
911
|
+
|
912
|
+
it "should parse an empty String as empty" do
|
913
|
+
a = OptionalAttribute.parse(fixture_file('optional_attributes.xml'))
|
914
|
+
a[0].street.should == ""
|
915
|
+
end
|
916
|
+
|
917
|
+
it "should parse a String with value" do
|
918
|
+
a = OptionalAttribute.parse(fixture_file('optional_attributes.xml'))
|
919
|
+
a[1].street.should == "Milchstrasse"
|
920
|
+
end
|
921
|
+
|
922
|
+
it "should parse a String with value" do
|
923
|
+
a = OptionalAttribute.parse(fixture_file('optional_attributes.xml'))
|
924
|
+
a[2].street.should be_nil
|
925
|
+
end
|
926
|
+
|
927
|
+
end
|
771
928
|
|
772
929
|
describe 'Xml Content' do
|
773
930
|
before(:each) do
|
@@ -789,8 +946,65 @@ describe HappyMapper do
|
|
789
946
|
end
|
790
947
|
|
791
948
|
it "should parse ambigous items" do
|
792
|
-
items = AmbigousItems::Item.parse(fixture_file('ambigous_items.xml'),
|
793
|
-
:xpath => '/ambigous/my-items')
|
949
|
+
items = AmbigousItems::Item.parse(fixture_file('ambigous_items.xml'), :xpath => '/ambigous/my-items')
|
794
950
|
items.map(&:name).should == %w(first second third).map{|s| "My #{s} item" }
|
795
951
|
end
|
952
|
+
|
953
|
+
|
954
|
+
context Article do
|
955
|
+
it "should parse the publish options for Article and Photo" do
|
956
|
+
@article.title.should_not be_nil
|
957
|
+
@article.text.should_not be_nil
|
958
|
+
@article.photos.should_not be_nil
|
959
|
+
@article.photos.first.title.should_not be_nil
|
960
|
+
end
|
961
|
+
|
962
|
+
it "should parse the publish options for Article" do
|
963
|
+
@article.publish_options.should_not be_nil
|
964
|
+
end
|
965
|
+
|
966
|
+
it "should parse the publish options for Photo" do
|
967
|
+
@article.photos.first.publish_options.should_not be_nil
|
968
|
+
end
|
969
|
+
|
970
|
+
it "should only find only items at the parent level" do
|
971
|
+
@article.photos.length.should == 1
|
972
|
+
end
|
973
|
+
|
974
|
+
before(:all) do
|
975
|
+
@article = Article.parse(fixture_file('subclass_namespace.xml'))
|
976
|
+
end
|
977
|
+
|
978
|
+
end
|
979
|
+
|
980
|
+
context "Namespace is missing because an optional element that uses it is not present" do
|
981
|
+
it "should parse successfully" do
|
982
|
+
@article = PartiallyBadArticle.parse(fixture_file('subclass_namespace.xml'))
|
983
|
+
@article.should_not be_nil
|
984
|
+
@article.title.should_not be_nil
|
985
|
+
@article.text.should_not be_nil
|
986
|
+
@article.photos.should_not be_nil
|
987
|
+
@article.photos.first.title.should_not be_nil
|
988
|
+
end
|
989
|
+
end
|
990
|
+
|
991
|
+
|
992
|
+
describe "with limit option" do
|
993
|
+
it "should return results with limited size: 6" do
|
994
|
+
sizes = []
|
995
|
+
posts = Post.parse(fixture_file('posts.xml'), :in_groups_of => 6) do |a|
|
996
|
+
sizes << a.size
|
997
|
+
end
|
998
|
+
sizes.should == [6, 6, 6, 2]
|
999
|
+
end
|
1000
|
+
|
1001
|
+
it "should return results with limited size: 10" do
|
1002
|
+
sizes = []
|
1003
|
+
posts = Post.parse(fixture_file('posts.xml'), :in_groups_of => 10) do |a|
|
1004
|
+
sizes << a.size
|
1005
|
+
end
|
1006
|
+
sizes.should == [10, 10]
|
1007
|
+
end
|
1008
|
+
end
|
1009
|
+
|
796
1010
|
end
|