ROXML 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. data/.gitignore +6 -0
  2. data/.gitmodules +3 -0
  3. data/History.txt +299 -0
  4. data/MIT-LICENSE +18 -0
  5. data/README.rdoc +161 -0
  6. data/Rakefile +95 -0
  7. data/TODO +39 -0
  8. data/VERSION +1 -0
  9. data/config/website.yml +2 -0
  10. data/examples/amazon.rb +35 -0
  11. data/examples/current_weather.rb +27 -0
  12. data/examples/dashed_elements.rb +20 -0
  13. data/examples/library.rb +40 -0
  14. data/examples/posts.rb +27 -0
  15. data/examples/rails.rb +70 -0
  16. data/examples/twitter.rb +37 -0
  17. data/examples/xml/active_record.xml +70 -0
  18. data/examples/xml/amazon.xml +133 -0
  19. data/examples/xml/current_weather.xml +89 -0
  20. data/examples/xml/dashed_elements.xml +52 -0
  21. data/examples/xml/posts.xml +23 -0
  22. data/examples/xml/twitter.xml +422 -0
  23. data/lib/roxml.rb +547 -0
  24. data/lib/roxml/definition.rb +236 -0
  25. data/lib/roxml/hash_definition.rb +25 -0
  26. data/lib/roxml/xml.rb +43 -0
  27. data/lib/roxml/xml/parsers/libxml.rb +91 -0
  28. data/lib/roxml/xml/parsers/nokogiri.rb +77 -0
  29. data/lib/roxml/xml/references.rb +297 -0
  30. data/roxml.gemspec +201 -0
  31. data/spec/definition_spec.rb +486 -0
  32. data/spec/examples/active_record_spec.rb +40 -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_spec.rb +372 -0
  40. data/spec/shared_specs.rb +15 -0
  41. data/spec/spec.opts +1 -0
  42. data/spec/spec_helper.rb +14 -0
  43. data/spec/support/libxml.rb +3 -0
  44. data/spec/support/nokogiri.rb +3 -0
  45. data/spec/xml/attributes_spec.rb +36 -0
  46. data/spec/xml/namespace_spec.rb +240 -0
  47. data/spec/xml/namespaces_spec.rb +32 -0
  48. data/spec/xml/parser_spec.rb +26 -0
  49. data/tasks/rdoc.rake +13 -0
  50. data/tasks/rspec.rake +25 -0
  51. data/tasks/test.rake +35 -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/support/fixtures.rb +11 -0
  85. data/test/test_helper.rb +34 -0
  86. data/test/unit/definition_test.rb +235 -0
  87. data/test/unit/deprecations_test.rb +24 -0
  88. data/test/unit/to_xml_test.rb +81 -0
  89. data/test/unit/xml_attribute_test.rb +39 -0
  90. data/test/unit/xml_block_test.rb +81 -0
  91. data/test/unit/xml_bool_test.rb +122 -0
  92. data/test/unit/xml_convention_test.rb +150 -0
  93. data/test/unit/xml_hash_test.rb +115 -0
  94. data/test/unit/xml_initialize_test.rb +49 -0
  95. data/test/unit/xml_name_test.rb +141 -0
  96. data/test/unit/xml_namespace_test.rb +31 -0
  97. data/test/unit/xml_object_test.rb +207 -0
  98. data/test/unit/xml_required_test.rb +94 -0
  99. data/test/unit/xml_text_test.rb +71 -0
  100. data/website/index.html +98 -0
  101. metadata +254 -0
@@ -0,0 +1,31 @@
1
+ require 'test/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,207 @@
1
+ require 'test/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 if ROXML::XML::Error.respond_to?(: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
@@ -0,0 +1,94 @@
1
+ require 'test/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
@@ -0,0 +1,71 @@
1
+ require 'test/test_helper'
2
+
3
+ class TestXMLText < ActiveSupport::TestCase
4
+ # Test a simple mapping with no composition
5
+ def test_valid_simple
6
+ book = Book.from_xml(fixture(:book_valid))
7
+ assert_equal("The PickAxe", book.title)
8
+ assert_equal("David Thomas, Andrew Hunt & Dave Thomas", book.author)
9
+
10
+ assert_equal xml_fixture(:book_valid).to_s.gsub("\n", ''), book.to_xml.to_s.gsub("\n", '')
11
+ end
12
+
13
+ def test_without_needed_from
14
+ assert !Library.from_xml(fixture(:library_uppercase)).name
15
+ end
16
+
17
+ def test_with_needed_from
18
+ assert_equal "Ruby library", Library.from_xml(fixture(:library)).name
19
+ assert_equal "Ruby library", UppercaseLibrary.from_xml(fixture(:library_uppercase)).name
20
+ end
21
+
22
+ def test_as_array
23
+ assert_equal ["David Thomas","Andrew Hunt","Dave Thomas"].sort,
24
+ BookWithAuthors.from_xml(fixture(:book_with_authors)).authors.sort
25
+ end
26
+
27
+ def test_empty_array_result_returned_properly
28
+ empty_array = Class.new do
29
+ include ROXML
30
+
31
+ xml_reader :missing_array, :as => [], :from => 'missing'
32
+ end
33
+
34
+ obj = empty_array.from_xml('<empty_array></empty_array>')
35
+ assert_equal [], obj.missing_array
36
+ end
37
+
38
+ def test_text_modification
39
+ person = Person.from_xml(fixture(:person))
40
+ assert_equal("Ben Franklin", person.name)
41
+ person.name = "Fred"
42
+ xml=person.to_xml.to_s
43
+ assert(/Fred/=~xml)
44
+ end
45
+
46
+ def test_default_initialization
47
+ person = PersonWithMotherOrMissing.from_xml(fixture(:nameless_ageless_youth))
48
+ assert_equal "Anonymous", person.name
49
+ end
50
+
51
+ def test_default_initialization_of_content
52
+ person = Person.from_xml(fixture(:nameless_ageless_youth))
53
+ assert_equal "Unknown", person.name
54
+ end
55
+
56
+ def test_recursive_with_default_initialization
57
+ p = PersonWithMotherOrMissing.from_xml(fixture(:person_with_mothers))
58
+ assert_equal 'Unknown', p.mother.mother.mother.name
59
+ end
60
+
61
+ def test_get_with_block
62
+ p = Book.from_xml(fixture(:book_valid))
63
+ assert_equal 357, p.pages
64
+ end
65
+
66
+ def test_no_name_clashes
67
+ n = NodeWithNameConflicts.from_xml(fixture(:node_with_name_conflicts))
68
+ assert_equal "Just junk... really", n.content
69
+ assert_equal "Cartwheel", n.name
70
+ end
71
+ end
@@ -0,0 +1,98 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
+
4
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
5
+ <head>
6
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
7
+
8
+ <title>Empact/roxml @ GitHub</title>
9
+
10
+ <style type="text/css">
11
+ body {
12
+ margin-top: 1.0em;
13
+ background-color: #baafdb;
14
+ font-family: "helvetica";
15
+ color: #000000;
16
+ }
17
+ #container {
18
+ margin: 0 auto;
19
+ width: 700px;
20
+ }
21
+ h1 { font-size: 3.8em; color: #455024; margin-bottom: 3px; }
22
+ h1 .small { font-size: 0.4em; }
23
+ h1 a { text-decoration: none }
24
+ h2 { font-size: 1.5em; color: #455024; }
25
+ h3 { text-align: center; color: #455024; }
26
+ a { color: #455024; }
27
+ .description { font-size: 1.2em; margin-bottom: 30px; margin-top: 30px; font-style: italic;}
28
+ .download { float: right; }
29
+ pre { background: #000; color: #fff; padding: 15px;}
30
+ hr { border: 0; width: 80%; border-bottom: 1px solid #aaa}
31
+ .footer { text-align:center; padding-top:30px; font-style: italic; }
32
+ </style>
33
+
34
+ </head>
35
+
36
+ <body>
37
+ <a href="http://github.com/Empact/roxml"><img style="position: absolute; top: 0; right: 0; border: 0;" src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" /></a>
38
+
39
+ <div id="container">
40
+
41
+ <div class="download">
42
+ <a href="http://github.com/Empact/roxml/zipball/master">
43
+ <img border="0" width="90" src="http://github.com/images/modules/download/zip.png"></a>
44
+ <a href="http://github.com/Empact/roxml/tarball/master">
45
+ <img border="0" width="90" src="http://github.com/images/modules/download/tar.png"></a>
46
+ </div>
47
+
48
+ <h1><a href="http://github.com/Empact/roxml">roxml</a>
49
+ <span class="small">by <a href="http://github.com/Empact">Empact</a></small></h1>
50
+
51
+ <div class="description">
52
+ ROXML is a module for binding Ruby classes to XML. It supports custom mapping and bidirectional marshaling between Ruby and XML using annotation-style class methods. ROXML supports the Nokogiri and LibXML XML processors.
53
+ </div>
54
+
55
+ <p>We may not want to work with XML, but we don't always get to pick just how our data comes. If you do need to do serious work with XML, ROXML is here to make it nice.
56
+
57
+ Use xpath-based xml declarations to map an XML response into an extensible object, use that object, and then ouput it back to xml as it came.
58
+
59
+ Aside from the basics, ROXML has a lot of little goodies to make these definitions minimal and readable, which are worth digging in for. See the 'xml_convention' method for easily referencing xml which uses other naming conventions. See the handling of '?' in accessor names, the application of blocks and such for other manipulations.
60
+
61
+ And finally, if you use this library, feel free to push code back my way. I'll be looking forward to it.</p><h2>Dependencies</h2>
62
+ <p>activesupport >= 2.1.0</p>
63
+ <h2>Install</h2>
64
+ <p>'gem install roxml' will install the latest stable <a href="http://rubyforge.org/frs/?group_id=305">rubyforge version</a></p>
65
+ <h2>License</h2>
66
+ <p>MIT License</p>
67
+ <h2>Authors</h2>
68
+ <p>Ben Woosley (ben.woosley@gmail.com)
69
+ <br/>Anders Engström (anders.engstrom@gnejs.net)
70
+ <br/>James Thompson (james@plainprograms.com)
71
+ <br/>James Healy (jimmy@deefa.com)
72
+ <br/>Zak Mandhro (mandhro@yahoo.com)
73
+ <br/>Russ Olsen (russell.olsen@gmail.com)</p>
74
+ <h2>Contact</h2>
75
+ <p>Ben Woosley (Ben.Woosley@gmail.com)
76
+ <br/> </p>
77
+
78
+
79
+ <h2>Download</h2>
80
+ <p>
81
+ You can download this project in either
82
+ <a href="http://github.com/Empact/roxml/zipball/master">zip</a> or
83
+ <a href="http://github.com/Empact/roxml/tarball/master">tar</a> formats.
84
+ </p>
85
+ <p>You can also clone the project with <a href="http://git-scm.com">Git</a>
86
+ by running:
87
+ <pre>$ git clone git://github.com/Empact/roxml</pre>
88
+ </p>
89
+
90
+ <div class="footer">
91
+ get the source code on GitHub : <a href="http://github.com/Empact/roxml">Empact/roxml</a>
92
+ </div>
93
+
94
+ </div>
95
+
96
+
97
+ </body>
98
+ </html>