roxml 2.3.2 → 2.4.0
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.
- 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
|