representable 0.0.1.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. data/.gitignore +7 -0
  2. data/.gitmodules +3 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +3 -0
  5. data/History.txt +354 -0
  6. data/LICENSE +20 -0
  7. data/README.rdoc +186 -0
  8. data/Rakefile +10 -0
  9. data/TODO +37 -0
  10. data/VERSION +1 -0
  11. data/examples/amazon.rb +35 -0
  12. data/examples/current_weather.rb +27 -0
  13. data/examples/dashed_elements.rb +20 -0
  14. data/examples/library.rb +40 -0
  15. data/examples/posts.rb +27 -0
  16. data/examples/rails.rb +70 -0
  17. data/examples/twitter.rb +37 -0
  18. data/examples/xml/active_record.xml +70 -0
  19. data/examples/xml/amazon.xml +133 -0
  20. data/examples/xml/current_weather.xml +89 -0
  21. data/examples/xml/dashed_elements.xml +52 -0
  22. data/examples/xml/posts.xml +23 -0
  23. data/examples/xml/twitter.xml +422 -0
  24. data/lib/representable.rb +257 -0
  25. data/lib/representable/definition.rb +109 -0
  26. data/lib/representable/nokogiri_extensions.rb +19 -0
  27. data/lib/representable/references.rb +153 -0
  28. data/lib/representable/version.rb +3 -0
  29. data/lib/representable/xml.rb +79 -0
  30. data/representable.gemspec +29 -0
  31. data/spec/definition_spec.rb +495 -0
  32. data/spec/examples/active_record_spec.rb +41 -0
  33. data/spec/examples/amazon_spec.rb +54 -0
  34. data/spec/examples/current_weather_spec.rb +37 -0
  35. data/spec/examples/dashed_elements_spec.rb +20 -0
  36. data/spec/examples/library_spec.rb +46 -0
  37. data/spec/examples/post_spec.rb +24 -0
  38. data/spec/examples/twitter_spec.rb +32 -0
  39. data/spec/roxml_integration_test.rb +289 -0
  40. data/spec/roxml_spec.rb +372 -0
  41. data/spec/shared_specs.rb +15 -0
  42. data/spec/spec_helper.rb +5 -0
  43. data/spec/support/libxml.rb +3 -0
  44. data/spec/support/nokogiri.rb +3 -0
  45. data/spec/xml/array_spec.rb +36 -0
  46. data/spec/xml/attributes_spec.rb +71 -0
  47. data/spec/xml/encoding_spec.rb +53 -0
  48. data/spec/xml/namespace_spec.rb +270 -0
  49. data/spec/xml/namespaces_spec.rb +67 -0
  50. data/spec/xml/object_spec.rb +82 -0
  51. data/spec/xml/parser_spec.rb +21 -0
  52. data/spec/xml/text_spec.rb +71 -0
  53. data/test/fixtures/book_malformed.xml +5 -0
  54. data/test/fixtures/book_pair.xml +8 -0
  55. data/test/fixtures/book_text_with_attribute.xml +5 -0
  56. data/test/fixtures/book_valid.xml +5 -0
  57. data/test/fixtures/book_with_authors.xml +7 -0
  58. data/test/fixtures/book_with_contributions.xml +9 -0
  59. data/test/fixtures/book_with_contributors.xml +7 -0
  60. data/test/fixtures/book_with_contributors_attrs.xml +7 -0
  61. data/test/fixtures/book_with_default_namespace.xml +9 -0
  62. data/test/fixtures/book_with_depth.xml +6 -0
  63. data/test/fixtures/book_with_octal_pages.xml +4 -0
  64. data/test/fixtures/book_with_publisher.xml +7 -0
  65. data/test/fixtures/book_with_wrapped_attr.xml +3 -0
  66. data/test/fixtures/dictionary_of_attr_name_clashes.xml +8 -0
  67. data/test/fixtures/dictionary_of_attrs.xml +6 -0
  68. data/test/fixtures/dictionary_of_guarded_names.xml +6 -0
  69. data/test/fixtures/dictionary_of_mixeds.xml +4 -0
  70. data/test/fixtures/dictionary_of_name_clashes.xml +10 -0
  71. data/test/fixtures/dictionary_of_names.xml +4 -0
  72. data/test/fixtures/dictionary_of_texts.xml +10 -0
  73. data/test/fixtures/library.xml +30 -0
  74. data/test/fixtures/library_uppercase.xml +30 -0
  75. data/test/fixtures/muffins.xml +3 -0
  76. data/test/fixtures/nameless_ageless_youth.xml +2 -0
  77. data/test/fixtures/node_with_attr_name_conflicts.xml +1 -0
  78. data/test/fixtures/node_with_name_conflicts.xml +4 -0
  79. data/test/fixtures/numerology.xml +4 -0
  80. data/test/fixtures/person.xml +1 -0
  81. data/test/fixtures/person_with_guarded_mothers.xml +13 -0
  82. data/test/fixtures/person_with_mothers.xml +10 -0
  83. data/test/mocks/dictionaries.rb +57 -0
  84. data/test/mocks/mocks.rb +279 -0
  85. data/test/roxml_test.rb +58 -0
  86. data/test/support/fixtures.rb +11 -0
  87. data/test/test_helper.rb +6 -0
  88. data/test/unit/definition_test.rb +235 -0
  89. data/test/unit/deprecations_test.rb +24 -0
  90. data/test/unit/to_xml_test.rb +81 -0
  91. data/test/unit/xml_attribute_test.rb +39 -0
  92. data/test/unit/xml_block_test.rb +81 -0
  93. data/test/unit/xml_bool_test.rb +122 -0
  94. data/test/unit/xml_convention_test.rb +150 -0
  95. data/test/unit/xml_hash_test.rb +115 -0
  96. data/test/unit/xml_initialize_test.rb +49 -0
  97. data/test/unit/xml_name_test.rb +141 -0
  98. data/test/unit/xml_namespace_test.rb +31 -0
  99. data/test/unit/xml_object_test.rb +206 -0
  100. data/test/unit/xml_required_test.rb +94 -0
  101. data/test/unit/xml_text_test.rb +71 -0
  102. data/website/index.html +98 -0
  103. metadata +248 -0
@@ -0,0 +1,49 @@
1
+ require_relative './../test_helper'
2
+
3
+ class InheritedBookWithDepth < Book
4
+ xml_reader :depth, :as => Measurement
5
+ end
6
+
7
+ class BookWithXmlInitialize < BookWithDepth
8
+ attr_reader :created_at, :creator
9
+
10
+ def initialize(created_at, creator = "Unknown")
11
+ @created_at = created_at
12
+ @creator = creator
13
+ end
14
+ end
15
+
16
+ class TestXMLInitialize < ActiveSupport::TestCase
17
+ def test_initialize_is_run
18
+ m = Measurement.from_xml('<measurement units="hundredths-meters">1130</measurement>')
19
+ assert_equal 11.3, m.value
20
+ assert_equal 'meters', m.units
21
+ end
22
+
23
+ def test_initialize_is_run_for_nested_type
24
+ b = BookWithDepth.from_xml(fixture(:book_with_depth))
25
+ assert_equal Measurement.new(11.3, 'meters'), b.depth
26
+ end
27
+
28
+ def test_initialize_is_run_for_nested_type_with_inheritance
29
+ b = InheritedBookWithDepth.from_xml(fixture(:book_with_depth))
30
+ assert_equal Measurement.new(11.3, 'meters'), b.depth
31
+ end
32
+
33
+ def test_initialize_fails_on_missing_required_arg
34
+ assert_raise ArgumentError do
35
+ BookWithXmlInitialize.from_xml(fixture(:book_with_depth))
36
+ end
37
+ end
38
+
39
+ def test_initialize_with_extra_args
40
+ now = Time.now
41
+ b = BookWithXmlInitialize.from_xml(fixture(:book_with_depth), now)
42
+ assert_equal now, b.created_at
43
+ assert_equal "Unknown", b.creator
44
+
45
+ b = BookWithXmlInitialize.from_xml(fixture(:book_with_depth), Time.now, "Joe Librarian")
46
+ assert now < b.created_at
47
+ assert_equal "Joe Librarian", b.creator
48
+ end
49
+ end
@@ -0,0 +1,141 @@
1
+ require_relative './../test_helper'
2
+
3
+ # Parent | Child
4
+ # :from | no :from |
5
+ # -------------------|--------------
6
+ # :from | xml_name | xml_name-d
7
+ # value | value |
8
+ # -------------------|--------------
9
+ # :from | parent's |
10
+ # value | accessor | un-xml_name-d
11
+ # | name |
12
+
13
+ class Child
14
+ include ROXML
15
+ end
16
+
17
+ class NamedChild
18
+ include ROXML
19
+
20
+ xml_name :xml_name_of_child
21
+ end
22
+
23
+ class ParentOfNamedChild
24
+ include ROXML
25
+
26
+ xml_name :parent
27
+ xml_accessor :child_accessor_name, :as => NamedChild
28
+ end
29
+
30
+ class ParentOfNamedChildWithFrom
31
+ include ROXML
32
+
33
+ xml_name :parent
34
+ xml_accessor :child_accessor_name, :as => NamedChild, :from => 'child_from_name'
35
+ end
36
+
37
+ class ParentOfUnnamedChild
38
+ include ROXML
39
+
40
+ xml_name :parent
41
+ xml_accessor :child_accessor_name, :as => Child
42
+ end
43
+
44
+ class ParentOfUnnamedChildWithFrom
45
+ include ROXML
46
+
47
+ xml_name :parent
48
+ xml_accessor :child_accessor_name,:as => Child, :from => 'child_from_name'
49
+ end
50
+
51
+ class TestXMLName < ActiveSupport::TestCase
52
+ def test_from_always_dominates_attribute_name_xml_name_or_not
53
+ parent = ParentOfNamedChildWithFrom.new
54
+ parent.child_accessor_name = Child.new
55
+
56
+ assert_equal "<parent><child_from_name/></parent>", parent.to_xml.to_s.gsub(/[\n ]/, '')
57
+
58
+ parent = ParentOfUnnamedChildWithFrom.new
59
+ parent.child_accessor_name = Child.new
60
+
61
+ assert_equal "<parent><child_from_name/></parent>", parent.to_xml.to_s.gsub(/[\n ]/, '')
62
+ end
63
+
64
+ def test_attribute_name_comes_from_the_xml_name_value_if_present
65
+ parent = ParentOfNamedChild.new
66
+ parent.child_accessor_name = Child.new
67
+
68
+ assert_equal "<parent><xml_name_of_child/></parent>", parent.to_xml.to_s.gsub(/[\n ]/, '')
69
+ end
70
+
71
+ def test_attribute_name_comes_from_parent_accessor_by_default
72
+ parent = ParentOfUnnamedChild.new
73
+ parent.child_accessor_name = Child.new
74
+
75
+ assert_equal "<parent><child_accessor_name/></parent>", parent.to_xml.to_s.gsub(/[\n ]/, '')
76
+ end
77
+
78
+ def test_it_should_be_inherited
79
+ class_with_inherited_name = Class.new(ParentOfNamedChild)
80
+ assert_equal :parent, class_with_inherited_name.tag_name
81
+ end
82
+
83
+ def test_it_should_be_inherited_over_multiple_levels
84
+ class_with_inherited_name = Class.new(Class.new(ParentOfNamedChild))
85
+ assert_equal :parent, class_with_inherited_name.tag_name
86
+ end
87
+
88
+ def test_named_books_picked_up
89
+ named = Library.from_xml(fixture(:library))
90
+ assert named.books
91
+ assert_equal :book, named.books.first.class.tag_name
92
+ end
93
+
94
+ def test_nameless_books_missing
95
+ nameless = LibraryWithBooksOfUnderivableName.from_xml(fixture(:library))
96
+ assert nameless.novels.empty?
97
+ end
98
+
99
+ def test_tag_name
100
+ assert_equal :dictionary, DictionaryOfTexts.tag_name
101
+
102
+ dict = DictionaryOfTexts.from_xml(fixture(:dictionary_of_texts))
103
+
104
+ assert_equal :dictionary, dict.class.tag_name
105
+ end
106
+
107
+ def test_roxml_attrs
108
+ assert_equal 'definition', DictionaryOfTexts.roxml_attrs.first.name
109
+ assert_equal 'word', DictionaryOfTexts.roxml_attrs.first.hash.key.name
110
+ assert_equal 'meaning', DictionaryOfTexts.roxml_attrs.first.hash.value.name
111
+ end
112
+
113
+ def test_xml_name_should_not_be_conventionalized_if_explicitly_set
114
+ reference = ROXML::XMLTextRef.new(ROXML::Definition.new(:name, :from => 'georss:name'), WrapModule::InstanceStandin.new)
115
+ assert_equal "georss:name", reference.name
116
+ end
117
+
118
+ def test_xml_name_not_screwed_up_by_xml_convention
119
+ reference = ROXML::XMLTextRef.new(ROXML::Definition.new(:name, :in => './'), WrapModule::InstanceStandin.new)
120
+ assert_equal "name value", reference.value_in(ROXML::XML.parse_string(%(
121
+ <Wrapper>
122
+ <MoreStuff>
123
+ <DeepWrapper>
124
+ <Name>name value</Name>
125
+ </DeepWrapper>
126
+ </MoreStuff>
127
+ </Wrapper>
128
+ )).root)
129
+ end
130
+ end
131
+
132
+ module WrapModule
133
+ class BaseClass
134
+ include ROXML
135
+ xml_convention :camelcase
136
+ end
137
+
138
+ class InstanceStandin < BaseClass
139
+ xml_reader :name, :in => './'
140
+ end
141
+ end
@@ -0,0 +1,31 @@
1
+ require_relative './../test_helper'
2
+
3
+ class TestDefaultXMLNamespaces < ActiveSupport::TestCase
4
+ def setup
5
+ @book = BookWithContributions.from_xml(fixture(:book_with_default_namespace))
6
+ end
7
+
8
+ def test_default_namespace_doesnt_interfere_with_normal_operation
9
+ assert_equal("Programming Ruby - 2nd Edition", @book.title)
10
+ end
11
+
12
+ def test_default_namespace_is_applied_to_in_element
13
+ expected_authors = ["David Thomas","Andrew Hunt","Chad Fowler"]
14
+ assert !@book.contributions.empty?
15
+ @book.contributions.each do |contributor|
16
+ assert expected_authors.include?(contributor.name)
17
+ end
18
+ end
19
+
20
+ def test_default_namespace_on_root_node_should_be_found
21
+ require 'libxml'
22
+ xml = LibXML::XML::Parser.string(
23
+ '<container xmlns="http://defaultnamespace.org"><node>Yeah, content</node><node><subnode>Another</subnode></node></container>').parse
24
+
25
+ assert_equal nil, xml.find_first('node')
26
+ assert_equal "Yeah, content", xml.find_first('ns:node', 'ns:http://defaultnamespace.org').content
27
+ assert_equal nil, xml.find_first('ns:node/subnode', 'ns:http://defaultnamespace.org')
28
+ assert_equal "Another", xml.find_first('ns:node/ns:subnode', 'ns:http://defaultnamespace.org').content
29
+ rescue LoadError
30
+ end
31
+ end
@@ -0,0 +1,206 @@
1
+ # encoding: utf-8
2
+ require_relative './../test_helper'
3
+
4
+ class EmptyCart
5
+ include ROXML
6
+
7
+ xml_reader :id
8
+
9
+ def empty?
10
+ true
11
+ end
12
+ end
13
+
14
+ class CartHolder
15
+ include ROXML
16
+
17
+ xml_reader :cart, :as => EmptyCart, :required => true
18
+ end
19
+
20
+ class TestXMLObject < ActiveSupport::TestCase
21
+ # Test book with text and attribute
22
+ def test_book_author_text_attribute
23
+ book = BookWithAuthorTextAttribute.from_xml(fixture(:book_text_with_attribute))
24
+ assert_equal("primary",book.author.role)
25
+ assert_equal("David Thomas",book.author.text)
26
+ end
27
+
28
+ # Test XML object containing list of other XML objects (one-to-many)
29
+ # In this case, book with contibutions
30
+ def test_one_to_many_with_container
31
+ expected_authors = ["David Thomas","Andrew Hunt","Chad Fowler"]
32
+ book = BookWithContributions.from_xml(fixture(:book_with_contributions))
33
+ assert_equal("Programming Ruby - 2nd Edition", book.title)
34
+ book.contributions.each do |contributor|
35
+ assert expected_authors.include?(contributor.name)
36
+ end
37
+ end
38
+
39
+ # Test XML object containing 1-n other XML objects without container (one-to-many)
40
+ # In this case, book with contibutions
41
+ def test_one_to_many_without_container
42
+ expected_contributors = ["David Thomas","Andrew Hunt","Chad Fowler"]
43
+ book = BookWithContributors.from_xml(fixture(:book_with_contributors))
44
+ assert_equal("Programming Ruby - 2nd Edition", book.title)
45
+ book.contributors.each do |contributor|
46
+ assert(expected_contributors.include?(contributor.name))
47
+ end
48
+ end
49
+
50
+ # when building objects that contain arrays, the second object seems to
51
+ # inherit data from the first
52
+ #
53
+ def test_one_to_many_without_container_sequence
54
+ contrib = WriteableContributor.new
55
+ contrib.name = "David Thomas"
56
+
57
+ book_one = WriteableBookWithContributors.new
58
+ book_one.isbn = "9781843549161"
59
+ book_one.title = "Anathem"
60
+ book_one.description = "A new title from Neal Stephenson"
61
+ book_one.contributors = [contrib]
62
+
63
+ # this book should be completely empty
64
+ book_two = WriteableBookWithContributors.new
65
+
66
+ assert_equal(nil, book_two.isbn)
67
+ assert_equal(nil, book_two.title)
68
+ assert_equal(nil, book_two.description)
69
+ assert_equal(nil, book_two.contributors)
70
+ end
71
+
72
+ # Test XML object containing one other XML object (one-to-one)
73
+ # In this case, book with publisher
74
+ def test_one_to_one
75
+ book = BookWithPublisher.from_xml(fixture(:book_with_publisher))
76
+ assert_equal("Programming Ruby - 2nd Edition", book.title)
77
+ assert_equal("Pragmatic Bookshelf", book.publisher.name)
78
+ end
79
+
80
+ # Test XML object containing type of self (self-reference)
81
+ def test_self_reference
82
+ book = BookPair.from_xml(fixture(:book_pair))
83
+ assert_equal("Programming Ruby - 2nd Edition", book.title)
84
+ assert_equal("Agile Web Development with Rails", book.book.title)
85
+ end
86
+
87
+ # Test three-level composition (one-to-many-to-many)
88
+ def test_one_to_many_to_many
89
+ expected_contributors = ["David Thomas","Andrew Hunt","Chad Fowler", "David Heinemeier Hansson"]
90
+ expected_books = ["Programming Ruby - 2nd Edition", "Agile Web Development with Rails"]
91
+ library = Library.from_xml(fixture(:library))
92
+ assert_equal("Ruby library", library.name)
93
+ assert !library.books.empty?
94
+ library.books.each do |book|
95
+ assert expected_books.include?(book.title)
96
+ book.contributions.each do |contributor|
97
+ assert(expected_contributors.include?(contributor.name))
98
+ end
99
+ end
100
+ end
101
+
102
+ def test_without_needed_from
103
+ assert_equal [], UppercaseLibrary.from_xml(fixture(:library)).books
104
+ assert_equal [], Library.from_xml(fixture(:library_uppercase)).books
105
+ end
106
+
107
+ def test_with_needed_from
108
+ assert Library.from_xml(fixture(:library)).books
109
+ assert UppercaseLibrary.from_xml(fixture(:library_uppercase)).books
110
+ end
111
+
112
+ def test_with_recursion
113
+ p = PersonWithMother.from_xml(fixture(:person_with_mothers))
114
+ assert_equal 'Ben Franklin', p.name
115
+ assert_equal 'Abiah Folger', p.mother.name
116
+ assert_equal 'Madeup Mother', p.mother.mother.name
117
+ assert_equal nil, p.mother.mother.mother
118
+ end
119
+
120
+ class Node
121
+ include ROXML
122
+
123
+ xml_reader :name, :from => 'node_name'
124
+ xml_reader :nodes, :as => [Node]
125
+ end
126
+
127
+ class Taxonomy
128
+ include ROXML
129
+
130
+ xml_reader :name, :from => 'taxonomy_name'
131
+ xml_reader :nodes, :as => [Node]
132
+ end
133
+
134
+ class Taxonomies
135
+ include ROXML
136
+ xml_reader :taxonomies, :as => [Taxonomy]
137
+ end
138
+
139
+ def test_more_recursion
140
+ taxonomies = Taxonomies.from_xml(<<HERE)
141
+ <?xml version="1.0" encoding="UTF-8" ?>
142
+ <!DOCTYPE taxonomies SYSTEM "taxonomy.dtd">
143
+ <taxonomies>
144
+ <taxonomy>
145
+ <taxonomy_name>World</taxonomy_name>
146
+ <node node_id="2" content_object_id="82534" object_type_id="2">
147
+ <node_name lang_iso="eng">Africa</node_name>
148
+ <node node_id="331" content_object_id="11" object_type_id="4">
149
+ <node_name lang_iso="eng">Algeria</node_name>
150
+ <node node_id="7271" content_object_id="117629" object_type_id="8">
151
+ <node_name lang_iso="eng">Algiers</node_name>
152
+ </node>
153
+ <node node_id="7272" content_object_id="117630" object_type_id="8">
154
+ <node_name lang_iso="eng">Gharda&#239;a</node_name>
155
+ </node>
156
+ <node node_id="7871" content_object_id="1000713999" object_type_id="8">
157
+ <node_name lang_iso="eng">El Oued</node_name>
158
+ </node>
159
+ <node node_id="7872" content_object_id="1000714008" object_type_id="8">
160
+ <node_name lang_iso="eng">Timimoun</node_name>
161
+ </node>
162
+ <node node_id="8903" content_object_id="1000565565" object_type_id="8">
163
+ <node_name lang_iso="eng">Annaba</node_name>
164
+ </node>
165
+ </node>
166
+ </node>
167
+ </taxonomy>
168
+ </taxonomies>
169
+ HERE
170
+ assert_equal 1, taxonomies.taxonomies.size
171
+ assert_equal 'World', taxonomies.taxonomies.first.name
172
+ node = taxonomies.taxonomies.first.nodes.first
173
+ assert_equal 'Africa', node.name
174
+ assert_equal 'Algeria', node.nodes.first.name
175
+ assert_equal ['Algiers', "Ghardaïa", 'El Oued', 'Timimoun', 'Annaba'],
176
+ node.nodes.first.nodes.map(&:name)
177
+ end
178
+
179
+ def test_with_guarded_recursion
180
+ p = PersonWithGuardedMother.from_xml(fixture(:person_with_guarded_mothers))
181
+ assert_equal 'Ben "Benji" Franklin', p.name
182
+ assert_equal 'Abiah \'Abby\' Folger', p.mother.name
183
+ assert_equal 'Madeup Mother < the third >', p.mother.mother.name
184
+ assert_equal nil, p.mother.mother.mother
185
+ end
186
+
187
+ def test_recursive_with_default_initialization
188
+ p = PersonWithMotherOrMissing.from_xml(fixture(:person_with_mothers))
189
+ assert_equal 'Unknown', p.mother.mother.mother.name
190
+ assert_equal Person, p.mother.mother.mother.class
191
+ end
192
+
193
+ def test_defining_empty_on_object_doesnt_cause_it_to_be_seen_as_absent
194
+ # absent means defaulting, failing required
195
+
196
+ holder = CartHolder.from_xml(%{
197
+ <cartholder>
198
+ <cart>
199
+ <id>111111</id>
200
+ </cart>
201
+ </cartholder>
202
+ })
203
+
204
+ assert_equal "111111", holder.cart.id
205
+ end
206
+ end
@@ -0,0 +1,94 @@
1
+ require_relative './../test_helper'
2
+
3
+ class TestXMLRequired < ActiveSupport::TestCase
4
+ def setup
5
+ @full_book = <<BOOK
6
+ <book ISBN="1234">
7
+ <title>This &amp; that</title>
8
+ <contributor_array>
9
+ <contributor role="Author">
10
+ <name>Johnny</name>
11
+ </contributor>
12
+ </contributor_array>
13
+ <contributor_hash>
14
+ <contributor role="Author" name="Johnny" />
15
+ </contributor_hash>
16
+ </book>
17
+ BOOK
18
+
19
+ @book_missing_attr = <<BOOK
20
+ <book>
21
+ <title>This &amp; that</title>
22
+ <contributor_array>
23
+ <contributor role="Author">
24
+ <name>Johnny</name>
25
+ </contributor>
26
+ </contributor_array>
27
+ <contributor_hash>
28
+ <contributor role="Author" name="Johnny" />
29
+ </contributor_hash>
30
+ </book>
31
+ BOOK
32
+
33
+ @book_missing_text = <<BOOK
34
+ <book ISBN="1234">
35
+ <contributor_array>
36
+ <contributor role="Author">
37
+ <name>Johnny</name>
38
+ </contributor>
39
+ </contributor_array>
40
+ <contributor_hash>
41
+ <contributor role="Author" name="Johnny" />
42
+ </contributor_hash>
43
+ </book>
44
+ BOOK
45
+
46
+ @book_missing_array = <<BOOK
47
+ <book ISBN="1234">
48
+ <title>This &amp; that</title>
49
+ <contributor_hash>
50
+ <contributor role="Author" name="Johnny" />
51
+ </contributor_hash>
52
+ </book>
53
+ BOOK
54
+
55
+ @book_missing_hash = <<BOOK
56
+ <book ISBN="1234">
57
+ <title>This &amp; that</title>
58
+ <contributor_array>
59
+ <contributor role="Author">
60
+ <name>Johnny</name>
61
+ </contributor>
62
+ </contributor_array>
63
+ </book>
64
+ BOOK
65
+ end
66
+
67
+ def test_required_passes_on_prescence
68
+ BookWithRequired.from_xml(@full_book)
69
+ end
70
+
71
+ def test_required_throws_on_attr_absence
72
+ assert_raise ROXML::RequiredElementMissing do
73
+ BookWithRequired.from_xml(@book_missing_attr)
74
+ end
75
+ end
76
+
77
+ def test_required_throws_on_text_absence
78
+ assert_raise ROXML::RequiredElementMissing do
79
+ BookWithRequired.from_xml(@book_missing_text)
80
+ end
81
+ end
82
+
83
+ def test_required_throws_on_array_absence
84
+ assert_raise ROXML::RequiredElementMissing do
85
+ BookWithRequired.from_xml(@book_missing_array)
86
+ end
87
+ end
88
+
89
+ def test_required_throws_on_hash_absence
90
+ assert_raise ROXML::RequiredElementMissing do
91
+ BookWithRequired.from_xml(@book_missing_hash)
92
+ end
93
+ end
94
+ end