Empact-roxml 2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +18 -0
- data/README.rdoc +122 -0
- data/Rakefile +104 -0
- data/lib/roxml.rb +362 -0
- data/lib/roxml/array.rb +15 -0
- data/lib/roxml/options.rb +175 -0
- data/lib/roxml/string.rb +35 -0
- data/lib/roxml/xml.rb +243 -0
- data/lib/roxml/xml/libxml.rb +63 -0
- data/lib/roxml/xml/rexml.rb +59 -0
- data/roxml.gemspec +78 -0
- data/test/fixtures/book_malformed.xml +5 -0
- data/test/fixtures/book_pair.xml +8 -0
- data/test/fixtures/book_text_with_attribute.xml +5 -0
- data/test/fixtures/book_valid.xml +5 -0
- data/test/fixtures/book_with_authors.xml +7 -0
- data/test/fixtures/book_with_contributions.xml +9 -0
- data/test/fixtures/book_with_contributors.xml +7 -0
- data/test/fixtures/book_with_contributors_attrs.xml +7 -0
- data/test/fixtures/book_with_default_namespace.xml +9 -0
- data/test/fixtures/book_with_depth.xml +6 -0
- data/test/fixtures/book_with_publisher.xml +7 -0
- data/test/fixtures/dictionary_of_attrs.xml +6 -0
- data/test/fixtures/dictionary_of_mixeds.xml +4 -0
- data/test/fixtures/dictionary_of_texts.xml +10 -0
- data/test/fixtures/library.xml +30 -0
- data/test/fixtures/library_uppercase.xml +30 -0
- data/test/fixtures/nameless_ageless_youth.xml +2 -0
- data/test/fixtures/person.xml +1 -0
- data/test/fixtures/person_with_guarded_mothers.xml +13 -0
- data/test/fixtures/person_with_mothers.xml +10 -0
- data/test/mocks/dictionaries.rb +56 -0
- data/test/mocks/mocks.rb +212 -0
- data/test/test_helper.rb +16 -0
- data/test/unit/options_test.rb +62 -0
- data/test/unit/roxml_test.rb +24 -0
- data/test/unit/string_test.rb +11 -0
- data/test/unit/to_xml_test.rb +75 -0
- data/test/unit/xml_attribute_test.rb +34 -0
- data/test/unit/xml_construct_test.rb +19 -0
- data/test/unit/xml_hash_test.rb +54 -0
- data/test/unit/xml_name_test.rb +14 -0
- data/test/unit/xml_namespace_test.rb +36 -0
- data/test/unit/xml_object_test.rb +94 -0
- data/test/unit/xml_text_test.rb +57 -0
- metadata +110 -0
data/test/mocks/mocks.rb
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
require "lib/roxml"
|
2
|
+
|
3
|
+
class Book
|
4
|
+
include ROXML
|
5
|
+
|
6
|
+
xml_accessor :isbn, :attr => 'ISBN'
|
7
|
+
xml_reader :title
|
8
|
+
xml_reader :description, :as => :cdata
|
9
|
+
xml_reader :author
|
10
|
+
xml_accessor :pages, :text => 'pagecount' do |val|
|
11
|
+
Integer(val)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class BookWithAttrFrom
|
16
|
+
include ROXML
|
17
|
+
|
18
|
+
xml_accessor :isbn, :attr, :from => 'ISBN'
|
19
|
+
end
|
20
|
+
|
21
|
+
class Measurement
|
22
|
+
include ROXML
|
23
|
+
|
24
|
+
xml_reader :units, :attr
|
25
|
+
xml_reader :value, :content
|
26
|
+
xml_construct :value, :units
|
27
|
+
|
28
|
+
def initialize(value, units = 'pixels')
|
29
|
+
@value = Float(value)
|
30
|
+
@units = units.to_s
|
31
|
+
if @units.starts_with? 'hundredths-'
|
32
|
+
@value /= 100
|
33
|
+
@units = @units.split('hundredths-')[1]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def ==(other)
|
38
|
+
other.units == @units && other.value == @value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class BookWithDepth
|
43
|
+
include ROXML
|
44
|
+
|
45
|
+
xml_reader :isbn, :attr => 'ISBN'
|
46
|
+
xml_reader :title
|
47
|
+
xml_reader :description, :as => :cdata
|
48
|
+
xml_reader :author
|
49
|
+
xml_reader :depth, Measurement
|
50
|
+
end
|
51
|
+
|
52
|
+
class InheritedBookWithDepth < Book
|
53
|
+
xml_reader :depth, Measurement
|
54
|
+
end
|
55
|
+
|
56
|
+
class Author
|
57
|
+
include ROXML
|
58
|
+
|
59
|
+
xml_reader :role, :attr
|
60
|
+
xml_reader :text, :content
|
61
|
+
end
|
62
|
+
|
63
|
+
class BookWithAuthors
|
64
|
+
include ROXML
|
65
|
+
|
66
|
+
xml_name :book
|
67
|
+
xml_reader :isbn, :attr, :from => 'ISBN'
|
68
|
+
xml_reader :title
|
69
|
+
xml_reader :description, :as => :cdata
|
70
|
+
xml_reader :authors, :as => :array
|
71
|
+
end
|
72
|
+
|
73
|
+
class BookWithAuthorTextAttribute
|
74
|
+
include ROXML
|
75
|
+
|
76
|
+
xml_name :book
|
77
|
+
xml_reader :isbn, :attr, :from => 'ISBN'
|
78
|
+
xml_reader :title
|
79
|
+
xml_reader :description, :as => :cdata
|
80
|
+
xml_reader :author, Author
|
81
|
+
end
|
82
|
+
|
83
|
+
class Contributor
|
84
|
+
include ROXML
|
85
|
+
|
86
|
+
xml_reader :role, :attr
|
87
|
+
xml_reader :name
|
88
|
+
end
|
89
|
+
|
90
|
+
class BookWithContributions
|
91
|
+
include ROXML
|
92
|
+
|
93
|
+
xml_name :book
|
94
|
+
xml_reader :isbn, :attr
|
95
|
+
xml_reader :title
|
96
|
+
xml_reader :description
|
97
|
+
xml_reader :contributions, [Contributor], :from => 'contributor', :in => "contributions"
|
98
|
+
end
|
99
|
+
|
100
|
+
class BookWithContributors
|
101
|
+
include ROXML
|
102
|
+
|
103
|
+
xml_name :book
|
104
|
+
xml_reader :isbn, :attr
|
105
|
+
xml_reader :title
|
106
|
+
xml_reader :description
|
107
|
+
xml_reader :contributors, Contributor, :as => :array
|
108
|
+
end
|
109
|
+
|
110
|
+
class NamelessBook
|
111
|
+
include ROXML
|
112
|
+
|
113
|
+
xml_reader :isbn, :attr
|
114
|
+
xml_reader :title
|
115
|
+
xml_reader :description
|
116
|
+
xml_reader :contributors, Contributor, :as => :array
|
117
|
+
end
|
118
|
+
|
119
|
+
class Publisher
|
120
|
+
include ROXML
|
121
|
+
|
122
|
+
xml_reader :name
|
123
|
+
end
|
124
|
+
|
125
|
+
class BookWithPublisher
|
126
|
+
include ROXML
|
127
|
+
|
128
|
+
xml_reader :book
|
129
|
+
xml_reader :isbn, :attr
|
130
|
+
xml_reader :title
|
131
|
+
xml_reader :description
|
132
|
+
xml_reader :publisher, Publisher
|
133
|
+
end
|
134
|
+
|
135
|
+
class BookPair
|
136
|
+
include ROXML
|
137
|
+
|
138
|
+
xml_reader :isbn, :attr
|
139
|
+
xml_reader :title
|
140
|
+
xml_reader :description
|
141
|
+
xml_reader :author
|
142
|
+
xml_reader :book, Book
|
143
|
+
end
|
144
|
+
|
145
|
+
class Library
|
146
|
+
include ROXML
|
147
|
+
|
148
|
+
xml_reader :name
|
149
|
+
xml_reader :books, BookWithContributions, :as => :array
|
150
|
+
end
|
151
|
+
|
152
|
+
class UppercaseLibrary
|
153
|
+
include ROXML
|
154
|
+
|
155
|
+
xml_name :library
|
156
|
+
xml_reader :name, :from => 'NAME'
|
157
|
+
xml_reader :books, [BookWithContributions], :from => 'BOOK'
|
158
|
+
end
|
159
|
+
|
160
|
+
class LibraryWithBooksOfUnderivableName
|
161
|
+
include ROXML
|
162
|
+
|
163
|
+
xml :name, true
|
164
|
+
xml_reader :novels, NamelessBook, :as => :array
|
165
|
+
end
|
166
|
+
|
167
|
+
class NodeWithNameConflicts
|
168
|
+
include ROXML
|
169
|
+
|
170
|
+
xml_name :node
|
171
|
+
xml_reader :content
|
172
|
+
xml_reader :name
|
173
|
+
end
|
174
|
+
|
175
|
+
class NodeWithAttrNameConflicts
|
176
|
+
include ROXML
|
177
|
+
|
178
|
+
xml_name :node
|
179
|
+
xml_reader :content, :attr => :content
|
180
|
+
xml_reader :name, :attr => :name
|
181
|
+
end
|
182
|
+
|
183
|
+
class Person
|
184
|
+
include ROXML
|
185
|
+
|
186
|
+
xml_reader :age, :attr, :else => 21
|
187
|
+
xml_accessor :name, :content, :else => 'Unknown'
|
188
|
+
end
|
189
|
+
|
190
|
+
class PersonWithMother
|
191
|
+
include ROXML
|
192
|
+
|
193
|
+
xml_name :person
|
194
|
+
xml_reader :name
|
195
|
+
xml_reader :mother, PersonWithMother
|
196
|
+
end
|
197
|
+
|
198
|
+
class PersonWithGuardedMother
|
199
|
+
include ROXML
|
200
|
+
|
201
|
+
xml_name :person
|
202
|
+
xml_reader :name
|
203
|
+
xml_reader :mother, PersonWithGuardedMother, :from => :person, :in => :mother
|
204
|
+
end
|
205
|
+
|
206
|
+
class PersonWithMotherOrMissing
|
207
|
+
include ROXML
|
208
|
+
|
209
|
+
xml_reader :age, :attr, :else => 21
|
210
|
+
xml_reader :name, :else => 'Anonymous'
|
211
|
+
xml_reader :mother, PersonWithMotherOrMissing, :else => Person.new
|
212
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require "lib/roxml"
|
2
|
+
require "test/unit"
|
3
|
+
require 'test/mocks/mocks'
|
4
|
+
require 'test/mocks/dictionaries'
|
5
|
+
|
6
|
+
def fixture(name)
|
7
|
+
File.read(fixture_path(name))
|
8
|
+
end
|
9
|
+
|
10
|
+
def xml_fixture(name)
|
11
|
+
ROXML::XML::Parser.parse_file(fixture_path(name)).root
|
12
|
+
end
|
13
|
+
|
14
|
+
def fixture_path(name)
|
15
|
+
"test/fixtures/#{name}.xml"
|
16
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'test_helper')
|
2
|
+
|
3
|
+
class TestOptions < Test::Unit::TestCase
|
4
|
+
def test_text_in_array_means_as_array_for_text
|
5
|
+
opts = ROXML::Opts.new(:authors, [:text])
|
6
|
+
assert opts.array?
|
7
|
+
assert_equal :text, opts.type
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_attr_in_array_means_as_array_for_attr
|
11
|
+
opts = ROXML::Opts.new(:authors, [:attr])
|
12
|
+
assert opts.array?
|
13
|
+
assert_equal :attr, opts.type
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_object_in_array_means_as_array_for_object
|
17
|
+
opts = ROXML::Opts.new(:authors, [Hash])
|
18
|
+
assert opts.array?
|
19
|
+
assert_equal Hash, opts.type
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_content_is_a_recognized_type
|
23
|
+
assert ROXML::Opts.new(:author, :content).content?
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_hash_of_attrs
|
27
|
+
opts = ROXML::Opts.new(:attributes, {:attrs => [:name, :value]})
|
28
|
+
assert opts.hash?
|
29
|
+
assert !opts.array?
|
30
|
+
assert_equal [ROXML::XMLAttributeRef, ROXML::XMLAttributeRef], opts.hash.types
|
31
|
+
assert_equal ['name', 'value'], opts.hash.names
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_hash_with_attr_key_and_text_val
|
35
|
+
opts = ROXML::Opts.new(:attributes, {:key => {:attr => :name},
|
36
|
+
:value => :value})
|
37
|
+
assert opts.hash?
|
38
|
+
assert !opts.array?
|
39
|
+
assert_equal [ROXML::XMLAttributeRef, ROXML::XMLTextRef], opts.hash.types
|
40
|
+
assert_equal ['name', 'value'], opts.hash.names
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_hash_with_attr_key_and_content_val
|
44
|
+
opts = ROXML::Opts.new(:attributes, {:key => {:attr => :name},
|
45
|
+
:value => :content})
|
46
|
+
assert opts.hash?
|
47
|
+
assert !opts.array?
|
48
|
+
assert opts.hash.value.content
|
49
|
+
assert_equal [ROXML::XMLAttributeRef, ROXML::XMLTextRef], opts.hash.types
|
50
|
+
assert_equal ['name', ''], opts.hash.names
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_hash_with_options
|
54
|
+
opts = ROXML::Opts.new(:definitions, {:attrs => [:dt, :dd]},
|
55
|
+
:in => :definitions)
|
56
|
+
|
57
|
+
assert opts.hash?
|
58
|
+
assert !opts.array?
|
59
|
+
assert_equal [ROXML::XMLAttributeRef, ROXML::XMLAttributeRef], opts.hash.types
|
60
|
+
assert_equal ['dt', 'dd'], opts.hash.names
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'test_helper')
|
2
|
+
|
3
|
+
class TestROXML < Test::Unit::TestCase
|
4
|
+
# Malformed XML parsing should throw REXML::ParseException
|
5
|
+
def test_malformed
|
6
|
+
ROXML::XML::Parser.register_error_handler {|err| }
|
7
|
+
assert_raise ROXML::XML::Parser::ParseError do
|
8
|
+
book = Book.parse(fixture(:book_malformed))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Verify that an exception is thrown when two accessors have the same
|
13
|
+
# name in a ROXML class.
|
14
|
+
def test_duplicate_accessor
|
15
|
+
assert_raise RuntimeError do
|
16
|
+
Class.new do
|
17
|
+
include ROXML
|
18
|
+
|
19
|
+
xml_reader :id
|
20
|
+
xml_accessor :id
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'lib/roxml/string'
|
2
|
+
|
3
|
+
class TestROXML < Test::Unit::TestCase
|
4
|
+
def test_to_latin_is_accessible
|
5
|
+
assert String.instance_methods.include?('to_latin')
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_to_utf_is_accessible
|
9
|
+
assert String.instance_methods.include?('to_utf')
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'test_helper')
|
2
|
+
|
3
|
+
def to_xml_test(*names)
|
4
|
+
names = names.only if names.one? && names.only.is_a?(Hash)
|
5
|
+
names.each do |(name, xml_name)|
|
6
|
+
xml_name ||= name
|
7
|
+
|
8
|
+
define_method "test_#{name}" do
|
9
|
+
dict = name.to_s.camelize.constantize.parse(fixture(xml_name))
|
10
|
+
xml = xml_fixture(xml_name)
|
11
|
+
remove_children(xml)
|
12
|
+
assert_equal xml, dict.to_xml
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def remove_children(xml)
|
18
|
+
return unless xml.respond_to? :children
|
19
|
+
xml.children.each do |child|
|
20
|
+
if child.blank?
|
21
|
+
child.remove!
|
22
|
+
else
|
23
|
+
remove_children(child)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class TestHashToXml < Test::Unit::TestCase
|
29
|
+
to_xml_test :dictionary_of_attrs,
|
30
|
+
:dictionary_of_mixeds,
|
31
|
+
:dictionary_of_texts,
|
32
|
+
:dictionary_of_names,
|
33
|
+
:dictionary_of_guarded_names,
|
34
|
+
:dictionary_of_name_clashes,
|
35
|
+
:dictionary_of_attr_name_clashes
|
36
|
+
end
|
37
|
+
|
38
|
+
class TestOtherToXml < Test::Unit::TestCase
|
39
|
+
to_xml_test :book => :book_valid,
|
40
|
+
:book_with_author_text_attribute => :book_text_with_attribute,
|
41
|
+
:uppercase_library => :library_uppercase
|
42
|
+
|
43
|
+
to_xml_test :book_with_authors,
|
44
|
+
:book_with_contributors,
|
45
|
+
:book_with_contributions,
|
46
|
+
:library,
|
47
|
+
:node_with_name_conflicts,
|
48
|
+
:node_with_attr_name_conflicts
|
49
|
+
|
50
|
+
to_xml_test :person_with_mother => :person_with_mothers,
|
51
|
+
:person_with_guarded_mother => :person_with_guarded_mothers
|
52
|
+
end
|
53
|
+
|
54
|
+
class TestToXmlWithDefaults < Test::Unit::TestCase
|
55
|
+
def test_content_and_attr_defaults_are_represented_in_output
|
56
|
+
dict = Person.parse(fixture(:nameless_ageless_youth))
|
57
|
+
|
58
|
+
xml = '<person age="21">Unknown</person>'
|
59
|
+
assert_equal ROXML::XML::Parser.parse(xml).root, dict.to_xml
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class TestToXmlWithBlocks < Test::Unit::TestCase
|
64
|
+
def test_pagecount_serialized_properly_after_modification
|
65
|
+
b = Book.parse(fixture(:book_valid))
|
66
|
+
xml = xml_fixture(:book_valid)
|
67
|
+
assert_equal '357', xml.search('pagecount').first.content
|
68
|
+
assert_equal 357, b.pages
|
69
|
+
|
70
|
+
b.pages = 500
|
71
|
+
doc = ROXML::XML::Document.new()
|
72
|
+
doc.root = b.to_xml
|
73
|
+
assert_equal '500', doc.search('pagecount').first.content
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'test_helper')
|
2
|
+
|
3
|
+
class TestXMLAttribute < Test::Unit::TestCase
|
4
|
+
def test_attr_from
|
5
|
+
# :attr => *
|
6
|
+
book = Book.parse(fixture(:book_text_with_attribute))
|
7
|
+
assert_equal '0201710897', book.isbn
|
8
|
+
|
9
|
+
# :attr, :from => *
|
10
|
+
book = BookWithAttrFrom.parse(fixture(:book_text_with_attribute))
|
11
|
+
assert_equal '0201710897', book.isbn
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_mutable_attr
|
15
|
+
book = Book.parse(fixture(:book_text_with_attribute))
|
16
|
+
assert book.respond_to?(:'isbn=')
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_default_initialization
|
20
|
+
person = PersonWithMotherOrMissing.parse(fixture(:nameless_ageless_youth))
|
21
|
+
assert_equal 21, person.age
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_recursive_with_default_initialization
|
25
|
+
p = PersonWithMotherOrMissing.parse(fixture(:person_with_mothers))
|
26
|
+
assert_equal 21, p.mother.mother.mother.age
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_no_name_clashes
|
30
|
+
n = NodeWithAttrNameConflicts.parse(fixture(:node_with_attr_name_conflicts))
|
31
|
+
assert_equal "Just junk... really", n.content
|
32
|
+
assert_equal "Cartwheel", n.name
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'test_helper')
|
2
|
+
|
3
|
+
class TestXMLAttribute < Test::Unit::TestCase
|
4
|
+
def test_initialize_is_run
|
5
|
+
m = Measurement.parse('<measurement units="hundredths-meters">1130</measurement>')
|
6
|
+
assert_equal 11.3, m.value
|
7
|
+
assert_equal 'meters', m.units
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_initialize_is_run_for_nested_type
|
11
|
+
b = BookWithDepth.parse(fixture(:book_with_depth))
|
12
|
+
assert_equal Measurement.new(11.3, 'meters'), b.depth
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_initialize_is_run_for_nested_type_with_inheritance
|
16
|
+
b = InheritedBookWithDepth.parse(fixture(:book_with_depth))
|
17
|
+
assert_equal Measurement.new(11.3, 'meters'), b.depth
|
18
|
+
end
|
19
|
+
end
|