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.
@@ -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>&nbsp;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>&nbsp;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&nbsp;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&nbsp;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&nbsp;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 &lt;&lt; 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>&lt;library&gt;<br> &lt;NAME&gt;&lt;![CDATA[Favorite Books]]&gt;&lt;/NAME&gt;<br> &lt;books&gt;<br> &lt;book ISBN='0201710897'&gt;<br> &lt;title&gt;The PickAxe&lt;/title&gt;<br> &lt;description&gt;&lt;![CDATA[Best Ruby book out there!]]&gt;&lt;/description&gt;<br> &lt;author&gt;David Thomas, Andrew Hunt, Dave Thomas&lt;/author&gt;<br> &lt;/book&gt;<br> &lt;/books&gt;<br>&lt;/library&gt;</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>&lt;book isbn="0974514055"&gt;<br> &lt;title&gt;Programming Ruby - 2nd Edition&lt;/title&gt;<br> &lt;description&gt;Second edition of the great book&lt;/description&gt;<br> &lt;publisher&gt;<br> &lt;name&gt;Pragmatic Bookshelf&lt;/name&gt;<br> &lt;/publisher&gt;<br>&lt;/book&gt;<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&nbsp;is licensed under the MIT License. See
257
+ bundled MIT-LICENSE.txt for details.&nbsp;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>.&nbsp;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>
@@ -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
+
@@ -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
- base.extend ClassMethods::Declarations
9
- base.extend ClassMethods::Operations
9
+ base.extend ClassMethods::Accessors,
10
+ ClassMethods::Declarations,
11
+ ClassMethods::Operations
10
12
  base.class_eval do
11
- include InstanceMethods::Accessors
12
- include InstanceMethods::Construction
13
- include InstanceMethods::Conversions
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.tag_refs
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
- tag_refs.each do |ref|
74
- v = __send__(ref.accessor)
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
- @xml_name = true
109
- @tag_name = name
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, :from => 'bob'
124
- # xml_accessor :pony, :attr => 'pony'
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
- ref = case opts.type
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 = tag_refs.map(&:accessor)
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(ref, writable)
417
- if tag_refs.map(&:accessor).include? ref.accessor
418
- raise "Accessor #{ref.accessor} is already defined as XML accessor in class #{self.name}"
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
- tag_refs << ref
460
+ @roxml_attrs << attr
421
461
 
422
- define_method(ref.accessor) do
423
- result = instance_variable_get("@#{ref.variable_name}")
462
+ define_method(attr.accessor) do
463
+ result = instance_variable_get("@#{attr.variable_name}")
424
464
  if result.nil?
425
- result = ref.default
426
- instance_variable_set("@#{ref.variable_name}", result)
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?("#{ref.accessor}=")
432
- define_method("#{ref.accessor}=") do |v|
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
- @tag_name ||= name.split('::').last.downcase
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
- @xml_refs ||= superclass.respond_to?(:tag_refs) ? superclass.tag_refs.clone : []
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
- tag_refs.find {|ref| ref.accessor == arg }
483
- end.map {|ref| ref.value(xml) }
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
- tag_refs.each do |ref|
488
- ref.populate(xml, inst)
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