Empact-roxml 2.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 (46) hide show
  1. data/MIT-LICENSE +18 -0
  2. data/README.rdoc +122 -0
  3. data/Rakefile +104 -0
  4. data/lib/roxml.rb +362 -0
  5. data/lib/roxml/array.rb +15 -0
  6. data/lib/roxml/options.rb +175 -0
  7. data/lib/roxml/string.rb +35 -0
  8. data/lib/roxml/xml.rb +243 -0
  9. data/lib/roxml/xml/libxml.rb +63 -0
  10. data/lib/roxml/xml/rexml.rb +59 -0
  11. data/roxml.gemspec +78 -0
  12. data/test/fixtures/book_malformed.xml +5 -0
  13. data/test/fixtures/book_pair.xml +8 -0
  14. data/test/fixtures/book_text_with_attribute.xml +5 -0
  15. data/test/fixtures/book_valid.xml +5 -0
  16. data/test/fixtures/book_with_authors.xml +7 -0
  17. data/test/fixtures/book_with_contributions.xml +9 -0
  18. data/test/fixtures/book_with_contributors.xml +7 -0
  19. data/test/fixtures/book_with_contributors_attrs.xml +7 -0
  20. data/test/fixtures/book_with_default_namespace.xml +9 -0
  21. data/test/fixtures/book_with_depth.xml +6 -0
  22. data/test/fixtures/book_with_publisher.xml +7 -0
  23. data/test/fixtures/dictionary_of_attrs.xml +6 -0
  24. data/test/fixtures/dictionary_of_mixeds.xml +4 -0
  25. data/test/fixtures/dictionary_of_texts.xml +10 -0
  26. data/test/fixtures/library.xml +30 -0
  27. data/test/fixtures/library_uppercase.xml +30 -0
  28. data/test/fixtures/nameless_ageless_youth.xml +2 -0
  29. data/test/fixtures/person.xml +1 -0
  30. data/test/fixtures/person_with_guarded_mothers.xml +13 -0
  31. data/test/fixtures/person_with_mothers.xml +10 -0
  32. data/test/mocks/dictionaries.rb +56 -0
  33. data/test/mocks/mocks.rb +212 -0
  34. data/test/test_helper.rb +16 -0
  35. data/test/unit/options_test.rb +62 -0
  36. data/test/unit/roxml_test.rb +24 -0
  37. data/test/unit/string_test.rb +11 -0
  38. data/test/unit/to_xml_test.rb +75 -0
  39. data/test/unit/xml_attribute_test.rb +34 -0
  40. data/test/unit/xml_construct_test.rb +19 -0
  41. data/test/unit/xml_hash_test.rb +54 -0
  42. data/test/unit/xml_name_test.rb +14 -0
  43. data/test/unit/xml_namespace_test.rb +36 -0
  44. data/test/unit/xml_object_test.rb +94 -0
  45. data/test/unit/xml_text_test.rb +57 -0
  46. metadata +110 -0
@@ -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
@@ -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