doxo-roxml 2.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. data/History.txt +279 -0
  2. data/MIT-LICENSE +18 -0
  3. data/Manifest.txt +103 -0
  4. data/README.rdoc +158 -0
  5. data/Rakefile +96 -0
  6. data/TODO +62 -0
  7. data/config/website.yml +2 -0
  8. data/examples/active_record.rb +70 -0
  9. data/examples/amazon.rb +33 -0
  10. data/examples/current_weather.rb +27 -0
  11. data/examples/dashed_elements.rb +20 -0
  12. data/examples/library.rb +40 -0
  13. data/examples/posts.rb +27 -0
  14. data/examples/twitter.rb +37 -0
  15. data/examples/xml/active_record.xml +70 -0
  16. data/examples/xml/amazon.xml +133 -0
  17. data/examples/xml/current_weather.xml +89 -0
  18. data/examples/xml/dashed_elements.xml +52 -0
  19. data/examples/xml/posts.xml +23 -0
  20. data/examples/xml/twitter.xml +422 -0
  21. data/lib/roxml.rb +511 -0
  22. data/lib/roxml/definition.rb +234 -0
  23. data/lib/roxml/extensions.rb +6 -0
  24. data/lib/roxml/extensions/array.rb +13 -0
  25. data/lib/roxml/extensions/array/conversions.rb +12 -0
  26. data/lib/roxml/extensions/deprecation.rb +33 -0
  27. data/lib/roxml/extensions/string.rb +6 -0
  28. data/lib/roxml/extensions/string/conversions.rb +5 -0
  29. data/lib/roxml/extensions/string/iterators.rb +12 -0
  30. data/lib/roxml/hash_definition.rb +25 -0
  31. data/lib/roxml/xml.rb +40 -0
  32. data/lib/roxml/xml/parsers/libxml.rb +86 -0
  33. data/lib/roxml/xml/parsers/rexml.rb +84 -0
  34. data/lib/roxml/xml/references.rb +299 -0
  35. data/roxml.gemspec +50 -0
  36. data/spec/definition_spec.rb +490 -0
  37. data/spec/examples/active_record_spec.rb +40 -0
  38. data/spec/examples/amazon_spec.rb +53 -0
  39. data/spec/examples/current_weather_spec.rb +37 -0
  40. data/spec/examples/dashed_elements_spec.rb +20 -0
  41. data/spec/examples/library_spec.rb +46 -0
  42. data/spec/examples/post_spec.rb +24 -0
  43. data/spec/examples/twitter_spec.rb +32 -0
  44. data/spec/roxml_spec.rb +372 -0
  45. data/spec/shared_specs.rb +15 -0
  46. data/spec/spec.opts +1 -0
  47. data/spec/spec_helper.rb +33 -0
  48. data/spec/xml/parser_spec.rb +47 -0
  49. data/tasks/rspec.rake +21 -0
  50. data/tasks/test.rake +42 -0
  51. data/test/bugs/rexml_bugs.rb +15 -0
  52. data/test/fixtures/book_malformed.xml +5 -0
  53. data/test/fixtures/book_pair.xml +8 -0
  54. data/test/fixtures/book_text_with_attribute.xml +5 -0
  55. data/test/fixtures/book_valid.xml +5 -0
  56. data/test/fixtures/book_with_authors.xml +7 -0
  57. data/test/fixtures/book_with_contributions.xml +9 -0
  58. data/test/fixtures/book_with_contributors.xml +7 -0
  59. data/test/fixtures/book_with_contributors_attrs.xml +7 -0
  60. data/test/fixtures/book_with_default_namespace.xml +9 -0
  61. data/test/fixtures/book_with_depth.xml +6 -0
  62. data/test/fixtures/book_with_octal_pages.xml +4 -0
  63. data/test/fixtures/book_with_publisher.xml +7 -0
  64. data/test/fixtures/book_with_wrapped_attr.xml +3 -0
  65. data/test/fixtures/dictionary_of_attr_name_clashes.xml +8 -0
  66. data/test/fixtures/dictionary_of_attrs.xml +6 -0
  67. data/test/fixtures/dictionary_of_guarded_names.xml +6 -0
  68. data/test/fixtures/dictionary_of_mixeds.xml +4 -0
  69. data/test/fixtures/dictionary_of_name_clashes.xml +10 -0
  70. data/test/fixtures/dictionary_of_names.xml +4 -0
  71. data/test/fixtures/dictionary_of_texts.xml +10 -0
  72. data/test/fixtures/library.xml +30 -0
  73. data/test/fixtures/library_uppercase.xml +30 -0
  74. data/test/fixtures/muffins.xml +3 -0
  75. data/test/fixtures/nameless_ageless_youth.xml +2 -0
  76. data/test/fixtures/node_with_attr_name_conflicts.xml +1 -0
  77. data/test/fixtures/node_with_name_conflicts.xml +4 -0
  78. data/test/fixtures/numerology.xml +4 -0
  79. data/test/fixtures/person.xml +1 -0
  80. data/test/fixtures/person_with_guarded_mothers.xml +13 -0
  81. data/test/fixtures/person_with_mothers.xml +10 -0
  82. data/test/mocks/dictionaries.rb +57 -0
  83. data/test/mocks/mocks.rb +279 -0
  84. data/test/test_helper.rb +45 -0
  85. data/test/unit/definition_test.rb +235 -0
  86. data/test/unit/deprecations_test.rb +24 -0
  87. data/test/unit/to_xml_test.rb +81 -0
  88. data/test/unit/xml_attribute_test.rb +39 -0
  89. data/test/unit/xml_block_test.rb +81 -0
  90. data/test/unit/xml_bool_test.rb +122 -0
  91. data/test/unit/xml_convention_test.rb +150 -0
  92. data/test/unit/xml_hash_test.rb +115 -0
  93. data/test/unit/xml_initialize_test.rb +49 -0
  94. data/test/unit/xml_name_test.rb +141 -0
  95. data/test/unit/xml_namespace_test.rb +76 -0
  96. data/test/unit/xml_object_test.rb +207 -0
  97. data/test/unit/xml_required_test.rb +93 -0
  98. data/test/unit/xml_text_test.rb +71 -0
  99. data/vendor/override_rake_task/README +30 -0
  100. data/vendor/override_rake_task/init.rb +1 -0
  101. data/vendor/override_rake_task/install.rb +46 -0
  102. data/vendor/override_rake_task/lib/override_rake_task.rb +16 -0
  103. data/website/index.html +98 -0
  104. metadata +234 -0
@@ -0,0 +1,115 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_helper')
2
+
3
+ class BookWithContributorHash
4
+ include ROXML
5
+
6
+ xml_reader :contributors, :as => {:key => '@role',
7
+ :value => 'name'}
8
+ end
9
+
10
+ class TestXMLHash < ActiveSupport::TestCase
11
+ def setup
12
+ @contents = {'quaquaversally' => 'adjective: (of a geological formation) sloping downward from the center in all directions.',
13
+ 'tergiversate' => 'To use evasions or ambiguities; equivocate.'}
14
+ end
15
+
16
+ def test_hash_preserves_data
17
+ b = BookWithContributorHash.from_xml(%{
18
+ <book isbn="0974514055">
19
+ <contributor role="author"><name>David Thomas</name></contributor>
20
+ <contributor role="supporting author"><name>Andrew Hunt</name></contributor>
21
+ <contributor role="supporting author"><name>Chad Fowler</name></contributor>
22
+ </book>
23
+ })
24
+ assert_equal({'author' => 'David Thomas', 'supporting author' => ['Andrew Hunt', 'Chad Fowler']},
25
+ b.contributors)
26
+ end
27
+
28
+ def test_hash_with_object_key_fails
29
+ assert_raise ArgumentError do
30
+ Class.new do
31
+ include ROXML
32
+
33
+ xml_reader :object_key_to_text, :as => {:key => BookWithContributorHash,
34
+ :value => 'text_node'}
35
+ end
36
+ end
37
+ end
38
+
39
+ def test_hash_with_object_value_fails
40
+ assert_raise ArgumentError do
41
+ Class.new do
42
+ include ROXML
43
+
44
+ xml_reader :key_to_object_value, :as => {:key => '@text_node',
45
+ :value => BookWithContributorHash}
46
+ end
47
+ end
48
+ end
49
+
50
+ def test_attrs_hash
51
+ dict = DictionaryOfAttrs.from_xml(fixture(:dictionary_of_attrs))
52
+ assert_equal Hash, dict.definitions.class
53
+ assert_equal @contents, dict.definitions
54
+ end
55
+
56
+ def test_text_hash
57
+ dict = DictionaryOfTexts.from_xml(fixture(:dictionary_of_texts))
58
+ assert_equal Hash, dict.definitions.class
59
+ assert_equal @contents, dict.definitions
60
+ end
61
+
62
+ def test_mixed_content_hash
63
+ dict = DictionaryOfMixeds.from_xml(fixture(:dictionary_of_mixeds))
64
+ assert_equal Hash, dict.definitions.class
65
+ assert_equal @contents, dict.definitions
66
+ end
67
+
68
+ def test_name_hash
69
+ dict = DictionaryOfNames.from_xml(fixture(:dictionary_of_names))
70
+ assert_equal Hash, dict.definitions.class
71
+ assert_equal @contents, dict.definitions
72
+ end
73
+
74
+ def test_guarded_name_hash
75
+ dict = DictionaryOfGuardedNames.from_xml(fixture(:dictionary_of_guarded_names))
76
+ assert_equal Hash, dict.definitions.class
77
+ assert_equal @contents, dict.definitions
78
+ end
79
+
80
+ def test_text_name_clashes
81
+ dict = DictionaryOfNameClashes.from_xml(fixture(:dictionary_of_name_clashes))
82
+ assert_equal Hash, dict.definitions.class
83
+ assert_equal @contents, dict.definitions
84
+ end
85
+
86
+ def test_attr_name_clashes
87
+ dict = DictionaryOfAttrNameClashes.from_xml(fixture(:dictionary_of_attr_name_clashes))
88
+ assert_equal Hash, dict.definitions.class
89
+ assert_equal @contents, dict.definitions
90
+ end
91
+
92
+ def test_it_should_gracefully_handle_empty_hash
93
+ dict = Class.new do
94
+ include ROXML
95
+
96
+ xml_reader :missing_hash, :as => {:key => :name, :value => :content}, :in => 'EmptyDictionary'
97
+ end
98
+
99
+ assert_equal({}, dict.from_xml(%{
100
+ <dict>
101
+ <EmptyDictionary>
102
+ </EmptyDictionary>
103
+ </dict>
104
+ }).missing_hash)
105
+ end
106
+
107
+ def test_as_hash_of_as_type_not_deprecated
108
+ assert_not_deprecated do
109
+ opts = ROXML::Definition.new(:name, :as => {:key => :name, :value => {:from => 'value', :as => OctalInteger}})
110
+ assert opts.hash?
111
+ assert_equal OctalInteger, opts.hash.value.type
112
+ assert_equal 'value', opts.hash.value.name
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,49 @@
1
+ require File.join(File.dirname(__FILE__), '..', '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 File.join(File.dirname(__FILE__), '..', '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::Parser.parse(%(
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,76 @@
1
+ require File.join(File.dirname(__FILE__), '..', '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_that_rexml_follows_nameless_default_namespace
21
+ require 'rexml/document'
22
+ xml = REXML::Document.new(
23
+ '<container xmlns="http://fakenamespace.org"><node>Yeah, content</node></container>')
24
+
25
+ assert_equal "Yeah, content", xml.root.get_elements('node').first.text
26
+ end
27
+
28
+ def test_default_namespace_on_root_node_should_be_found
29
+ require 'libxml'
30
+ xml = LibXML::XML::Parser.string(
31
+ '<container xmlns="http://defaultnamespace.org"><node>Yeah, content</node><node><subnode>Another</subnode></node></container>').parse
32
+
33
+ assert_equal nil, xml.find_first('node')
34
+ assert_equal "Yeah, content", xml.find_first('ns:node', 'ns:http://defaultnamespace.org').content
35
+ assert_equal nil, xml.find_first('ns:node/subnode', 'ns:http://defaultnamespace.org')
36
+ assert_equal "Another", xml.find_first('ns:node/ns:subnode', 'ns:http://defaultnamespace.org').content
37
+ rescue LoadError
38
+ end
39
+ end
40
+
41
+ class NamespaceyObject
42
+ include ROXML
43
+ xml_namespace :aws
44
+
45
+ xml_reader :default_namespace
46
+ xml_reader :different_namespace, :from => 'different:namespace'
47
+ xml_reader :no_namespace, :from => 'no_namespace'
48
+ end
49
+
50
+ class TestXMLNamespaceDeclarations < ActiveSupport::TestCase
51
+ def setup
52
+ @instance = NamespaceyObject.from_xml(%{
53
+ <aws:book xmlns:aws="http://www.aws.com/aws" xmlns:different="http://www.aws.com/different">
54
+ <aws:default_namespace>default_value</aws:default_namespace>
55
+ <different:namespace>different_value</different:namespace>
56
+ <no_namespace>no_value</no_namespace>
57
+ </aws:book>
58
+ })
59
+ end
60
+
61
+ def test_namespace_is_accessible
62
+ assert_equal "aws", @instance.class.roxml_namespace
63
+ end
64
+
65
+ def test_namespace_declaration_should_be_followed_on_attributes
66
+ assert_equal "default_value", @instance.default_namespace
67
+ end
68
+
69
+ def test_namespace_declaration_can_be_overriden_for_different_namespace_elements
70
+ assert_equal "different_value", @instance.different_namespace
71
+ end
72
+
73
+ def test_namespace_declaration_can_be_overriden_for_no_namespace_elements
74
+ assert_equal "no_value", @instance.no_namespace
75
+ end
76
+ end
@@ -0,0 +1,207 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'test_helper')
2
+
3
+ class EmptyCart
4
+ include ROXML
5
+
6
+ xml_reader :id
7
+
8
+ def empty?
9
+ true
10
+ end
11
+ end
12
+
13
+ class CartHolder
14
+ include ROXML
15
+
16
+ xml_reader :cart, :as => EmptyCart, :required => true
17
+ end
18
+
19
+ class TestXMLObject < ActiveSupport::TestCase
20
+ # Test book with text and attribute
21
+ def test_book_author_text_attribute
22
+ book = BookWithAuthorTextAttribute.from_xml(fixture(:book_text_with_attribute))
23
+ assert_equal("primary",book.author.role)
24
+ assert_equal("David Thomas",book.author.text)
25
+ end
26
+
27
+ # Test XML object containing list of other XML objects (one-to-many)
28
+ # In this case, book with contibutions
29
+ def test_one_to_many_with_container
30
+ expected_authors = ["David Thomas","Andrew Hunt","Chad Fowler"]
31
+ book = BookWithContributions.from_xml(fixture(:book_with_contributions))
32
+ assert_equal("Programming Ruby - 2nd Edition", book.title)
33
+ book.contributions.each do |contributor|
34
+ assert expected_authors.include?(contributor.name)
35
+ end
36
+ end
37
+
38
+ # Test XML object containing 1-n other XML objects without container (one-to-many)
39
+ # In this case, book with contibutions
40
+ def test_one_to_many_without_container
41
+ expected_contributors = ["David Thomas","Andrew Hunt","Chad Fowler"]
42
+ book = BookWithContributors.from_xml(fixture(:book_with_contributors))
43
+ assert_equal("Programming Ruby - 2nd Edition", book.title)
44
+ book.contributors.each do |contributor|
45
+ assert(expected_contributors.include?(contributor.name))
46
+ end
47
+ end
48
+
49
+ # when building objects that contain arrays, the second object seems to
50
+ # inherit data from the first
51
+ #
52
+ def test_one_to_many_without_container_sequence
53
+ contrib = WriteableContributor.new
54
+ contrib.name = "David Thomas"
55
+
56
+ book_one = WriteableBookWithContributors.new
57
+ book_one.isbn = "9781843549161"
58
+ book_one.title = "Anathem"
59
+ book_one.description = "A new title from Neal Stephenson"
60
+ book_one.contributors = [contrib]
61
+
62
+ # this book should be completely empty
63
+ book_two = WriteableBookWithContributors.new
64
+
65
+ assert_equal(nil, book_two.isbn)
66
+ assert_equal(nil, book_two.title)
67
+ assert_equal(nil, book_two.description)
68
+ assert_equal(nil, book_two.contributors)
69
+ end
70
+
71
+ # Test XML object containing one other XML object (one-to-one)
72
+ # In this case, book with publisher
73
+ def test_one_to_one
74
+ book = BookWithPublisher.from_xml(fixture(:book_with_publisher))
75
+ assert_equal("Programming Ruby - 2nd Edition", book.title)
76
+ assert_equal("Pragmatic Bookshelf", book.publisher.name)
77
+ end
78
+
79
+ # Test XML object containing type of self (self-reference)
80
+ def test_self_reference
81
+ book = BookPair.from_xml(fixture(:book_pair))
82
+ assert_equal("Programming Ruby - 2nd Edition", book.title)
83
+ assert_equal("Agile Web Development with Rails", book.book.title)
84
+ end
85
+
86
+ # Test three-level composition (one-to-many-to-many)
87
+ def test_one_to_many_to_many
88
+ expected_contributors = ["David Thomas","Andrew Hunt","Chad Fowler", "David Heinemeier Hansson"]
89
+ expected_books = ["Programming Ruby - 2nd Edition", "Agile Web Development with Rails"]
90
+ library = Library.from_xml(fixture(:library))
91
+ assert_equal("Ruby library", library.name)
92
+ assert !library.books.empty?
93
+ library.books.each do |book|
94
+ assert expected_books.include?(book.title)
95
+ book.contributions.each do |contributor|
96
+ assert(expected_contributors.include?(contributor.name))
97
+ end
98
+ end
99
+ end
100
+
101
+ def test_without_needed_from
102
+ assert_equal [], UppercaseLibrary.from_xml(fixture(:library)).books
103
+ assert_equal [], Library.from_xml(fixture(:library_uppercase)).books
104
+ end
105
+
106
+ def test_with_needed_from
107
+ assert Library.from_xml(fixture(:library)).books
108
+ assert UppercaseLibrary.from_xml(fixture(:library_uppercase)).books
109
+ end
110
+
111
+ def test_with_recursion
112
+ p = PersonWithMother.from_xml(fixture(:person_with_mothers))
113
+ assert_equal 'Ben Franklin', p.name
114
+ assert_equal 'Abiah Folger', p.mother.name
115
+ assert_equal 'Madeup Mother', p.mother.mother.name
116
+ assert_equal nil, p.mother.mother.mother
117
+ end
118
+
119
+ class Node
120
+ include ROXML
121
+
122
+ xml_reader :name, :from => 'node_name'
123
+ xml_reader :nodes, :as => [Node]
124
+ end
125
+
126
+ class Taxonomy
127
+ include ROXML
128
+
129
+ xml_reader :name, :from => 'taxonomy_name'
130
+ xml_reader :nodes, :as => [Node]
131
+ end
132
+
133
+ class Taxonomies
134
+ include ROXML
135
+ xml_reader :taxonomies, :as => [Taxonomy]
136
+ end
137
+
138
+ def test_more_recursion
139
+ # quiet the error handler
140
+ ROXML::XML::Error.reset_handler
141
+ taxonomies = Taxonomies.from_xml(<<HERE)
142
+ <?xml version="1.0" encoding="UTF-8" ?>
143
+ <!DOCTYPE taxonomies SYSTEM "taxonomy.dtd">
144
+ <taxonomies>
145
+ <taxonomy>
146
+ <taxonomy_name>World</taxonomy_name>
147
+ <node node_id="2" content_object_id="82534" object_type_id="2">
148
+ <node_name lang_iso="eng">Africa</node_name>
149
+ <node node_id="331" content_object_id="11" object_type_id="4">
150
+ <node_name lang_iso="eng">Algeria</node_name>
151
+ <node node_id="7271" content_object_id="117629" object_type_id="8">
152
+ <node_name lang_iso="eng">Algiers</node_name>
153
+ </node>
154
+ <node node_id="7272" content_object_id="117630" object_type_id="8">
155
+ <node_name lang_iso="eng">Gharda&#239;a</node_name>
156
+ </node>
157
+ <node node_id="7871" content_object_id="1000713999" object_type_id="8">
158
+ <node_name lang_iso="eng">El Oued</node_name>
159
+ </node>
160
+ <node node_id="7872" content_object_id="1000714008" object_type_id="8">
161
+ <node_name lang_iso="eng">Timimoun</node_name>
162
+ </node>
163
+ <node node_id="8903" content_object_id="1000565565" object_type_id="8">
164
+ <node_name lang_iso="eng">Annaba</node_name>
165
+ </node>
166
+ </node>
167
+ </node>
168
+ </taxonomy>
169
+ </taxonomies>
170
+ HERE
171
+ assert_equal 1, taxonomies.taxonomies.size
172
+ assert_equal 'World', taxonomies.taxonomies.first.name
173
+ node = taxonomies.taxonomies.first.nodes.first
174
+ assert_equal 'Africa', node.name
175
+ assert_equal 'Algeria', node.nodes.first.name
176
+ assert_equal ['Algiers', "Gharda\303\257a", 'El Oued', 'Timimoun', 'Annaba'],
177
+ node.nodes.first.nodes.map(&:name)
178
+ end
179
+
180
+ def test_with_guarded_recursion
181
+ p = PersonWithGuardedMother.from_xml(fixture(:person_with_guarded_mothers))
182
+ assert_equal 'Ben "Benji" Franklin', p.name
183
+ assert_equal 'Abiah \'Abby\' Folger', p.mother.name
184
+ assert_equal 'Madeup Mother < the third >', p.mother.mother.name
185
+ assert_equal nil, p.mother.mother.mother
186
+ end
187
+
188
+ def test_recursive_with_default_initialization
189
+ p = PersonWithMotherOrMissing.from_xml(fixture(:person_with_mothers))
190
+ assert_equal 'Unknown', p.mother.mother.mother.name
191
+ assert_equal Person, p.mother.mother.mother.class
192
+ end
193
+
194
+ def test_defining_empty_on_object_doesnt_cause_it_to_be_seen_as_absent
195
+ # absent means defaulting, failing required
196
+
197
+ holder = CartHolder.from_xml(%{
198
+ <cartholder>
199
+ <cart>
200
+ <id>111111</id>
201
+ </cart>
202
+ </cartholder>
203
+ })
204
+
205
+ assert_equal "111111", holder.cart.id
206
+ end
207
+ end