representable 0.0.1.alpha1

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 (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