Empact-roxml 2.3.1 → 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/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>&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>
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
- 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,10 @@ 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
- if v = __send__(ref.accessor)
76
+ self.class.roxml_attrs.each do |attr|
77
+ ref = attr.to_ref(self)
78
+ v = ref.to_xml
79
+ unless v.nil?
75
80
  ref.update_xml(root, v)
76
81
  end
77
82
  end
@@ -88,11 +93,6 @@ module ROXML # :nodoc:
88
93
  #
89
94
  module ClassMethods # :nodoc:
90
95
  module Declarations
91
- # A helper which enables us to detect when the xml_name has been explicitly set
92
- def xml_name? #:nodoc:
93
- @xml_name
94
- end
95
-
96
96
  # Sets the name of the XML element that represents this class. Use this
97
97
  # to override the default lowercase class name.
98
98
  #
@@ -104,8 +104,49 @@ module ROXML # :nodoc:
104
104
  # Without the xml_name annotation, the XML mapped tag would have been "bookwithpublisher".
105
105
  #
106
106
  def xml_name(name)
107
- @xml_name = true
108
- @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
109
150
  end
110
151
 
111
152
  # Declares an accesser to a certain xml element, whether an attribute, a node,
@@ -119,13 +160,13 @@ module ROXML # :nodoc:
119
160
  # This name will be the default node or attribute name searched for,
120
161
  # if no other is declared. For example,
121
162
  #
122
- # xml_reader :bob, :from => 'bob'
123
- # xml_accessor :pony, :attr => 'pony'
163
+ # xml_reader :bob
164
+ # xml_accessor :pony, :attr
124
165
  #
125
166
  # are equivalent to:
126
167
  #
127
- # xml_reader :bob
128
- # xml_accessor :pony, :attr
168
+ # xml_reader :bob, :from => 'bob'
169
+ # xml_accessor :pony, :attr => 'pony'
129
170
  #
130
171
  # === Boolean attributes
131
172
  # If the name ends in a ?, ROXML will attempt to coerce the value to true or false,
@@ -372,35 +413,35 @@ module ROXML # :nodoc:
372
413
  # [:in] An optional name of a wrapping tag for this XML accessor
373
414
  # [:else] Default value for attribute, if missing
374
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.
375
417
  #
376
418
  def xml(sym, writable = false, type_and_or_opts = :text, opts = nil, &block)
377
419
  opts = Opts.new(sym, *[type_and_or_opts, opts].compact, &block)
378
420
 
379
- ref = case opts.type
380
- when :attr then XMLAttributeRef
381
- when :content then XMLTextRef
382
- when :text then XMLTextRef
383
- when :hash then XMLHashRef
384
- when Symbol then raise ArgumentError, "Invalid type argument #{opts.type}"
385
- else XMLObjectRef
386
- end.new(opts)
387
-
388
- add_accessor(ref, writable)
421
+ add_accessor(opts, writable)
389
422
  end
390
423
 
391
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.
392
429
  def xml_reader(sym, type_and_or_opts = :text, opts = nil, &block)
393
430
  xml sym, false, type_and_or_opts, opts, &block
394
431
  end
395
432
 
396
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.
397
438
  def xml_accessor(sym, type_and_or_opts = :text, opts = nil, &block)
398
439
  xml sym, true, type_and_or_opts, opts, &block
399
440
  end
400
441
 
401
442
  # This method is deprecated, please use xml_initialize instead
402
443
  def xml_construct(*args)
403
- present_tags = tag_refs.map(&:accessor)
444
+ present_tags = tag_refs_without_deprecation.map(&:accessor)
404
445
  missing_tags = args - present_tags
405
446
  unless missing_tags.empty?
406
447
  raise ArgumentError, "All construction tags must be declared first using xml, " +
@@ -412,25 +453,23 @@ module ROXML # :nodoc:
412
453
  deprecate :xml_construct => :xml_initialize
413
454
 
414
455
  private
415
- def add_accessor(ref, writable)
416
- if tag_refs.map(&:accessor).include? ref.accessor
417
- 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}"
418
459
  end
419
- tag_refs << ref
460
+ @roxml_attrs << attr
420
461
 
421
- define_method(ref.accessor) do
422
- result = instance_variable_get("@#{ref.variable_name}")
462
+ define_method(attr.accessor) do
463
+ result = instance_variable_get("@#{attr.variable_name}")
423
464
  if result.nil?
424
- result = ref.default
425
- instance_variable_set("@#{ref.variable_name}", result)
465
+ result = attr.default
466
+ instance_variable_set("@#{attr.variable_name}", result)
426
467
  end
427
468
  result
428
469
  end
429
470
 
430
- if writable && !instance_methods.include?("#{ref.accessor}=")
431
- define_method("#{ref.accessor}=") do |v|
432
- instance_variable_set("@#{ref.accessor}", v)
433
- end
471
+ if writable && !instance_methods.include?("#{attr.accessor}=")
472
+ attr_writer(attr.accessor)
434
473
  end
435
474
  end
436
475
  end
@@ -441,18 +480,43 @@ module ROXML # :nodoc:
441
480
  end
442
481
  deprecate :xml_construction_args
443
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
+
444
489
  # Returns the tag name (also known as xml_name) of the class.
445
490
  # If no tag name is set with xml_name method, returns default class name
446
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.
447
497
  def tag_name
448
- @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)
449
507
  end
450
508
 
451
509
  # Returns array of internal reference objects, such as attributes
452
510
  # and composed XML objects
511
+ def roxml_attrs
512
+ @roxml_attrs ||= []
513
+ (@roxml_attrs + (superclass.try(:roxml_attrs) || [])).freeze
514
+ end
515
+
453
516
  def tag_refs
454
- @xml_refs ||= superclass.respond_to?(:tag_refs) ? superclass.tag_refs.clone : []
517
+ roxml_attrs.map {|a| a.to_ref(nil) }
455
518
  end
519
+ deprecate :tag_refs => :roxml_attrs
456
520
  end
457
521
 
458
522
  module Operations
@@ -478,13 +542,13 @@ module ROXML # :nodoc:
478
542
 
479
543
  unless xml_construction_args_without_deprecation.empty?
480
544
  args = xml_construction_args_without_deprecation.map do |arg|
481
- tag_refs.find {|ref| ref.accessor == arg }
482
- 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) }
483
547
  new(*args)
484
548
  else
485
549
  returning allocate do |inst|
486
- tag_refs.each do |ref|
487
- 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))
488
552
  end
489
553
  inst.send(:xml_initialize, *initialization_args)
490
554
  end