roxml 2.3.2 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +145 -0
- data/Manifest.txt +81 -0
- data/Rakefile +85 -95
- data/TODO +42 -0
- data/html/index.html +278 -0
- data/html/style.css +79 -0
- data/lib/roxml.rb +112 -49
- data/lib/roxml/extensions/active_support.rb +37 -12
- data/lib/roxml/extensions/array/conversions.rb +12 -2
- data/lib/roxml/extensions/deprecation.rb +4 -4
- data/lib/roxml/options.rb +39 -30
- data/lib/roxml/xml.rb +119 -54
- data/lib/roxml/xml/rexml.rb +2 -2
- data/roxml.gemspec +40 -101
- data/tasks/test.rake +42 -0
- data/test/bugs/rexml_bugs.rb +15 -0
- data/test/fixtures/book_with_octal_pages.xml +4 -0
- data/test/mocks/mocks.rb +0 -4
- data/test/test_helper.rb +1 -1
- data/test/unit/array_test.rb +16 -0
- data/test/unit/freeze_test.rb +71 -0
- data/test/unit/inheritance_test.rb +26 -3
- data/test/unit/options_test.rb +20 -19
- data/test/unit/overriden_output_test.rb +33 -0
- data/test/unit/roxml_test.rb +7 -0
- data/test/unit/xml_bool_test.rb +16 -11
- data/test/unit/xml_convention_test.rb +150 -0
- data/test/unit/xml_hash_test.rb +41 -0
- data/test/unit/xml_name_test.rb +29 -6
- data/test/unit/xml_object_test.rb +30 -0
- data/vendor/override_rake_task/README +30 -0
- data/vendor/override_rake_task/init.rb +1 -0
- data/vendor/override_rake_task/install.rb +46 -0
- data/vendor/override_rake_task/lib/override_rake_task.rb +16 -0
- metadata +77 -34
data/html/index.html
ADDED
@@ -0,0 +1,278 @@
|
|
1
|
+
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
|
5
|
+
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
|
11
|
+
<link rel="stylesheet" type="text/css" media="screen" href="style.css">
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
<title>ROXML - Ruby Object to XML Mapping Library</title>
|
19
|
+
</head>
|
20
|
+
|
21
|
+
|
22
|
+
<body>
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
<div id="header" class="contentBlock">
|
28
|
+
<h1>ROXML - Ruby Object to XML Mapping Library</h1>
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
</div>
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
<div class="quickLinks"> <a href="#download">Download</a>
|
39
|
+
<a href="#quickstart">Quick Start Guide</a> <a href="http://rubyforge.org/projects/roxml">RubyForge Project
|
40
|
+
Site</a>
|
41
|
+
</div>
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
<div class="contentBlock">
|
47
|
+
<p>ROXML is a <a href="http://ruby-lang.org/" title="Main Ruby Website">Ruby</a> library
|
48
|
+
designed to make it easier for Ruby developers to work with <a href="http://en.wikipedia.org/wiki/XML">XML</a>.
|
49
|
+
Using
|
50
|
+
simple annotations, it enables Ruby classes to be custom-mapped to XML. ROXML
|
51
|
+
takes care of the marshalling and unmarshalling of mapped attributes so
|
52
|
+
that developers can focus on building first-class Ruby classes. As a
|
53
|
+
result, ROXML <span class="highlight">simplifies the
|
54
|
+
development of <a href="http://en.wikipedia.org/wiki/Representational_State_Transfer">RESTful</a> applications,
|
55
|
+
<a href="http://en.wikipedia.org/wiki/Web_Services">Web
|
56
|
+
Services</a>, and <a href="http://en.wikipedia.org/wiki/XML-RPC">XML-RPC</a>.</span></p>
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
<p>ROXML leverages the <a href="http://www.germane-software.com/software/rexml/">REXML</a>
|
62
|
+
Ruby XML processor. ROXML powers the <a href="http://rubyforge.org/projects/uddi4r">uddi4r -
|
63
|
+
UDDI for Ruby</a> project.</p>
|
64
|
+
|
65
|
+
|
66
|
+
|
67
|
+
|
68
|
+
<h2>ROXML 1.0 Features</h2>
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
ROXML 1.0 provides the following capabilities:<br>
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
<ul>
|
79
|
+
|
80
|
+
|
81
|
+
|
82
|
+
|
83
|
+
<li>Read Ruby objects from XML (marshal)</li>
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
<li>Write Ruby objects to XML (unmarshal)</li>
|
89
|
+
|
90
|
+
|
91
|
+
|
92
|
+
|
93
|
+
<li>Smart defaults for XML mapping</li>
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
|
98
|
+
<li>Annotation-style methods (also known as macros) for XML
|
99
|
+
mapping</li>
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
<li>One-to-one (composition) Ruby to XML</li>
|
105
|
+
|
106
|
+
|
107
|
+
|
108
|
+
|
109
|
+
<li>One-to-many (aggregation) Ruby with array to XML</li>
|
110
|
+
|
111
|
+
|
112
|
+
|
113
|
+
|
114
|
+
<li>UTF-8 support for multi-lingual documents</li>
|
115
|
+
|
116
|
+
|
117
|
+
|
118
|
+
|
119
|
+
</ul>
|
120
|
+
|
121
|
+
|
122
|
+
<h2>ROXML 1.1 Features</h2>
|
123
|
+
|
124
|
+
|
125
|
+
|
126
|
+
|
127
|
+
ROXML 1.1 adds the following new capabilities:
|
128
|
+
<ul>
|
129
|
+
<li>Handling text elements with attributes</li>
|
130
|
+
<li>Support for mapped Ruby objects in modules</li>
|
131
|
+
</ul>
|
132
|
+
<p><a name="download"></a> </p>
|
133
|
+
|
134
|
+
|
135
|
+
|
136
|
+
|
137
|
+
<h2>Download</h2>
|
138
|
+
|
139
|
+
|
140
|
+
|
141
|
+
|
142
|
+
<p>You can download source and library releases on the <a href="http://rubyforge.org/frs/?group_id=305">RubyForge
|
143
|
+
downloads</a> page. Alternatively, you can checkout the latest
|
144
|
+
source from <a href="http://rubyforge.org/scm/?group_id=305">CVS</a>.</p>
|
145
|
+
|
146
|
+
|
147
|
+
|
148
|
+
|
149
|
+
<p> <a name="quickstart"></a> </p>
|
150
|
+
|
151
|
+
|
152
|
+
|
153
|
+
|
154
|
+
<h2>Quick Start Guide</h2>
|
155
|
+
|
156
|
+
|
157
|
+
|
158
|
+
|
159
|
+
<p>
|
160
|
+
This is a short usage example. See <a href="../doc/classes/ROXML/ROXML_Class.html">ROXML::ROXML_Class</a> and
|
161
|
+
packaged test cases for more information.</p>
|
162
|
+
|
163
|
+
|
164
|
+
|
165
|
+
|
166
|
+
|
167
|
+
<p>
|
168
|
+
Consider an XML document representing a Library containing a number of
|
169
|
+
Books. You can map this structure to Ruby classes that provide addition
|
170
|
+
useful behavior. With ROXML, you can
|
171
|
+
annotate the Ruby classes as follows:</p>
|
172
|
+
|
173
|
+
|
174
|
+
|
175
|
+
|
176
|
+
<pre><code><span style="font-weight: bold;">class</span> Book<br> <span style="font-weight: bold;">include</span> ROXML<br><br> xml_attribute :isbn, "ISBN"<br> xml_text :title<br> xml_text :description, <span style="font-weight: bold;">nil</span>, ROXML::TAG_CDATA<br> xml_text :author<span style="font-weight: bold;"></span><br><span style="font-weight: bold;">end</span><br><br><span style="font-weight: bold;">class</span> Library<br> <span style="font-weight: bold;">include</span> ROXML<br><br> xml_text :name, "NAME", ROXML::TAG_CDATA<br> xml_object :books, Book, ROXML::TAG_ARRAY, "books"<br><span style="font-weight: bold;">end</span><br></code><br></pre>
|
177
|
+
|
178
|
+
|
179
|
+
|
180
|
+
|
181
|
+
<p>To create a library and put a number of books in it, we could
|
182
|
+
run the following code:</p>
|
183
|
+
|
184
|
+
|
185
|
+
<pre> book = Book.<span style="font-weight: bold;">new</span>()<br> book.isbn = "0201710897"<br> book.title = "The PickAxe"<br> book.description = "Best Ruby book out there!"<br> book.author = "David Thomas, Andrew Hunt, Dave Thomas"<br><br> lib = Library.<span style="font-weight: bold;">new</span>()<br> lib.name = "Favorite Books"<br> lib.books << book</pre>
|
186
|
+
|
187
|
+
|
188
|
+
<p>To save this information to an XML file:</p>
|
189
|
+
|
190
|
+
|
191
|
+
|
192
|
+
|
193
|
+
<pre><code>File.open("library.xml", "w") <span style="font-weight: bold;">do</span> |f|<br> lib.to_xml.write(f, 0)<br><span style="font-weight: bold;">end</span><br></code><br></pre>
|
194
|
+
|
195
|
+
|
196
|
+
|
197
|
+
|
198
|
+
<p>This would put the following XML into <em>library.xml</em>:</p>
|
199
|
+
|
200
|
+
|
201
|
+
|
202
|
+
|
203
|
+
<pre><code><library><br> <NAME><![CDATA[Favorite Books]]></NAME><br> <books><br> <book ISBN='0201710897'><br> <title>The PickAxe</title><br> <description><![CDATA[Best Ruby book out there!]]></description><br> <author>David Thomas, Andrew Hunt, Dave Thomas</author><br> </book><br> </books><br></library></code><br></pre>
|
204
|
+
|
205
|
+
|
206
|
+
|
207
|
+
|
208
|
+
<p>To later populate the library object from the XML file:</p>
|
209
|
+
|
210
|
+
|
211
|
+
|
212
|
+
|
213
|
+
<pre> <code>lib = Library.parse(File.read("library.xml"))<br></code><br></pre>
|
214
|
+
|
215
|
+
|
216
|
+
|
217
|
+
|
218
|
+
<p>To do a one-to-one mapping between XML objects, such as book
|
219
|
+
and publisher,
|
220
|
+
you would use the <em>xml_object</em> annotation. For
|
221
|
+
example:</p>
|
222
|
+
|
223
|
+
|
224
|
+
|
225
|
+
|
226
|
+
<pre><code><book isbn="0974514055"><br> <title>Programming Ruby - 2nd Edition</title><br> <description>Second edition of the great book</description><br> <publisher><br> <name>Pragmatic Bookshelf</name><br> </publisher><br></book><br></code><br></pre>
|
227
|
+
|
228
|
+
|
229
|
+
|
230
|
+
|
231
|
+
<p>Can be mapped using the following code:</p>
|
232
|
+
|
233
|
+
|
234
|
+
|
235
|
+
|
236
|
+
<pre><code><span style="font-weight: bold;">class</span> BookWithPublisher<br> <span style="font-weight: bold;">include</span> ROXML<br><br> xml_name :book<br> xml_object :publisher, Publisher<br><span style="font-weight: bold;">end</span><br></code><br></pre>
|
237
|
+
|
238
|
+
|
239
|
+
|
240
|
+
|
241
|
+
<p>Note: In the above example, <em>xml_name</em>
|
242
|
+
annotation tells ROXML to set the element
|
243
|
+
name to "book" for mapping to XML. The default is XML element name is
|
244
|
+
the class name in lowercase; "bookwithpublisher"
|
245
|
+
in this case.</p>
|
246
|
+
|
247
|
+
|
248
|
+
|
249
|
+
|
250
|
+
</div>
|
251
|
+
|
252
|
+
|
253
|
+
|
254
|
+
|
255
|
+
<div id="footer" class="contentBlock">
|
256
|
+
<p>ROXML is licensed under the MIT License. See
|
257
|
+
bundled MIT-LICENSE.txt for details. ROXML was created by <a href="http://rubyforge.org/users/andersengstrom">Anders
|
258
|
+
Engstrom</a> and is being developed and maintained by <a href="http://rubyforge.org/users/zakmandhro">Zak Mandhro</a>. For
|
259
|
+
project tracking, downloads, and other information, visit
|
260
|
+
the <a href="http://rubyforge.org/projects/roxml">ROXML
|
261
|
+
RubyForge Project</a> page.</p>
|
262
|
+
|
263
|
+
|
264
|
+
|
265
|
+
|
266
|
+
</div>
|
267
|
+
|
268
|
+
|
269
|
+
|
270
|
+
|
271
|
+
<div id="cvs_id">$Id: INDEX,v 1.6 2006/06/28 03:52:38
|
272
|
+
zakmandhro Exp $</div>
|
273
|
+
|
274
|
+
|
275
|
+
|
276
|
+
|
277
|
+
</body>
|
278
|
+
</html>
|
data/html/style.css
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
/* Generated by CaScadeS, a stylesheet editor for Mozilla Composer */
|
2
|
+
|
3
|
+
body { margin: 0px;
|
4
|
+
font-size: 13px;
|
5
|
+
font-family: arial,sans-serif;
|
6
|
+
background: #DDD;
|
7
|
+
}
|
8
|
+
|
9
|
+
.highlight {
|
10
|
+
background: #FFD;
|
11
|
+
}
|
12
|
+
.contentBlock {
|
13
|
+
margin-left: 50px;
|
14
|
+
margin-top: 2px;
|
15
|
+
background: #FEFEFE;
|
16
|
+
width: 700px; padding: 10px;
|
17
|
+
border-left: solid 2px #FFF;
|
18
|
+
border-right: solid 2px #AAA;
|
19
|
+
}
|
20
|
+
|
21
|
+
.quickLinks { text-align: center;
|
22
|
+
margin-left: 50px;
|
23
|
+
padding-left: 10px; padding-right: 10px;
|
24
|
+
padding-top: 3px; padding-bottom: 3px;
|
25
|
+
margin-top: 2px;
|
26
|
+
border-left: solid 2px #FFF;
|
27
|
+
border-right: solid 2px #AAA;
|
28
|
+
background: white;
|
29
|
+
width: 700px;
|
30
|
+
}
|
31
|
+
|
32
|
+
.quickLinks a { padding-left: 5px;
|
33
|
+
padding-right: 5px;
|
34
|
+
text-decoration: underline;
|
35
|
+
}
|
36
|
+
|
37
|
+
.quickLinks a:hover {
|
38
|
+
background: #EEE;
|
39
|
+
}
|
40
|
+
|
41
|
+
a { text-decoration: none;
|
42
|
+
}
|
43
|
+
|
44
|
+
a:hover { }
|
45
|
+
|
46
|
+
code { font-size: 12px;
|
47
|
+
font-family: monospace;
|
48
|
+
}
|
49
|
+
|
50
|
+
pre { border: 1px solid rgb(221, 221, 221);
|
51
|
+
padding: 1em;
|
52
|
+
overflow: auto;
|
53
|
+
width: 70%;
|
54
|
+
background-color: rgb(238, 238, 238);
|
55
|
+
color: rgb(0, 0, 0);
|
56
|
+
}
|
57
|
+
|
58
|
+
h1 {
|
59
|
+
text-align: center;
|
60
|
+
font-size: 150%;
|
61
|
+
}
|
62
|
+
|
63
|
+
h2 { border-bottom: 1px solid rgb(221, 221, 221);
|
64
|
+
font-size: 150%;
|
65
|
+
}
|
66
|
+
|
67
|
+
h3 { font-size: 120%;
|
68
|
+
}
|
69
|
+
|
70
|
+
#footer { border-top: 1px dashed gray;
|
71
|
+
background-color: rgb(240, 240, 240);
|
72
|
+
font-size: x-small;
|
73
|
+
}
|
74
|
+
|
75
|
+
div#cvs_id { text-align: right;
|
76
|
+
color: rgb(204, 204, 204);
|
77
|
+
font-size: 75%;
|
78
|
+
}
|
79
|
+
|
data/lib/roxml.rb
CHANGED
@@ -3,14 +3,16 @@
|
|
3
3
|
end
|
4
4
|
|
5
5
|
module ROXML # :nodoc:
|
6
|
+
VERSION = '2.4.0'
|
7
|
+
|
6
8
|
def self.included(base) # :nodoc:
|
7
|
-
base.extend ClassMethods::Accessors
|
8
|
-
|
9
|
-
|
9
|
+
base.extend ClassMethods::Accessors,
|
10
|
+
ClassMethods::Declarations,
|
11
|
+
ClassMethods::Operations
|
10
12
|
base.class_eval do
|
11
|
-
include InstanceMethods::Accessors
|
12
|
-
|
13
|
-
|
13
|
+
include InstanceMethods::Accessors,
|
14
|
+
InstanceMethods::Construction,
|
15
|
+
InstanceMethods::Conversions
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
@@ -24,8 +26,9 @@ module ROXML # :nodoc:
|
|
24
26
|
|
25
27
|
# Provides access to ROXML::ClassMethods::Accessors::tag_refs directly from an instance of a ROXML class
|
26
28
|
def tag_refs
|
27
|
-
self.class.
|
29
|
+
self.class.tag_refs_without_deprecation
|
28
30
|
end
|
31
|
+
deprecate :tag_refs => :roxml_attrs
|
29
32
|
end
|
30
33
|
|
31
34
|
module Construction
|
@@ -70,8 +73,9 @@ module ROXML # :nodoc:
|
|
70
73
|
# Returns a LibXML::XML::Node or a REXML::Element representing this object
|
71
74
|
def to_xml(name = nil)
|
72
75
|
returning XML::Node.new_element(name || tag_name) do |root|
|
73
|
-
|
74
|
-
|
76
|
+
self.class.roxml_attrs.each do |attr|
|
77
|
+
ref = attr.to_ref(self)
|
78
|
+
v = ref.to_xml
|
75
79
|
unless v.nil?
|
76
80
|
ref.update_xml(root, v)
|
77
81
|
end
|
@@ -89,11 +93,6 @@ module ROXML # :nodoc:
|
|
89
93
|
#
|
90
94
|
module ClassMethods # :nodoc:
|
91
95
|
module Declarations
|
92
|
-
# A helper which enables us to detect when the xml_name has been explicitly set
|
93
|
-
def xml_name? #:nodoc:
|
94
|
-
@xml_name
|
95
|
-
end
|
96
|
-
|
97
96
|
# Sets the name of the XML element that represents this class. Use this
|
98
97
|
# to override the default lowercase class name.
|
99
98
|
#
|
@@ -105,8 +104,49 @@ module ROXML # :nodoc:
|
|
105
104
|
# Without the xml_name annotation, the XML mapped tag would have been "bookwithpublisher".
|
106
105
|
#
|
107
106
|
def xml_name(name)
|
108
|
-
@
|
109
|
-
|
107
|
+
@roxml_tag_name = name
|
108
|
+
end
|
109
|
+
|
110
|
+
# Most xml documents have a consistent naming convention, for example, the node and
|
111
|
+
# and attribute names might appear in CamelCase. xml_convention enables you to adapt
|
112
|
+
# the roxml default names for this object to suit this convention. For example,
|
113
|
+
# if I had a document like so:
|
114
|
+
#
|
115
|
+
# <XmlDoc>
|
116
|
+
# <MyPreciousData />
|
117
|
+
# <MoreToSee InAttrs="" />
|
118
|
+
# </XmlDoc>
|
119
|
+
#
|
120
|
+
# Then I could access it's contents by defining the following class:
|
121
|
+
#
|
122
|
+
# class XmlDoc
|
123
|
+
# include ROXML
|
124
|
+
# xml_convention :camelcase
|
125
|
+
# xml_reader :my_precious_data
|
126
|
+
# xml_reader :in_attrs, :in => 'MoreToSee'
|
127
|
+
# end
|
128
|
+
#
|
129
|
+
# You may supply a block or any #to_proc-able object as the argument,
|
130
|
+
# and it will be called against the default node and attribute names before searching
|
131
|
+
# the document. Here are some example declaration:
|
132
|
+
#
|
133
|
+
# xml_convention :upcase
|
134
|
+
# xml_convention &:camelcase
|
135
|
+
# xml_convention {|val| val.gsub('_', '').downcase }
|
136
|
+
#
|
137
|
+
# See ActiveSupport::CoreExtensions::String::Inflections for more prepackaged formats
|
138
|
+
#
|
139
|
+
# Note that the xml_convention is also applied to the default root-level tag_name,
|
140
|
+
# but in this case an underscored version of the name is applied, for convenience.
|
141
|
+
def xml_convention(to_proc_able = nil, &block)
|
142
|
+
raise ArgumentError, "conventions are already set" if @roxml_naming_convention
|
143
|
+
raise ArgumentError, "only one conventions can be set" if to_proc_able.respond_to?(:to_proc) && block_given?
|
144
|
+
@roxml_naming_convention = to_proc_able.try(:to_proc)
|
145
|
+
@roxml_naming_convention = block if block_given?
|
146
|
+
end
|
147
|
+
|
148
|
+
def roxml_naming_convention # :nodoc:
|
149
|
+
(@roxml_naming_convention || superclass.try(:roxml_naming_convention)).freeze
|
110
150
|
end
|
111
151
|
|
112
152
|
# Declares an accesser to a certain xml element, whether an attribute, a node,
|
@@ -120,13 +160,13 @@ module ROXML # :nodoc:
|
|
120
160
|
# This name will be the default node or attribute name searched for,
|
121
161
|
# if no other is declared. For example,
|
122
162
|
#
|
123
|
-
# xml_reader :bob
|
124
|
-
# xml_accessor :pony, :attr
|
163
|
+
# xml_reader :bob
|
164
|
+
# xml_accessor :pony, :attr
|
125
165
|
#
|
126
166
|
# are equivalent to:
|
127
167
|
#
|
128
|
-
# xml_reader :bob
|
129
|
-
# xml_accessor :pony, :attr
|
168
|
+
# xml_reader :bob, :from => 'bob'
|
169
|
+
# xml_accessor :pony, :attr => 'pony'
|
130
170
|
#
|
131
171
|
# === Boolean attributes
|
132
172
|
# If the name ends in a ?, ROXML will attempt to coerce the value to true or false,
|
@@ -373,35 +413,35 @@ module ROXML # :nodoc:
|
|
373
413
|
# [:in] An optional name of a wrapping tag for this XML accessor
|
374
414
|
# [:else] Default value for attribute, if missing
|
375
415
|
# [:required] If true, throws RequiredElementMissing when the element isn't present
|
416
|
+
# [:frozen] If true, all results are frozen (using #freeze) at parse-time.
|
376
417
|
#
|
377
418
|
def xml(sym, writable = false, type_and_or_opts = :text, opts = nil, &block)
|
378
419
|
opts = Opts.new(sym, *[type_and_or_opts, opts].compact, &block)
|
379
420
|
|
380
|
-
|
381
|
-
when :attr then XMLAttributeRef
|
382
|
-
when :content then XMLTextRef
|
383
|
-
when :text then XMLTextRef
|
384
|
-
when :hash then XMLHashRef
|
385
|
-
when Symbol then raise ArgumentError, "Invalid type argument #{opts.type}"
|
386
|
-
else XMLObjectRef
|
387
|
-
end.new(opts)
|
388
|
-
|
389
|
-
add_accessor(ref, writable)
|
421
|
+
add_accessor(opts, writable)
|
390
422
|
end
|
391
423
|
|
392
424
|
# Declares a read-only xml reference. See xml for details.
|
425
|
+
#
|
426
|
+
# Note that while xml_reader does not create a setter for this attribute,
|
427
|
+
# its value can be modified indirectly via methods. For more complete
|
428
|
+
# protection, consider the :frozen option.
|
393
429
|
def xml_reader(sym, type_and_or_opts = :text, opts = nil, &block)
|
394
430
|
xml sym, false, type_and_or_opts, opts, &block
|
395
431
|
end
|
396
432
|
|
397
433
|
# Declares a writable xml reference. See xml for details.
|
434
|
+
#
|
435
|
+
# Note that while xml_accessor does create a setter for this attribute,
|
436
|
+
# you can use the :frozen option to prevent its value from being
|
437
|
+
# modified indirectly via methods.
|
398
438
|
def xml_accessor(sym, type_and_or_opts = :text, opts = nil, &block)
|
399
439
|
xml sym, true, type_and_or_opts, opts, &block
|
400
440
|
end
|
401
441
|
|
402
442
|
# This method is deprecated, please use xml_initialize instead
|
403
443
|
def xml_construct(*args)
|
404
|
-
present_tags =
|
444
|
+
present_tags = tag_refs_without_deprecation.map(&:accessor)
|
405
445
|
missing_tags = args - present_tags
|
406
446
|
unless missing_tags.empty?
|
407
447
|
raise ArgumentError, "All construction tags must be declared first using xml, " +
|
@@ -413,25 +453,23 @@ module ROXML # :nodoc:
|
|
413
453
|
deprecate :xml_construct => :xml_initialize
|
414
454
|
|
415
455
|
private
|
416
|
-
def add_accessor(
|
417
|
-
if
|
418
|
-
raise "Accessor #{
|
456
|
+
def add_accessor(attr, writable)
|
457
|
+
if roxml_attrs.map(&:accessor).include? attr.accessor
|
458
|
+
raise "Accessor #{attr.accessor} is already defined as XML accessor in class #{self.name}"
|
419
459
|
end
|
420
|
-
|
460
|
+
@roxml_attrs << attr
|
421
461
|
|
422
|
-
define_method(
|
423
|
-
result = instance_variable_get("@#{
|
462
|
+
define_method(attr.accessor) do
|
463
|
+
result = instance_variable_get("@#{attr.variable_name}")
|
424
464
|
if result.nil?
|
425
|
-
result =
|
426
|
-
instance_variable_set("@#{
|
465
|
+
result = attr.default
|
466
|
+
instance_variable_set("@#{attr.variable_name}", result)
|
427
467
|
end
|
428
468
|
result
|
429
469
|
end
|
430
470
|
|
431
|
-
if writable && !instance_methods.include?("#{
|
432
|
-
|
433
|
-
instance_variable_set("@#{ref.accessor}", v)
|
434
|
-
end
|
471
|
+
if writable && !instance_methods.include?("#{attr.accessor}=")
|
472
|
+
attr_writer(attr.accessor)
|
435
473
|
end
|
436
474
|
end
|
437
475
|
end
|
@@ -442,18 +480,43 @@ module ROXML # :nodoc:
|
|
442
480
|
end
|
443
481
|
deprecate :xml_construction_args
|
444
482
|
|
483
|
+
# A helper which enables us to detect when the xml_name has been explicitly set
|
484
|
+
def xml_name? #:nodoc:
|
485
|
+
@roxml_tag_name
|
486
|
+
end
|
487
|
+
deprecate :xml_name?
|
488
|
+
|
445
489
|
# Returns the tag name (also known as xml_name) of the class.
|
446
490
|
# If no tag name is set with xml_name method, returns default class name
|
447
491
|
# in lowercase.
|
492
|
+
#
|
493
|
+
# If xml_convention is set, it is called with an *underscored* version of
|
494
|
+
# the class name. This is because active support's inflector generally expects
|
495
|
+
# an underscored version, and several operations (e.g. camelcase(:lower), dasherize)
|
496
|
+
# do not work without one.
|
448
497
|
def tag_name
|
449
|
-
|
498
|
+
return roxml_tag_name if roxml_tag_name
|
499
|
+
|
500
|
+
if tag_name = name.split('::').last
|
501
|
+
roxml_naming_convention ? roxml_naming_convention.call(tag_name.underscore) : tag_name.downcase
|
502
|
+
end
|
503
|
+
end
|
504
|
+
|
505
|
+
def roxml_tag_name # :nodoc:
|
506
|
+
@roxml_tag_name || superclass.try(:roxml_tag_name)
|
450
507
|
end
|
451
508
|
|
452
509
|
# Returns array of internal reference objects, such as attributes
|
453
510
|
# and composed XML objects
|
511
|
+
def roxml_attrs
|
512
|
+
@roxml_attrs ||= []
|
513
|
+
(@roxml_attrs + (superclass.try(:roxml_attrs) || [])).freeze
|
514
|
+
end
|
515
|
+
|
454
516
|
def tag_refs
|
455
|
-
|
517
|
+
roxml_attrs.map {|a| a.to_ref(nil) }
|
456
518
|
end
|
519
|
+
deprecate :tag_refs => :roxml_attrs
|
457
520
|
end
|
458
521
|
|
459
522
|
module Operations
|
@@ -479,13 +542,13 @@ module ROXML # :nodoc:
|
|
479
542
|
|
480
543
|
unless xml_construction_args_without_deprecation.empty?
|
481
544
|
args = xml_construction_args_without_deprecation.map do |arg|
|
482
|
-
|
483
|
-
end.map {|
|
545
|
+
roxml_attrs.find {|attr| attr.accessor == arg }
|
546
|
+
end.map {|attr| attr.to_ref(self).value_in(xml) }
|
484
547
|
new(*args)
|
485
548
|
else
|
486
549
|
returning allocate do |inst|
|
487
|
-
|
488
|
-
|
550
|
+
roxml_attrs.each do |attr|
|
551
|
+
inst.instance_variable_set("@#{attr.variable_name}", attr.to_ref(inst).value_in(xml))
|
489
552
|
end
|
490
553
|
inst.send(:xml_initialize, *initialization_args)
|
491
554
|
end
|