objectify-xml 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ module Objectify
2
+ class ElementParser < Xml
3
+ def primary_xml_element(xml)
4
+ xml.attributes.keys.each do |name|
5
+ method = "#{ name }="
6
+ if respond_to? method
7
+ send(method, xml_text_to_value(xml[name]))
8
+ end
9
+ end
10
+ if respond_to? :inner_html=
11
+ self.inner_html = xml.inner_html
12
+ end
13
+ if respond_to? :inner_text=
14
+ self.inner_text = xml.inner_text
15
+ end
16
+ end
17
+ end
18
+ end
data/spec/atom_spec.rb ADDED
@@ -0,0 +1,63 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ # This is not a validation of my definition of Atom, just a sort of integration test.
4
+ describe Objectify::Atom do
5
+ before :all do
6
+ @feed = Objectify::Atom::Feed.new(sample_feed('wikipedia.atom'))
7
+ end
8
+
9
+ describe 'the feed' do
10
+ it 'should have 2 links' do
11
+ @feed.links.length.should == 2
12
+ end
13
+
14
+ it 'should have 50 entries' do
15
+ @feed.entries.length.should == 50
16
+ end
17
+
18
+ it 'should be generated by Mediawiki' do
19
+ @feed.generator.inner_text.should == 'MediaWiki 1.15alpha'
20
+ end
21
+ end
22
+
23
+ describe 'the first link' do
24
+ it 'should have all attributes' do
25
+ link = @feed.links.first
26
+ link.rel.should == 'self'
27
+ link.type.should == 'application/atom+xml'
28
+ # the href has been unescaped.
29
+ link.href.should == "http://en.wikipedia.org/w/index.php?title=Special:RecentChanges&feed=atom"
30
+ end
31
+ end
32
+
33
+ describe 'the first entry' do
34
+ before do
35
+ @entry = @feed.entries.first
36
+ end
37
+
38
+ it 'should be updated at...' do
39
+ @entry.updated.should == DateTime.parse('2009-02-23T19:08:18')
40
+ end
41
+
42
+ it 'should be titled ...' do
43
+ @entry.title.should == 'Audio commentary'
44
+ end
45
+
46
+ it 'should have a summary' do
47
+ @entry.summary.length.should == 5692
48
+ end
49
+
50
+ it 'should inspect nicely' do
51
+ @entry.inspect.should == '<Objectify::Atom::Entry title, author:Objectify::Atom::Author, id, summary, links:1, updated>'
52
+ end
53
+
54
+ it 'should have the author name' do
55
+ @entry.author.name.should == 'Richiekim'
56
+ end
57
+
58
+ it 'should be nil if the element was not included' do
59
+ @entry.category.should be_nil
60
+ @entry.published.should be_nil
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,156 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Objectify::DocumentParser do
4
+ class D < Objectify::DocumentParser
5
+ def initialize; end
6
+ public *private_instance_methods.map { |s| s.to_sym }
7
+ end
8
+
9
+ before do
10
+ @author = Nokogiri::XML('<author><name>Joe</name></author>').child
11
+ end
12
+
13
+ describe 'primary_xml_element' do
14
+ it 'should call parse_xml' do
15
+ d = D.new
16
+ d.expects(:parse_xml).with(@author.child)
17
+ d.primary_xml_element(@author)
18
+ end
19
+ end
20
+
21
+ describe 'xml helpers' do
22
+ before do
23
+ @title = Nokogiri::XML('<title>Joe</title>').child
24
+ @isbn = Nokogiri::XML('<entry xmlns:book="http://example.com"><book:isbn>123</book:isbn></entry>').child.child
25
+ @generator = Nokogiri::XML('<generator type="friendly">Mr. Happy</title>').child
26
+ end
27
+
28
+ describe 'qualified_name' do
29
+ it 'should return name with just name' do
30
+ D.new.qualified_name(@author).should == 'author'
31
+ end
32
+
33
+ it 'should return namespace:name with name and namespace' do
34
+ D.new.qualified_name(@isbn).should == 'book:isbn'
35
+ end
36
+ end
37
+
38
+ describe 'delegate methods' do
39
+ it 'attribute_type' do
40
+ D.expects(:attribute_type).with('title').returns(:result)
41
+ D.new.attribute_type(@title).should == :result
42
+ end
43
+ it 'flatten?' do
44
+ D.expects(:flatten?).with('title').returns(:result)
45
+ D.new.flatten?(@title).should == :result
46
+ end
47
+ it 'collection?' do
48
+ D.expects(:collection?).with('title').returns(:result)
49
+ D.new.collection?(@title).should == :result
50
+ end
51
+ it 'attribute' do
52
+ D.expects(:find_attribute).with('book:isbn', 'book', 'isbn').returns(:result)
53
+ D.new.attribute(@isbn).should == :result
54
+ end
55
+ end
56
+
57
+ describe 'namespace?' do
58
+ it 'should delegate if the element has a namespace' do
59
+ d = D.new
60
+ d.class.expects(:namespace?).with('book').returns(:result)
61
+ d.namespace?(@isbn).should == :result
62
+ end
63
+
64
+ it 'should just return true if no namespace' do
65
+ d = D.new
66
+ d.class.expects(:namespace?).never
67
+ d.namespace?(@title).should be_true
68
+ end
69
+ end
70
+
71
+ describe 'parse_xml' do
72
+ it 'should call read_xml_element on each xml sibling node' do
73
+ xml2 = mock('xml2', :next => nil)
74
+ xml = mock('xml1', :next => xml2)
75
+ d = D.new
76
+ d.expects(:read_xml_element).with(xml)
77
+ d.expects(:read_xml_element).with(xml2)
78
+ d.parse_xml(xml)
79
+ end
80
+ end
81
+
82
+ describe 'read_xml_element' do
83
+ class FlattenAuthor < D
84
+ flatten :author
85
+ attribute :name
86
+ end
87
+
88
+ it 'should skip text elements' do
89
+ d = D.new
90
+ d.expects(:set_attribute).never
91
+ @title.child.should be_an_instance_of(Nokogiri::XML::Text)
92
+ d.read_xml_element(@title.child)
93
+ end
94
+
95
+ it 'should skip elements in the wrong namespace' do
96
+ d = D.new
97
+ d.expects(:set_attribute).never
98
+ d.read_xml_element(@isbn)
99
+ end
100
+
101
+ it 'should call parse_xml on the first child node of a flattened element' do
102
+ e = FlattenAuthor.new
103
+ e.flatten?(@author).should be_true
104
+ e.expects(:parse_xml).with(@author.child)
105
+ e.read_xml_element(@author)
106
+ end
107
+
108
+ describe 'with an attribute type' do
109
+ class HasAuthor < D
110
+ has_one 'author', Objectify::Atom::Author, 'author'
111
+ end
112
+ it 'should set the attribute' do
113
+ e = HasAuthor.new
114
+ e.attribute_type(@author).should == Objectify::Atom::Author
115
+ e.expects(:author=).with(instance_of(Objectify::Atom::Author))
116
+ e.read_xml_element(@author)
117
+ end
118
+ end
119
+
120
+ describe 'without an attribute type' do
121
+ it 'should set the attribute' do
122
+ e = FlattenAuthor.new
123
+ e.expects(:xml_text_to_value).with('Joe').returns(43)
124
+ e.expects(:name=).with(43)
125
+ e.read_xml_element(@author)
126
+ end
127
+ end
128
+ end
129
+
130
+ describe 'set_attribute' do
131
+ it 'should do nothing if the attribute is not found' do
132
+ d = D.new
133
+ d.expects(:attribute).with(:xml).returns(nil)
134
+ d.set_attribute(:xml) do
135
+ fail "Shouldn't get here"
136
+ end
137
+ end
138
+ it 'should yield and append the result to a collection' do
139
+ d = D.new
140
+ d.expects(:attribute).with(:xml).returns('attr_name')
141
+ d.expects(:collection?).with(:xml).returns(true)
142
+ array = []
143
+ d.expects(:attr_name).returns(array)
144
+ d.set_attribute(:xml) { :value }
145
+ array.should == [:value]
146
+ end
147
+ it 'should yield and set the result if not a collection' do
148
+ d = D.new
149
+ d.expects(:attribute).with(:xml).returns('attr_name')
150
+ d.expects(:collection?).with(:xml).returns(false)
151
+ d.expects(:attr_name=).with(:value)
152
+ d.set_attribute(:xml) { :value }
153
+ end
154
+ end
155
+ end
156
+ end
data/spec/dsl_spec.rb ADDED
@@ -0,0 +1,164 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Objectify::Xml::Dsl do
4
+ A = Objectify::Atom
5
+
6
+ class D < Objectify::DocumentParser
7
+ def initialize; end
8
+ end
9
+
10
+ class MyAuthor < A::Author
11
+ attributes :weight
12
+ attribute 'attr', 'ns:realname'
13
+ attribute 'attrs', nil, true
14
+ attribute 'ns_something'
15
+ flatten :nest
16
+ namespace 'ns', 'http://example.com'
17
+ namespaces :blog
18
+ default_namespace 'http://place.com'
19
+ has_one :link, 'Objectify::Atom::Link', 'link'
20
+ end
21
+
22
+ it 'should init the metadata' do
23
+ D.metadata.each do |k, v|
24
+ v.should_not be_nil
25
+ end
26
+ end
27
+ describe 'inheritance' do
28
+ it 'should include the parent metadata' do
29
+ MyAuthor.metadata[:attributes].should include('name')
30
+ end
31
+ it 'should include the new metadata' do
32
+ MyAuthor.metadata[:attributes].should include('weight')
33
+ end
34
+ it "should not affect the parent's metadata" do
35
+ A::Author.metadata[:attributes].should include('name')
36
+ A::Author.metadata[:attributes].should_not include('weight')
37
+ end
38
+ end
39
+
40
+ describe 'has_one' do
41
+ it 'should set the metadata correctly' do
42
+ A::Feed.metadata[:attributes].should_not include('generator')
43
+ A::Feed.metadata[:qualified_attributes].should include('generator')
44
+ A::Feed.metadata[:collections].should_not include('generator')
45
+ A::Feed.metadata[:types]['generator'].should == A::Generator
46
+ end
47
+ end
48
+
49
+ describe 'has_many' do
50
+ it 'should set the metadata correctly' do
51
+ A::Feed.metadata[:attributes].should_not include('links')
52
+ A::Feed.metadata[:qualified_attributes].should include('link')
53
+ A::Feed.metadata[:collections].should include('link')
54
+ A::Feed.metadata[:types]['link'].to_s.should match(/Link/)
55
+ end
56
+ end
57
+
58
+ describe 'attributes' do
59
+ it 'should call attribute for each item' do
60
+ D.expects(:attribute).with(:one)
61
+ D.expects(:attribute).with(:two)
62
+ D.attributes :one, :two
63
+ end
64
+ end
65
+
66
+ describe 'attribute' do
67
+ it 'should set up an attribute with namespace' do
68
+ #attribute 'attr', 'ns:realname'
69
+ MyAuthor.metadata[:attributes].should_not include('attr')
70
+ MyAuthor.metadata[:qualified_attributes]['ns:realname'].should == 'attr'
71
+ MyAuthor.metadata[:collections].should_not include('ns:realname')
72
+ MyAuthor.metadata[:collections].should_not include('attr')
73
+ MyAuthor.metadata[:types]['ns:realname'].should be_nil
74
+ MyAuthor.metadata[:types]['attr'].should be_nil
75
+ d = MyAuthor.new('')
76
+ d.attr.should be_nil
77
+ d.attr = true
78
+ d.attr.should be_true
79
+ end
80
+ it 'should set up a collection attribute' do
81
+ #attribute 'attr2', nil, true
82
+ MyAuthor.metadata[:attributes].should include('attrs')
83
+ MyAuthor.metadata[:qualified_attributes].keys.should_not include('attrs')
84
+ MyAuthor.metadata[:qualified_attributes].values.should_not include('attrs')
85
+ MyAuthor.metadata[:collections].should include('attrs')
86
+ MyAuthor.metadata[:types]['attr'].should be_nil
87
+ d = MyAuthor.new('')
88
+ d.attrs.should == []
89
+ d.attrs = [:something]
90
+ d.attrs.should == [:something]
91
+ end
92
+ end
93
+
94
+ describe 'find_attribute' do
95
+ it 'should find the attribute with namespace' do
96
+ MyAuthor.find_attribute('ns:realname', 'ns', 'realname').should == 'attr'
97
+ end
98
+ it 'should find the attribute' do
99
+ MyAuthor.find_attribute('attrs', nil, 'attrs').should == 'attrs'
100
+ end
101
+ it 'should pluralize and find the attribute' do
102
+ A::Entry.find_attribute('link', nil, 'link').should == 'links'
103
+ end
104
+ it 'should not find attr with a wrong namespace' do
105
+ MyAuthor.find_attribute('attr', nil, 'attr').should be_nil
106
+ end
107
+ it 'should find the attribute with implicit namespaced name' do
108
+ MyAuthor.find_attribute('ns:something', 'ns', 'something').should == 'ns_something'
109
+ end
110
+ end
111
+
112
+ describe 'flatten' do
113
+ it 'should set the metadata' do
114
+ MyAuthor.metadata[:flatten].should include('nest')
115
+ end
116
+ end
117
+
118
+ describe 'flatten?' do
119
+ it 'should work' do
120
+ MyAuthor.flatten?('nest').should be_true
121
+ MyAuthor.flatten?('attr').should be_false
122
+ end
123
+ end
124
+
125
+ describe 'namespace, namespaces and default_namespace' do
126
+ it 'should define the namespaces' do
127
+ MyAuthor.metadata[:namespaces].keys.should include('ns')
128
+ MyAuthor.metadata[:namespaces].keys.should include('blog')
129
+ MyAuthor.metadata[:namespaces].keys.should include('')
130
+ end
131
+ end
132
+
133
+ describe 'find_namespace' do
134
+ it 'should find a namespace' do
135
+ MyAuthor.find_namespace('blog').should be_nil
136
+ MyAuthor.find_namespace.should == 'http://place.com'
137
+ MyAuthor.find_namespace('').should == 'http://place.com'
138
+ MyAuthor.find_namespace('ns').should == 'http://example.com'
139
+ end
140
+ end
141
+
142
+ describe 'attribute_type' do
143
+ it 'should parse a symbol type definition in module scope' do
144
+ A::Feed.attribute_type('link').should == A::Link
145
+ end
146
+ it 'should parse a string type definition' do
147
+ MyAuthor.attribute_type('link').should == A::Link
148
+ end
149
+ end
150
+
151
+ describe 'set_type' do
152
+ it 'should work' do
153
+ MyAuthor.metadata[:types]['link'].to_s.should == 'Objectify::Atom::Link'
154
+ end
155
+ end
156
+
157
+ describe 'collection?' do
158
+ it 'should work' do
159
+ MyAuthor.collection?('ns:realname').should be_false
160
+ MyAuthor.collection?('attr').should be_false
161
+ MyAuthor.collection?('attrs').should be_true
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,38 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Objectify::ElementParser do
4
+ class TestElement < Objectify::ElementParser
5
+ attr_accessor :attr
6
+
7
+ def initialize(*args)
8
+ yield self if block_given?
9
+ super
10
+ end
11
+ end
12
+
13
+ class TestBodyElement < Objectify::ElementParser
14
+ attr_accessor :attr, :inner_html, :inner_text
15
+ end
16
+
17
+ describe 'primary_xml_element' do
18
+ before do
19
+ @xml = Nokogiri::XML('<element attr="value" attr2="value2"><div>body</div></element>').child
20
+ end
21
+ it 'should call xml_text_to_value for defined attributes only' do
22
+ TestElement.new(@xml) do |te|
23
+ te.expects(:xml_text_to_value).with('value')
24
+ te.expects(:xml_text_to_value).with('value2').never
25
+ end
26
+ end
27
+ it 'should set inner_html and inner_text if the methods are defined' do
28
+ e = TestBodyElement.new(@xml)
29
+ e.inner_html.should == '<div>body</div>'
30
+ e.inner_text.should == 'body'
31
+ end
32
+ it 'should not set inner_html and inner_text if the methods are not defined' do
33
+ @xml.expects(:inner_html).never
34
+ @xml.expects(:inner_text).never
35
+ e = TestElement.new(@xml)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,81 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Objectify::Xml do
4
+ class Xml < Objectify::Xml
5
+ def initialize(*args)
6
+ yield self if block_given?
7
+ super
8
+ end
9
+ public :xml_text_to_value
10
+ end
11
+
12
+ describe 'initialize' do
13
+ describe 'with nil' do
14
+ it 'should handle nil string' do
15
+ Xml.new(nil) do |x|
16
+ x.expects(:primary_xml_element).never
17
+ end
18
+ end
19
+ it 'should assign the parent' do
20
+ x = Xml.new(nil, :parent)
21
+ x.parent.should == :parent
22
+ end
23
+ it 'should initialize the attributes' do
24
+ x = Xml.new(nil)
25
+ x.attributes.should == {}
26
+ end
27
+ end
28
+
29
+ describe 'with multiple nodes before the first element' do
30
+ it 'should call parse_xml with the first element' do
31
+ xml_string = <<-exml
32
+ <?xml version="1.0"?>
33
+ <?xml-stylesheet type="text/css" href="http://en.wikipedia.org/skins-1.5/common/feed.css?206xx"?>
34
+ <feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"></feed>
35
+ exml
36
+ xml = Nokogiri::XML(xml_string).child.next
37
+ x = Xml.new(xml_string) do |x|
38
+ def x.primary_xml_element(xml)
39
+ xml.name.should == 'feed'
40
+ @parent = :called
41
+ end
42
+ end
43
+ x.parent.should == :called
44
+ end
45
+ end
46
+
47
+ describe 'with ""' do
48
+ it 'should not call primary_xml_element' do
49
+ Xml.new('') do |x|
50
+ x.expects(:primary_xml_element).never
51
+ end
52
+ end
53
+ end
54
+ describe 'with valid xml' do
55
+ it 'should call primary_xml_element with a Nokogiri XML object' do
56
+ Xml.new('<foo><bar>no</bar></foo>') do |x|
57
+ x.expects(:primary_xml_element)
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ describe 'inspect' do
64
+ it 'should work' do
65
+ f = Objectify::Atom::Feed.new(sample_feed('wikipedia.atom'))
66
+ f.inspect.should == '<Objectify::Atom::Feed title, id, subtitle, links:2, generator:Objectify::Atom::Generator, entries:50, updated>'
67
+ end
68
+ end
69
+
70
+ describe 'xml_text_to_value' do
71
+ it('should cast true') { Xml.new('').xml_text_to_value('true').should == true }
72
+ it('should cast false') { Xml.new('').xml_text_to_value('false').should == false }
73
+ it('should cast integer') { Xml.new('').xml_text_to_value('12').should == 12 }
74
+ it('should cast float') { Xml.new('').xml_text_to_value('3.5').should == 3.5 }
75
+ it('should cast date1') { Xml.new('').xml_text_to_value('2009-02-01T11:32:11.03Z').should be_an_instance_of(DateTime) }
76
+ it('should cast date2') { Xml.new('').xml_text_to_value('2009-02-01T11:32:11+400Z').should be_an_instance_of(DateTime) }
77
+ it('should cast date3') { Xml.new('').xml_text_to_value('2009-02-01T11:32:11').should be_an_instance_of(DateTime) }
78
+ it('should cast date4') { Xml.new('').xml_text_to_value('2009-02-01').should be_an_instance_of(DateTime) }
79
+ it('should not cast anything else') { Xml.new('').xml_text_to_value('anything').should == 'anything' }
80
+ end
81
+ end