Empact-roxml 2.4.3 → 2.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/History.txt +65 -0
  2. data/Manifest.txt +11 -6
  3. data/README.rdoc +48 -26
  4. data/Rakefile +5 -2
  5. data/TODO +30 -31
  6. data/examples/active_record.rb +70 -0
  7. data/examples/amazon.rb +1 -1
  8. data/examples/current_weather.rb +1 -1
  9. data/examples/library.rb +40 -0
  10. data/examples/posts.rb +8 -8
  11. data/examples/twitter.rb +2 -2
  12. data/examples/xml/active_record.xml +70 -0
  13. data/lib/roxml.rb +174 -174
  14. data/lib/roxml/definition.rb +165 -89
  15. data/lib/roxml/extensions/deprecation.rb +5 -0
  16. data/lib/roxml/extensions/string/conversions.rb +2 -3
  17. data/lib/roxml/hash_definition.rb +26 -25
  18. data/lib/roxml/xml.rb +15 -6
  19. data/lib/roxml/xml/parsers/libxml.rb +14 -6
  20. data/lib/roxml/xml/parsers/rexml.rb +16 -5
  21. data/lib/roxml/xml/references.rb +14 -17
  22. data/roxml.gemspec +14 -5
  23. data/spec/definition_spec.rb +563 -0
  24. data/spec/examples/active_record_spec.rb +40 -0
  25. data/spec/examples/library_spec.rb +41 -0
  26. data/spec/roxml_spec.rb +372 -0
  27. data/spec/shared_specs.rb +15 -0
  28. data/spec/spec_helper.rb +21 -4
  29. data/spec/string_spec.rb +15 -0
  30. data/spec/xml/parser_spec.rb +47 -0
  31. data/test/fixtures/book_valid.xml +1 -1
  32. data/test/fixtures/person_with_guarded_mothers.xml +3 -3
  33. data/test/mocks/mocks.rb +57 -45
  34. data/test/test_helper.rb +1 -1
  35. data/test/unit/definition_test.rb +161 -12
  36. data/test/unit/deprecations_test.rb +97 -0
  37. data/test/unit/to_xml_test.rb +30 -1
  38. data/test/unit/xml_bool_test.rb +15 -3
  39. data/test/unit/xml_construct_test.rb +6 -6
  40. data/test/unit/xml_hash_test.rb +18 -0
  41. data/test/unit/xml_initialize_test.rb +6 -3
  42. data/test/unit/xml_namespace_test.rb +1 -0
  43. data/test/unit/xml_object_test.rb +66 -5
  44. data/test/unit/xml_text_test.rb +3 -0
  45. metadata +57 -24
  46. data/test/unit/array_test.rb +0 -16
  47. data/test/unit/freeze_test.rb +0 -71
  48. data/test/unit/inheritance_test.rb +0 -63
  49. data/test/unit/overriden_output_test.rb +0 -33
  50. data/test/unit/roxml_test.rb +0 -60
  51. data/test/unit/string_test.rb +0 -11
data/examples/posts.rb CHANGED
@@ -4,19 +4,19 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec/spec_helper')
4
4
  class Post
5
5
  include ROXML
6
6
 
7
- xml_reader :href, :attr
8
- xml_reader :hash, :attr
9
- xml_reader :description, :attr
10
- xml_reader :tag, :attr
11
- xml_reader :time, :attr, :as => DateTime
12
- xml_reader :others, :attr, :as => Integer
13
- xml_reader :extended, :attr
7
+ xml_reader :href, :from => :attr
8
+ xml_reader :hash, :from => :attr
9
+ xml_reader :description, :from => :attr
10
+ xml_reader :tag, :from => :attr
11
+ xml_reader :time, :from => :attr, :as => DateTime
12
+ xml_reader :others, :from => :attr, :as => Integer
13
+ xml_reader :extended, :from => :attr
14
14
  end
15
15
 
16
16
  class Posts
17
17
  include ROXML
18
18
 
19
- xml_reader :posts, [Post]
19
+ xml_reader :posts, :as => [Post]
20
20
  end
21
21
 
22
22
  unless defined?(Spec)
data/examples/twitter.rb CHANGED
@@ -27,11 +27,11 @@ class Status
27
27
  xml_reader :in_reply_to_status_id, :as => Integer
28
28
  xml_reader :in_reply_to_user_id, :as => Integer
29
29
  xml_reader :favorited?
30
- xml_reader :user, User
30
+ xml_reader :user, :as => User
31
31
  end
32
32
 
33
33
  class Statuses
34
34
  include ROXML
35
35
 
36
- xml_reader :statuses, [Status]
36
+ xml_reader :statuses, :as => [Status]
37
37
  end
@@ -0,0 +1,70 @@
1
+ <route>
2
+ <totalDist>11185.321521477119</totalDist>
3
+ <totalHg>640</totalHg>
4
+ <totalMins>235.75000000000003</totalMins>
5
+ <lonlatx>357865</lonlatx>
6
+ <lonlaty>271635</lonlaty>
7
+ <grcenter>SH 71635 57865</grcenter>
8
+ <waypoints>
9
+ <waypoint>
10
+ <isLeg>false</isLeg>
11
+ <lonlatx>357290</lonlatx>
12
+ <lonlaty>271650</lonlaty>
13
+ <gridReference>SH 71650 57290</gridReference>
14
+ <ascent>81</ascent>
15
+ <descent>220</descent>
16
+ <distance>0</distance>
17
+ <bearing>0</bearing>
18
+ </waypoint>
19
+ <waypoint>
20
+ <isLeg>false</isLeg>
21
+ <lonlatx>357260</lonlatx>
22
+ <lonlaty>274600</lonlaty>
23
+ <gridReference>SH 74600 57260</gridReference>
24
+ <ascent>275</ascent>
25
+ <descent>48</descent>
26
+ <distance>2950.152538429157</distance>
27
+ <bearing>91</bearing>
28
+ </waypoint>
29
+ <waypoint>
30
+ <isLeg>false</isLeg>
31
+ <lonlatx>359160</lonlatx>
32
+ <lonlaty>273330</lonlaty>
33
+ <gridReference>SH 73330 59160</gridReference>
34
+ <ascent>73</ascent>
35
+ <descent>170</descent>
36
+ <distance>2285.3664913969487</distance>
37
+ <bearing>326</bearing>
38
+ </waypoint>
39
+ <waypoint>
40
+ <isLeg>false</isLeg>
41
+ <lonlatx>359170</lonlatx>
42
+ <lonlaty>270050</lonlaty>
43
+ <gridReference>SH 70050 59170</gridReference>
44
+ <ascent>182</ascent>
45
+ <descent>172</descent>
46
+ <distance>3280.015243867016</distance>
47
+ <bearing>270</bearing>
48
+ </waypoint>
49
+ <waypoint>
50
+ <isLeg>false</isLeg>
51
+ <lonlatx>357470</lonlatx>
52
+ <lonlaty>269740</lonlaty>
53
+ <gridReference>SH 69740 57470</gridReference>
54
+ <ascent>29</ascent>
55
+ <descent>107</descent>
56
+ <distance>1728.0335644888382</distance>
57
+ <bearing>190</bearing>
58
+ </waypoint>
59
+ <waypoint>
60
+ <isLeg>false</isLeg>
61
+ <lonlatx>356840</lonlatx>
62
+ <lonlaty>270440</lonlaty>
63
+ <gridReference>SH 70440 56840</gridReference>
64
+ <ascent>640</ascent>
65
+ <descent>717</descent>
66
+ <distance>941.7536832951597</distance>
67
+ <bearing>132</bearing>
68
+ </waypoint>
69
+ </waypoints>
70
+ </route>
data/lib/roxml.rb CHANGED
@@ -1,12 +1,14 @@
1
1
  $LOAD_PATH.unshift(File.dirname(__FILE__)) unless
2
2
  $LOAD_PATH.include?(File.dirname(__FILE__)) || $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))
3
3
 
4
+ require 'uri'
5
+
4
6
  %w(extensions definition xml).each do |file|
5
7
  require File.join('roxml', file)
6
8
  end
7
9
 
8
10
  module ROXML # :nodoc:
9
- VERSION = '2.4.3'
11
+ VERSION = '2.5.1'
10
12
 
11
13
  def self.included(base) # :nodoc:
12
14
  base.extend ClassMethods::Accessors,
@@ -46,8 +48,8 @@ module ROXML # :nodoc:
46
48
  # class Measurement
47
49
  # include ROXML
48
50
  #
49
- # xml_reader :units, :attr
50
- # xml_reader :value, :content
51
+ # xml_reader :units, :from => :attr
52
+ # xml_reader :value, :from => :content
51
53
  #
52
54
  # def xml_initialize
53
55
  # # the object is instantiated, and all xml attributes are imported
@@ -69,14 +71,15 @@ module ROXML # :nodoc:
69
71
  # #xml_initialize may be written to take arguments, in which case extra arguments
70
72
  # from from_xml will be passed into the function.
71
73
  #
72
- def xml_initialize
74
+ def xml_initialize # :nodoc:
73
75
  end
76
+ deprecate :xml_initialize => :after_parse
74
77
  end
75
78
 
76
79
  module Conversions
77
80
  # Returns a LibXML::XML::Node or a REXML::Element representing this object
78
81
  def to_xml(name = nil)
79
- returning XML::Node.new_element(name || self.class.tag_name) do |root|
82
+ returning XML::Node.new((name || self.class.tag_name).to_s) do |root|
80
83
  self.class.roxml_attrs.each do |attr|
81
84
  ref = attr.to_ref(self)
82
85
  v = ref.to_xml
@@ -175,9 +178,9 @@ module ROXML # :nodoc:
175
178
  (@roxml_naming_convention || superclass.try(:roxml_naming_convention)).freeze
176
179
  end
177
180
 
178
- # Declares an accesser to a certain xml element, whether an attribute, a node,
179
- # or a typed collection of nodes. Typically you should call xml_reader or xml_accessor
180
- # rather than calling this method directly, but the instructions below apply to both.
181
+ # Declares a reference to a certain xml element, whether an attribute, a node,
182
+ # or a typed collection of nodes. This method does not add a corresponding accessor
183
+ # to the object. For that behavior see the similar methods: .xml_reader and .xml_accessor.
181
184
  #
182
185
  # == Sym Option
183
186
  # [sym] Symbol representing the name of the accessor.
@@ -187,12 +190,12 @@ module ROXML # :nodoc:
187
190
  # if no other is declared. For example,
188
191
  #
189
192
  # xml_reader :bob
190
- # xml_accessor :pony, :attr
193
+ # xml_accessor :pony, :from => :attr
191
194
  #
192
195
  # are equivalent to:
193
196
  #
194
197
  # xml_reader :bob, :from => 'bob'
195
- # xml_accessor :pony, :attr => 'pony'
198
+ # xml_accessor :pony, :from => '@pony'
196
199
  #
197
200
  # === Boolean attributes
198
201
  # If the name ends in a ?, ROXML will attempt to coerce the value to true or false,
@@ -200,7 +203,7 @@ module ROXML # :nodoc:
200
203
  # to false, as shown below:
201
204
  #
202
205
  # xml_reader :desirable?
203
- # xml_reader :bizzare?, :attr => 'BIZZARE'
206
+ # xml_reader :bizzare?, :from => '@BIZZARE'
204
207
  #
205
208
  # x = #from_xml(%{
206
209
  # <object BIZZARE="1">
@@ -234,94 +237,90 @@ module ROXML # :nodoc:
234
237
  # }).strange?
235
238
  # => DUNNO
236
239
  #
237
- # == Type options
238
- # All type arguments may be used as the type argument to indicate just type,
239
- # or used as :from, pointing to a xml name to indicate both type and attribute name.
240
- # Also, any type may be passed via an array to indicate that multiple instances
241
- # of the object should be returned as an array.
240
+ # == Blocks
241
+ # You may also pass a block which manipulates the associated parsed value.
242
242
  #
243
- # === :attr
244
- # Declare an accessor that represents an XML attribute.
243
+ # class Muffins
244
+ # include ROXML
245
245
  #
246
- # Example:
247
- # class Book
248
- # xml_reader :isbn, :attr => "ISBN" # 'ISBN' is used to specify :from
249
- # xml_accessor :title, :attr # :from defaults to :title
246
+ # xml_reader(:count, :from => 'bakers_dozens') {|val| val.to_i * 13 }
250
247
  # end
251
248
  #
252
- # To map:
253
- # <book ISBN="0974514055" title="Programming Ruby: the pragmatic programmers' guide" />
249
+ # For hash types, the block recieves the key and value as arguments, and they should
250
+ # be returned as an array of [key, value]
254
251
  #
255
- # === :text
256
- # The default type, if none is specified. Declares an accessor that
257
- # represents a text node from XML.
252
+ # For array types, the entire array is passed in, and must be returned in the same fashion.
258
253
  #
259
- # Example:
260
- # class Book
261
- # xml_reader :author, :text => 'Author'
262
- # xml_accessor :description, :text, :as => :cdata
263
- # xml_reader :title
264
- # end
254
+ # == Options
255
+ # === :as
256
+ # ==== Basic Types
257
+ # Allows you to specify one of several basic types to return the value as. For example
265
258
  #
266
- # To map:
259
+ # xml_reader :count, :as => Integer
260
+ #
261
+ # is equivalent to:
262
+ #
263
+ # xml_reader(:count) {|val| Integer(val) unless val.empty? }
264
+ #
265
+ # Such block shorthands for Integer, Float, Fixnum, BigDecimal, Date, Time, and DateTime
266
+ # are currently available, but only for non-Hash declarations.
267
+ #
268
+ # To reference many elements, put the desired type in a literal array. e.g.:
269
+ #
270
+ # xml_reader :counts, :as => [Integer]
271
+ #
272
+ # Even an array of :text nodes can be specified with :as => []
273
+ #
274
+ # xml_reader :quotes, :as => []
275
+ #
276
+ # === Other ROXML Class
277
+ # Declares an accessor that represents another ROXML class as child XML element
278
+ # (one-to-one or composition) or array of child elements (one-to-many or
279
+ # aggregation) of this type. Default is one-to-one. For one-to-many, simply pass the class
280
+ # as the only element in an array.
281
+ #
282
+ # Composition example:
267
283
  # <book>
268
- # <title>Programming Ruby: the pragmatic programmers' guide</title>
269
- # <description><![CDATA[Probably the best Ruby book out there]]></description>
270
- # <Author>David Thomas</author>
284
+ # <publisher>
285
+ # <name>Pragmatic Bookshelf</name>
286
+ # </publisher>
271
287
  # </book>
272
288
  #
273
- # Likewise, a number of :text node values can be collected in an array like so:
289
+ # Can be mapped using the following code:
290
+ # class Book
291
+ # xml_reader :publisher, :as => Publisher
292
+ # end
274
293
  #
275
- # Example:
294
+ # Aggregation example:
295
+ # <library>
296
+ # <books>
297
+ # <book/>
298
+ # <book/>
299
+ # </books>
300
+ # </library>
301
+ #
302
+ # Can be mapped using the following code:
276
303
  # class Library
277
- # xml_reader :books, [:text], :in => 'books'
304
+ # xml_reader :books, :as => [Book], :in => "books"
278
305
  # end
279
306
  #
280
- # To map:
307
+ # If you don't have the <books> tag to wrap around the list of <book> tags:
281
308
  # <library>
282
- # <books>
283
- # <book>To kill a mockingbird</book>
284
- # <book>House of Leaves</book>
285
- # <book>Gödel, Escher, Bach</book>
286
- # </books>
309
+ # <name>Ruby books</name>
310
+ # <book/>
311
+ # <book/>
287
312
  # </library>
288
313
  #
289
- # === :content
290
- # A special case of :text, this refers to the content of the current node,
291
- # rather than a sub-node
292
- #
293
- # Example:
294
- # class Contributor
295
- # xml_reader :name, :content
296
- # xml_reader :role, :attr
297
- # end
298
- #
299
- # To map:
300
- # <contributor role="editor">James Wick</contributor>
314
+ # You can skip the wrapper argument:
315
+ # xml_reader :books, :as => [Book]
301
316
  #
302
- # === Hash
317
+ # ==== Hash
303
318
  # Somewhere between the simplicity of a :text/:attr mapping, and the complexity of
304
319
  # a full Object/Type mapping, lies the Hash mapping. It serves in the case where you have
305
320
  # a collection of key-value pairs represented in your xml. You create a hash declaration by
306
321
  # passing a hash mapping as the type argument. A few examples:
307
322
  #
308
- # ==== Hash of :attrs
309
- # For xml such as this:
310
- #
311
- # <dictionary>
312
- # <definitions>
313
- # <definition dt="quaquaversally"
314
- # dd="adjective: (of a geological formation) sloping downward from the center in all directions." />
315
- # <definition dt="tergiversate"
316
- # dd="To use evasions or ambiguities; equivocate." />
317
- # </definitions>
318
- # </dictionary>
319
- #
320
- # You can use the :attrs key in you has with a [:key, :value] name array:
321
- #
322
- # xml_reader :definitions, {:attrs => ['dt', 'dd']}, :in => :definitions
323
- #
324
- # ==== Hash of :texts
323
+ # ===== Hash of :texts
325
324
  # For xml such as this:
326
325
  #
327
326
  # <dictionary>
@@ -336,10 +335,10 @@ module ROXML # :nodoc:
336
335
  # </dictionary>
337
336
  #
338
337
  # You can individually declare your key and value names:
339
- # xml_reader :definitions, {:key => 'word',
340
- # :value => 'meaning'}
338
+ # xml_reader :definitions, :as => {:key => 'word',
339
+ # :value => 'meaning'}
341
340
  #
342
- # ==== Hash of :content &c.
341
+ # ===== Hash of :content &c.
343
342
  # For xml such as this:
344
343
  #
345
344
  # <dictionary>
@@ -350,10 +349,10 @@ module ROXML # :nodoc:
350
349
  # You can individually declare the key and value, but with the attr, you need to provide both the type
351
350
  # and name of that type (i.e. {:attr => :word}), because omitting the type will result in ROXML
352
351
  # defaulting to :text
353
- # xml_reader :definitions, {:key => {:attr => 'word'},
354
- # :value => :content}
352
+ # xml_reader :definitions, :as => {:key => {:attr => 'word'},
353
+ # :value => :content}
355
354
  #
356
- # ==== Hash of :name &c.
355
+ # ===== Hash of :name &c.
357
356
  # For xml such as this:
358
357
  #
359
358
  # <dictionary>
@@ -362,112 +361,119 @@ module ROXML # :nodoc:
362
361
  # </dictionary>
363
362
  #
364
363
  # You can pick up the node names (e.g. quaquaversally) using the :name keyword:
365
- # xml_reader :definitions, {:key => :name,
366
- # :value => :content}
367
- #
368
- # === Other ROXML Class
369
- # Declares an accessor that represents another ROXML class as child XML element
370
- # (one-to-one or composition) or array of child elements (one-to-many or
371
- # aggregation) of this type. Default is one-to-one. Use :array option for one-to-many, or
372
- # simply pass the class in an array.
364
+ # xml_reader :definitions, :as => {:key => :name,
365
+ # :value => :content}
373
366
  #
374
- # Composition example:
375
- # <book>
376
- # <publisher>
377
- # <name>Pragmatic Bookshelf</name>
378
- # </publisher>
379
- # </book>
367
+ # === :from
368
+ # The name by which the xml value will be found, either an attribute or tag name in XML.
369
+ # Default is sym, or the singular form of sym, in the case of arrays and hashes.
380
370
  #
381
- # Can be mapped using the following code:
382
- # class Book
383
- # xml_reader :publisher, Publisher
384
- # end
371
+ # This value may also include XPath notation.
385
372
  #
386
- # Aggregation example:
387
- # <library>
388
- # <books>
389
- # <book/>
390
- # <book/>
391
- # </books>
392
- # </library>
373
+ # ==== :from => :content
374
+ # When :from is set to :content, this refers to the content of the current node,
375
+ # rather than a sub-node. It is equivalent to :from => '.'
393
376
  #
394
- # Can be mapped using the following code:
395
- # class Library
396
- # xml_reader :books, [Book], :in => "books"
377
+ # Example:
378
+ # class Contributor
379
+ # xml_reader :name, :from => :content
380
+ # xml_reader :role, :from => :attr
397
381
  # end
398
382
  #
399
- # If you don't have the <books> tag to wrap around the list of <book> tags:
400
- # <library>
401
- # <name>Ruby books</name>
402
- # <book/>
403
- # <book/>
404
- # </library>
405
- #
406
- # You can skip the wrapper argument:
407
- # xml_reader :books, [Book]
408
- #
409
- # == Blocks
410
- # You may also pass a block which manipulates the associated parsed value.
383
+ # To map:
384
+ # <contributor role="editor">James Wick</contributor>
411
385
  #
412
- # class Muffins
413
- # include ROXML
386
+ # ==== :from => :attr
387
+ # When :from is set to :attr, this refers to the content of an attribute,
388
+ # rather than a sub-node. It is equivalent to :from => '@attribute_name'
414
389
  #
415
- # xml_reader(:count, :from => 'bakers_dozens') {|val| val.to_i * 13 }
390
+ # Example:
391
+ # class Book
392
+ # xml_reader :isbn, :from => "@ISBN"
393
+ # xml_accessor :title, :from => :attr # :from defaults to '@title'
416
394
  # end
417
395
  #
418
- # For hash types, the block recieves the key and value as arguments, and they should
419
- # be returned as an array of [key, value]
396
+ # To map:
397
+ # <book ISBN="0974514055" title="Programming Ruby: the pragmatic programmers' guide" />
420
398
  #
421
- # For array types, the entire array is passed in, and must be returned in the same fashion.
399
+ # ==== :from => :text
400
+ # The default source, if none is specified, this means the accessor
401
+ # represents a text node from XML. This is documented for completeness
402
+ # only. You should just leave this option off when you want the default behavior,
403
+ # as in the examples below.
422
404
  #
423
- # === Block Shorthands
405
+ # :text is equivalent to :from => accessor_name, and you should specify the
406
+ # actual node name if it differs, as in the case of :author below.
424
407
  #
425
- # Alternatively, you may use block shorthands to specify common coercions, such that:
408
+ # Example:
409
+ # class Book
410
+ # xml_reader :author, :from => 'Author
411
+ # xml_accessor :description, :cdata => true
412
+ # xml_reader :title
413
+ # end
426
414
  #
427
- # xml_reader :count, :as => Integer
415
+ # To map:
416
+ # <book>
417
+ # <title>Programming Ruby: the pragmatic programmers' guide</title>
418
+ # <description><![CDATA[Probably the best Ruby book out there]]></description>
419
+ # <Author>David Thomas</Author>
420
+ # </book>
428
421
  #
429
- # is equivalent to:
422
+ # Likewise, a number of :text node values can be collected in an array like so:
430
423
  #
431
- # xml_reader(:count) {|val| Integer(val) }
424
+ # Example:
425
+ # class Library
426
+ # xml_reader :books, :as => []
427
+ # end
432
428
  #
433
- # Block shorthands :float, Float, :integer and Integer are currently available,
434
- # but only for non-Hash declarations.
429
+ # To map:
430
+ # <library>
431
+ # <book>To kill a mockingbird</book>
432
+ # <book>House of Leaves</book>
433
+ # <book>Gödel, Escher, Bach</book>
434
+ # </library>
435
435
  #
436
- # == Other options
437
- # [:from] The name by which the xml value will be found, either an attribute or tag name in XML. Default is sym, or the singular form of sym, in the case of arrays and hashes.
438
- # [:as] one or more of the following: :cdata for character data; Integer, Float, Date, Time or DateTime to coerce to the respective type
439
- # [:in] An optional name of a wrapping tag for this XML accessor
440
- # [:else] Default value for attribute, if missing
436
+ # === Other Options
437
+ # [:in] An optional name of a wrapping tag for this XML accessor.
438
+ # This can include other xpath values, which will be joined with :from with a '/'
439
+ # [:else] Default value for attribute, if missing from the xml on .from_xml
441
440
  # [:required] If true, throws RequiredElementMissing when the element isn't present
442
441
  # [:frozen] If true, all results are frozen (using #freeze) at parse-time.
442
+ # [:cdata] True for values which should be input from or output as cdata elements
443
443
  #
444
- def xml_reference(writable, sym, type_and_or_opts = :text, opts = nil, &block)
445
- opts = Definition.new(sym, *[type_and_or_opts, opts].compact, &block)
446
-
447
- add_accessor(opts, writable)
444
+ def xml_attr(sym, type_and_or_opts = nil, opts = nil, &block)
445
+ returning Definition.new(sym, *[type_and_or_opts, opts].compact, &block) do |attr|
446
+ if roxml_attrs.map(&:accessor).include? attr.accessor
447
+ raise "Accessor #{attr.accessor} is already defined as XML accessor in class #{self.name}"
448
+ end
449
+ @roxml_attrs << attr
450
+ end
448
451
  end
449
452
 
450
- def xml(sym, writable = false, type_and_or_opts = :text, opts = nil, &block) #:nodoc:
451
- xml_reference(writable, sym, type_and_or_opts, opts, &block)
453
+ def xml(sym, writable = false, type_and_or_opts = nil, opts = nil, &block) #:nodoc:
454
+ send(writable ? :xml_accessor : :xml_reader, sym, type_and_or_opts, opts, &block)
452
455
  end
453
- deprecate :xml => :xml_reference
456
+ deprecate :xml => "use xml_attr, xml_reader, or xml_accessor instead"
454
457
 
455
- # Declares a read-only xml reference. See xml for details.
458
+ # Declares a read-only xml reference. See xml_attr for details.
456
459
  #
457
460
  # Note that while xml_reader does not create a setter for this attribute,
458
461
  # its value can be modified indirectly via methods. For more complete
459
462
  # protection, consider the :frozen option.
460
- def xml_reader(sym, type_and_or_opts = :text, opts = nil, &block)
461
- xml_reference false, sym, type_and_or_opts, opts, &block
463
+ def xml_reader(sym, type_and_or_opts = nil, opts = nil, &block)
464
+ attr = xml_attr sym, type_and_or_opts, opts, &block
465
+ add_reader(attr)
462
466
  end
463
467
 
464
- # Declares a writable xml reference. See xml for details.
468
+ # Declares a writable xml reference. See xml_attr for details.
465
469
  #
466
470
  # Note that while xml_accessor does create a setter for this attribute,
467
471
  # you can use the :frozen option to prevent its value from being
468
472
  # modified indirectly via methods.
469
- def xml_accessor(sym, type_and_or_opts = :text, opts = nil, &block)
470
- xml_reference true, sym, type_and_or_opts, opts, &block
473
+ def xml_accessor(sym, type_and_or_opts = nil, opts = nil, &block)
474
+ attr = xml_attr sym, type_and_or_opts, opts, &block
475
+ add_reader(attr)
476
+ attr_writer(attr.variable_name)
471
477
  end
472
478
 
473
479
  # This method is deprecated, please use xml_initialize instead
@@ -484,23 +490,9 @@ module ROXML # :nodoc:
484
490
  deprecate :xml_construct => :xml_initialize
485
491
 
486
492
  private
487
- def add_accessor(attr, writable)
488
- if roxml_attrs.map(&:accessor).include? attr.accessor
489
- raise "Accessor #{attr.accessor} is already defined as XML accessor in class #{self.name}"
490
- end
491
- @roxml_attrs << attr
492
-
493
+ def add_reader(attr)
493
494
  define_method(attr.accessor) do
494
- result = instance_variable_get("@#{attr.variable_name}")
495
- if result.nil?
496
- result = attr.default
497
- instance_variable_set("@#{attr.variable_name}", result)
498
- end
499
- result
500
- end
501
-
502
- if writable && !instance_methods.include?("#{attr.accessor}=")
503
- attr_writer(attr.accessor)
495
+ instance_variable_get("@#{attr.variable_name}")
504
496
  end
505
497
  end
506
498
  end
@@ -559,7 +551,7 @@ module ROXML # :nodoc:
559
551
  # Creates a new Ruby object from XML using mapping information
560
552
  # annotated in the class.
561
553
  #
562
- # The input data is either an XML::Node or a String representing
554
+ # The input data is either an XML::Node, String, Pathname, or File representing
563
555
  # the XML document.
564
556
  #
565
557
  # Example
@@ -568,7 +560,9 @@ module ROXML # :nodoc:
568
560
  # book = Book.from_xml("<book><name>Beyond Java</name></book>")
569
561
  #
570
562
  # _initialization_args_ passed into from_xml will be passed into
571
- # the object #xml_initialize method.
563
+ # the object's .new, prior to populating the xml_attrs.
564
+ #
565
+ # After the instatiation and xml population
572
566
  #
573
567
  # See also: xml_initialize
574
568
  #
@@ -581,13 +575,19 @@ module ROXML # :nodoc:
581
575
  end.map {|attr| attr.to_ref(self).value_in(xml) }
582
576
  new(*args)
583
577
  else
584
- returning allocate do |inst|
578
+ returning new(*initialization_args) do |inst|
585
579
  roxml_attrs.each do |attr|
586
- inst.instance_variable_set("@#{attr.variable_name}", attr.to_ref(inst).value_in(xml))
580
+ value = attr.to_ref(inst).value_in(xml)
581
+ setter = :"#{attr.variable_name}="
582
+ inst.respond_to?(setter) \
583
+ ? inst.send(setter, value) \
584
+ : inst.instance_variable_set("@#{attr.variable_name}", value)
587
585
  end
588
- inst.send(:xml_initialize, *initialization_args)
586
+ inst.try(:after_parse)
589
587
  end
590
588
  end
589
+ rescue ArgumentError => e
590
+ raise e, e.message + " for class #{self}"
591
591
  end
592
592
 
593
593
  # Deprecated in favor of #from_xml