xml-mapping 0.8.1 → 0.9.1

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.
Files changed (71) hide show
  1. data/ChangeLog +64 -3
  2. data/README +871 -173
  3. data/README_XPATH +40 -13
  4. data/Rakefile +37 -26
  5. data/TODO.txt +39 -8
  6. data/examples/README +5 -0
  7. data/examples/company_usage.intout +34 -22
  8. data/examples/documents_folders.rb +31 -0
  9. data/examples/documents_folders.xml +16 -0
  10. data/examples/documents_folders_usage.intin.rb +18 -0
  11. data/examples/documents_folders_usage.intout +46 -0
  12. data/examples/order_signature_enhanced_usage.intout +21 -11
  13. data/examples/order_usage.intin.rb +52 -5
  14. data/examples/order_usage.intout +154 -80
  15. data/examples/person.intin.rb +44 -0
  16. data/examples/person.intout +27 -0
  17. data/examples/person_mm.intin.rb +119 -0
  18. data/examples/person_mm.intout +114 -0
  19. data/examples/publication.intin.rb +44 -0
  20. data/examples/publication.intout +20 -0
  21. data/examples/reader.intin.rb +33 -0
  22. data/examples/reader.intout +19 -0
  23. data/examples/stringarray.rb +5 -0
  24. data/examples/stringarray.xml +10 -0
  25. data/examples/stringarray_usage.intin.rb +11 -0
  26. data/examples/stringarray_usage.intout +31 -0
  27. data/examples/time_augm.intout +19 -7
  28. data/examples/time_augm_loading.intin.rb +44 -0
  29. data/examples/time_augm_loading.intout +12 -0
  30. data/examples/time_node.intin.rb +79 -0
  31. data/examples/time_node.rb +3 -2
  32. data/examples/time_node_w_marshallers.intin.rb +48 -0
  33. data/examples/time_node_w_marshallers.intout +25 -0
  34. data/examples/time_node_w_marshallers.xml +9 -0
  35. data/examples/xpath_create_new.intout +132 -114
  36. data/examples/xpath_ensure_created.intout +86 -65
  37. data/examples/xpath_pathological.intout +16 -16
  38. data/examples/xpath_usage.intout +1 -1
  39. data/install.rb +1 -0
  40. data/lib/xml/mapping.rb +3 -1
  41. data/lib/xml/mapping/base.rb +442 -272
  42. data/lib/xml/mapping/core_classes_mapping.rb +32 -0
  43. data/lib/xml/mapping/standard_nodes.rb +176 -86
  44. data/lib/xml/mapping/version.rb +2 -2
  45. data/lib/xml/rexml_ext.rb +186 -0
  46. data/lib/xml/xxpath.rb +28 -265
  47. data/lib/xml/xxpath/steps.rb +345 -0
  48. data/lib/xml/xxpath_methods.rb +96 -0
  49. data/test/all_tests.rb +4 -1
  50. data/test/benchmark_fixtures.rb +14 -0
  51. data/test/{multiple_mappings.rb → bookmarks.rb} +0 -0
  52. data/test/company.rb +47 -0
  53. data/test/documents_folders.rb +11 -1
  54. data/test/examples_test.rb +29 -0
  55. data/test/fixtures/benchmark.xml +77 -0
  56. data/test/fixtures/company1.xml +9 -0
  57. data/test/fixtures/documents_folders.xml +0 -8
  58. data/test/fixtures/documents_folders2.xml +13 -19
  59. data/test/fixtures/triangle_m1.xml +17 -0
  60. data/test/fixtures/triangle_m2.xml +19 -0
  61. data/test/inheritance_test.rb +50 -0
  62. data/test/multiple_mappings_test.rb +155 -0
  63. data/test/rexml_xpath_benchmark.rb +29 -0
  64. data/test/triangle_mm.rb +57 -0
  65. data/test/xml_mapping_adv_test.rb +36 -1
  66. data/test/xml_mapping_test.rb +136 -7
  67. data/test/xpath_test.rb +154 -0
  68. data/test/xxpath_benchmark.rb +36 -0
  69. data/test/xxpath_benchmark.result1.txt +17 -0
  70. data/test/xxpath_methods_test.rb +61 -0
  71. metadata +139 -90
@@ -0,0 +1,32 @@
1
+ class String
2
+ def self.load_from_xml(xml, options={:mapping=>:_default})
3
+ xml.text
4
+ end
5
+
6
+ def fill_into_xml(xml, options={:mapping=>:_default})
7
+ xml.text = self
8
+ end
9
+
10
+ def text
11
+ self
12
+ end
13
+ end
14
+
15
+
16
+ class Numeric
17
+ def self.load_from_xml(xml, options={:mapping=>:_default})
18
+ begin
19
+ Integer(xml.text)
20
+ rescue ArgumentError
21
+ Float(xml.text)
22
+ end
23
+ end
24
+
25
+ def fill_into_xml(xml, options={:mapping=>:_default})
26
+ xml.text = self.to_s
27
+ end
28
+
29
+ def text
30
+ self.to_s
31
+ end
32
+ end
@@ -9,21 +9,21 @@ module XML
9
9
  #
10
10
  # text_node :_attrname_, _path_ [, :default_value=>_obj_]
11
11
  # [, :optional=>true]
12
+ # [, :mapping=>_m_]
12
13
  #
13
14
  # Node that maps an XML node's text (the element's first child
14
15
  # text node resp. the attribute's value) to a (string) attribute
15
- # of the mapped object. Since TextNode inherits from
16
- # SingleAttributeNode, the first argument to the node factory
17
- # function is the attribute name (as a symbol). Handling of
18
- # <tt>:default_value</tt> and <tt>:optional</tt> option arguments
19
- # (if given) is also provided by the superclass -- see there for
20
- # details.
16
+ # of the mapped object. _path_ (an XPath expression) locates the
17
+ # XML node, _attrname_ (a symbol) names the
18
+ # attribute. <tt>:default_value</tt> is the default value,
19
+ # :optional=>true is equivalent to :default_value=>nil (see
20
+ # superclass documentation for details). <tt>_m_</tt> is the
21
+ # mapping; it defaults to the current default mapping
21
22
  class TextNode < SingleAttributeNode
22
- # Initializer. _path_ (a string, the 2nd argument to the node
23
- # factory function) is the XPath expression that locates the
24
- # mapped node in the XML.
25
- def initialize_impl(path)
23
+ def initialize(*args)
24
+ path,*args = super(*args)
26
25
  @path = XML::XXPath.new(path)
26
+ args
27
27
  end
28
28
  def extract_attr_value(xml) # :nodoc:
29
29
  default_when_xpath_err{ @path.first(xml).text }
@@ -37,13 +37,16 @@ module XML
37
37
  #
38
38
  # numeric_node :_attrname_, _path_ [, :default_value=>_obj_]
39
39
  # [, :optional=>true]
40
+ # [, :mapping=>_m_]
40
41
  #
41
42
  # Like TextNode, but interprets the XML node's text as a number
42
43
  # (Integer or Float, depending on the nodes's text) and maps it to
43
44
  # an Integer or Float attribute.
44
45
  class NumericNode < SingleAttributeNode
45
- def initialize_impl(path)
46
+ def initialize(*args)
47
+ path,*args = super(*args)
46
48
  @path = XML::XXPath.new(path)
49
+ args
47
50
  end
48
51
  def extract_attr_value(xml) # :nodoc:
49
52
  txt = default_when_xpath_err{ @path.first(xml).text }
@@ -67,10 +70,9 @@ module XML
67
70
  class SubObjectBaseNode < SingleAttributeNode
68
71
  # processes the keyword arguments :class, :marshaller, and
69
72
  # :unmarshaller (_args_ is ignored). When this initiaizer
70
- # returns, @options[:marshaller] and @options[:unmarshaller] are
71
- # set to procs that marshal/unmarshal a Ruby object to/from an
72
- # XML tree according to the keyword arguments that were passed
73
- # to the initializer:
73
+ # returns, @marshaller and @unmarshaller are set to procs that
74
+ # marshal/unmarshal a Ruby object to/from an XML tree according
75
+ # to the keyword arguments that were passed to the initializer:
74
76
  #
75
77
  # You either supply a :class argument with a class implementing
76
78
  # XML::Mapping -- in that case, the subtree will be mapped to an
@@ -89,37 +91,50 @@ module XML
89
91
  #
90
92
  # If both :class and :marshaller/:unmarshaller arguments are
91
93
  # supplied, the latter take precedence.
92
- def initialize_impl(*args)
94
+ def initialize(*args)
95
+ args = super(*args)
96
+
97
+ @sub_mapping = @options[:sub_mapping] || @mapping
98
+ @marshaller, @unmarshaller = @options[:marshaller], @options[:unmarshaller]
99
+
93
100
  if @options[:class]
94
- unless @options[:marshaller]
95
- @options[:marshaller] = proc {|xml,value|
96
- value.fill_into_xml(xml)
101
+ unless @marshaller
102
+ @marshaller = proc {|xml,value|
103
+ value.fill_into_xml xml, :mapping=>@sub_mapping
104
+ if xml.unspecified?
105
+ xml.name = value.class.root_element_name :mapping=>@sub_mapping
106
+ xml.unspecified = false
107
+ end
97
108
  }
98
109
  end
99
- unless @options[:unmarshaller]
100
- @options[:unmarshaller] = proc {|xml|
101
- @options[:class].load_from_xml(xml)
110
+ unless @unmarshaller
111
+ @unmarshaller = proc {|xml|
112
+ @options[:class].load_from_xml xml, :mapping=>@sub_mapping
102
113
  }
103
114
  end
104
115
  end
105
116
 
106
- unless @options[:marshaller]
107
- @options[:marshaller] = proc {|xml,value|
108
- value.fill_into_xml(xml)
117
+ unless @marshaller
118
+ @marshaller = proc {|xml,value|
119
+ value.fill_into_xml xml, :mapping=>@sub_mapping
109
120
  if xml.unspecified?
110
- xml.name = value.class.root_element_name
121
+ xml.name = value.class.root_element_name :mapping=>@sub_mapping
111
122
  xml.unspecified = false
112
123
  end
113
124
  }
114
125
  end
115
- unless @options[:unmarshaller]
116
- @options[:unmarshaller] = proc {|xml|
117
- XML::Mapping.load_object_from_xml(xml)
126
+ unless @unmarshaller
127
+ @unmarshaller = proc {|xml|
128
+ XML::Mapping.load_object_from_xml xml, :mapping=>@sub_mapping
118
129
  }
119
130
  end
131
+
132
+ args
120
133
  end
121
134
  end
122
135
 
136
+ require 'xml/mapping/core_classes_mapping'
137
+
123
138
  # Node factory function synopsis:
124
139
  #
125
140
  # object_node :_attrname_, _path_ [, :default_value=>_obj_]
@@ -127,6 +142,8 @@ module XML
127
142
  # [, :class=>_c_]
128
143
  # [, :marshaller=>_proc_]
129
144
  # [, :unmarshaller=>_proc_]
145
+ # [, :mapping=>_m_]
146
+ # [, :sub_mapping=>_sm_]
130
147
  #
131
148
  # Node that maps a subtree in the source XML to a Ruby
132
149
  # object. :_attrname_ and _path_ are again the attribute name
@@ -140,15 +157,16 @@ module XML
140
157
  class ObjectNode < SubObjectBaseNode
141
158
  # Initializer. _path_ (a string denoting an XPath expression) is
142
159
  # the location of the subtree.
143
- def initialize_impl(path)
144
- super
145
- @path = XML::XXPath.new(path)
160
+ def initialize(*args)
161
+ path,*args = super(*args)
162
+ @path = XML::XXPath.new(path)
163
+ args
146
164
  end
147
165
  def extract_attr_value(xml) # :nodoc:
148
- @options[:unmarshaller].call(default_when_xpath_err{@path.first(xml)})
166
+ @unmarshaller.call(default_when_xpath_err{@path.first(xml)})
149
167
  end
150
168
  def set_attr_value(xml, value) # :nodoc:
151
- @options[:marshaller].call(@path.first(xml,:ensure_created=>true), value)
169
+ @marshaller.call(@path.first(xml,:ensure_created=>true), value)
152
170
  end
153
171
  end
154
172
 
@@ -157,6 +175,7 @@ module XML
157
175
  # boolean_node :_attrname_, _path_,
158
176
  # _true_value_, _false_value_ [, :default_value=>_obj_]
159
177
  # [, :optional=>true]
178
+ # [, :mapping=>_m_]
160
179
  #
161
180
  # Node that maps an XML node's text (the element name resp. the
162
181
  # attribute value) to a boolean attribute of the mapped
@@ -168,9 +187,11 @@ module XML
168
187
  # represent the +false+ boolean value.
169
188
  class BooleanNode < SingleAttributeNode
170
189
  # Initializer.
171
- def initialize_impl(path,true_value,false_value)
190
+ def initialize(*args)
191
+ path,true_value,false_value,*args = super(*args)
172
192
  @path = XML::XXPath.new(path)
173
193
  @true_value = true_value; @false_value = false_value
194
+ args
174
195
  end
175
196
  def extract_attr_value(xml) # :nodoc:
176
197
  default_when_xpath_err{ @path.first(xml).text==@true_value }
@@ -188,6 +209,8 @@ module XML
188
209
  # [, :class=>_c_]
189
210
  # [, :marshaller=>_proc_]
190
211
  # [, :unmarshaller=>_proc_]
212
+ # [, :mapping=>_m_]
213
+ # [, :sub_mapping=>_sm_]
191
214
  #
192
215
  # -or-
193
216
  #
@@ -233,38 +256,35 @@ module XML
233
256
  # </bar>
234
257
  # </foo>
235
258
  class ArrayNode < SubObjectBaseNode
236
- # Initializer, delegates to do_initialize. Called with keyword
237
- # arguments and either 1 or 2 paths; the hindmost path argument
238
- # passed is delegated to _per_arrelement_path_; the preceding
239
- # path argument (if present, "" by default) is delegated to
240
- # _base_path_.
241
- def initialize_impl(path,path2=nil)
242
- super
243
- if path2
244
- do_initialize(path,path2)
245
- else
246
- do_initialize("",path)
247
- end
248
- end
249
- # "Real" initializer.
250
- def do_initialize(base_path,per_arrelement_path)
251
- per_arrelement_path=per_arrelement_path[1..-1] if per_arrelement_path[0]==?/
252
- @base_path = XML::XXPath.new(base_path)
253
- @per_arrelement_path = XML::XXPath.new(per_arrelement_path)
254
- @reader_path = XML::XXPath.new(base_path+"/"+per_arrelement_path)
259
+ # Initializer. Called with keyword arguments and either 1 or 2
260
+ # paths; the hindmost path argument passed is delegated to
261
+ # _per_arrelement_path_; the preceding path argument (if
262
+ # present, "" by default) is delegated to _base_path_.
263
+ def initialize(*args)
264
+ path,path2,*args = super(*args)
265
+ base_path,per_arrelement_path = if path2
266
+ [path,path2]
267
+ else
268
+ [".",path]
269
+ end
270
+ per_arrelement_path=per_arrelement_path[1..-1] if per_arrelement_path[0]==?/
271
+ @base_path = XML::XXPath.new(base_path)
272
+ @per_arrelement_path = XML::XXPath.new(per_arrelement_path)
273
+ @reader_path = XML::XXPath.new(base_path+"/"+per_arrelement_path)
274
+ args
255
275
  end
256
276
  def extract_attr_value(xml) # :nodoc:
257
277
  result = []
258
278
  default_when_xpath_err{@reader_path.all(xml)}.each do |elt|
259
- result << @options[:unmarshaller].call(elt)
279
+ result << @unmarshaller.call(elt)
260
280
  end
261
281
  result
262
282
  end
263
283
  def set_attr_value(xml, value) # :nodoc:
264
- base_elt = @base_path.first(xml,:ensure_created=>true)
265
- value.each do |arr_elt|
266
- @options[:marshaller].call(@per_arrelement_path.create_new(base_elt), arr_elt)
267
- end
284
+ base_elt = @base_path.first(xml,:ensure_created=>true)
285
+ value.each do |arr_elt|
286
+ @marshaller.call(@per_arrelement_path.create_new(base_elt), arr_elt)
287
+ end
268
288
  end
269
289
  end
270
290
 
@@ -277,6 +297,8 @@ module XML
277
297
  # [, :class=>_c_]
278
298
  # [, :marshaller=>_proc_]
279
299
  # [, :unmarshaller=>_proc_]
300
+ # [, :mapping=>_m_]
301
+ # [, :sub_mapping=>_sm_]
280
302
  #
281
303
  # - or -
282
304
  #
@@ -296,45 +318,113 @@ module XML
296
318
  # to such a node, key_path_ names the node whose text becomes the
297
319
  # associated hash key.
298
320
  class HashNode < SubObjectBaseNode
299
- # Initializer, delegates to do_initialize. Called with keyword
300
- # arguments and either 2 or 3 paths; the hindmost path argument
301
- # passed is delegated to _key_path_, the preceding path argument
302
- # is delegated to _per_arrelement_path_, the path preceding that
303
- # argument (if present, "" by default) is delegated to
304
- # _base_path_. The meaning of the keyword arguments is the same
305
- # as for ObjectNode.
306
- def initialize_impl(path1,path2,path3=nil)
307
- super
308
- if path3
309
- do_initialize(path1,path2,path3)
310
- else
311
- do_initialize("",path1,path2)
312
- end
313
- end
314
- # "Real" initializer.
315
- def do_initialize(base_path,per_hashelement_path,key_path)
316
- per_hashelement_path=per_hashelement_path[1..-1] if per_hashelement_path[0]==?/
317
- @base_path = XML::XXPath.new(base_path)
318
- @per_hashelement_path = XML::XXPath.new(per_hashelement_path)
319
- @key_path = XML::XXPath.new(key_path)
320
- @reader_path = XML::XXPath.new(base_path+"/"+per_hashelement_path)
321
+ # Initializer. Called with keyword arguments and either 2 or 3
322
+ # paths; the hindmost path argument passed is delegated to
323
+ # _key_path_, the preceding path argument is delegated to
324
+ # _per_arrelement_path_, the path preceding that argument (if
325
+ # present, "" by default) is delegated to _base_path_. The
326
+ # meaning of the keyword arguments is the same as for
327
+ # ObjectNode.
328
+ def initialize(*args)
329
+ path1,path2,path3,*args = super(*args)
330
+ base_path,per_hashelement_path,key_path = if path3
331
+ [path1,path2,path3]
332
+ else
333
+ ["",path1,path2]
334
+ end
335
+ per_hashelement_path=per_hashelement_path[1..-1] if per_hashelement_path[0]==?/
336
+ @base_path = XML::XXPath.new(base_path)
337
+ @per_hashelement_path = XML::XXPath.new(per_hashelement_path)
338
+ @key_path = XML::XXPath.new(key_path)
339
+ @reader_path = XML::XXPath.new(base_path+"/"+per_hashelement_path)
340
+ args
321
341
  end
322
342
  def extract_attr_value(xml) # :nodoc:
323
343
  result = {}
324
344
  default_when_xpath_err{@reader_path.all(xml)}.each do |elt|
325
345
  key = @key_path.first(elt).text
326
- value = @options[:unmarshaller].call(elt)
346
+ value = @unmarshaller.call(elt)
327
347
  result[key] = value
328
348
  end
329
349
  result
330
350
  end
331
351
  def set_attr_value(xml, value) # :nodoc:
332
- base_elt = @base_path.first(xml,:ensure_created=>true)
333
- value.each_pair do |k,v|
352
+ base_elt = @base_path.first(xml,:ensure_created=>true)
353
+ value.each_pair do |k,v|
334
354
  elt = @per_hashelement_path.create_new(base_elt)
335
- @options[:marshaller].call(elt,v)
355
+ @marshaller.call(elt,v)
336
356
  @key_path.first(elt,:ensure_created=>true).text = k
337
- end
357
+ end
358
+ end
359
+ end
360
+
361
+
362
+ class ChoiceNode < Node
363
+
364
+ def initialize(*args)
365
+ args = super(*args)
366
+ @choices = []
367
+ path=nil
368
+ args.each do |arg|
369
+ next if [:if,:then,:elsif].include? arg
370
+ if path.nil?
371
+ path = (if [:else,:default,:otherwise].include? arg
372
+ :else
373
+ else
374
+ XML::XXPath.new arg
375
+ end)
376
+ else
377
+ raise XML::MappingError, "node expected, found: #{arg.inspect}" unless Node===arg
378
+ @choices << [path,arg]
379
+
380
+ # undo what the node factory fcn did -- ugly ugly! would
381
+ # need some way to lazy-evaluate arg (a proc would be
382
+ # simple but ugly for the user), and then use some
383
+ # mechanism (a flag with dynamic scope probably) to tell
384
+ # the node factory fcn not to add the node to the
385
+ # xml_mapping_nodes
386
+ @owner.xml_mapping_nodes(:mapping=>@mapping).delete arg
387
+ path=nil
388
+ end
389
+ end
390
+
391
+ raise XML::MappingError, "node missing at end of argument list" unless path.nil?
392
+ raise XML::MappingError, "no choices were supplied" if @choices.empty?
393
+
394
+ []
395
+ end
396
+
397
+ def xml_to_obj(obj,xml)
398
+ @choices.each do |path,node|
399
+ if path==:else or not(path.all(xml).empty?)
400
+ node.xml_to_obj(obj,xml)
401
+ return true
402
+ end
403
+ end
404
+ raise XML::MappingError, "xml_to_obj: no choice matched in: #{xml}"
405
+ end
406
+
407
+ def obj_to_xml(obj,xml)
408
+ @choices.each do |path,node|
409
+ if node.is_present_in? obj
410
+ node.obj_to_xml(obj,xml)
411
+ path.first(xml, :ensure_created=>true)
412
+ return true
413
+ end
414
+ end
415
+ # @choices[0][1].obj_to_xml(obj,xml)
416
+ raise XML::MappingError, "obj_to_xml: no choice present in object: #{obj.inspect}"
417
+ end
418
+
419
+ def obj_initializing(obj,mapping)
420
+ @choices[0][1].obj_initializing(obj,mapping)
421
+ end
422
+
423
+ # (overridden) true if at least one of our nodes is_present_in?
424
+ # obj.
425
+ def is_present_in? obj
426
+ # TODO: use Enumerable#any?
427
+ @choices.inject(false){|prev,(path,node)| prev or node.is_present_in?(obj)}
338
428
  end
339
429
  end
340
430
 
@@ -1,8 +1,8 @@
1
1
  # xml-mapping -- bidirectional Ruby-XML mapper
2
- # Copyright (C) 2004,2005 Olaf Klischat
2
+ # Copyright (C) 2004-2010 Olaf Klischat
3
3
 
4
4
  module XML
5
5
  module Mapping
6
- VERSION = '0.8.1'
6
+ VERSION = '0.9.1'
7
7
  end
8
8
  end
@@ -0,0 +1,186 @@
1
+ # xxpath -- XPath implementation for Ruby, including write access
2
+ # Copyright (C) 2004-2010 Olaf Klischat
3
+
4
+ require 'rexml/document'
5
+
6
+ module XML
7
+
8
+ class XXPath
9
+ module Accessors #:nodoc:
10
+
11
+ # we need a boolean "unspecified?" attribute for XML nodes --
12
+ # paths like "*" oder (somewhen) "foo|bar" create "unspecified"
13
+ # nodes that the user must then "specify" by setting their text
14
+ # etc. (or manually setting unspecified=false)
15
+ #
16
+ # This is mixed into the REXML::Element and
17
+ # XML::XXPath::Accessors::Attribute classes.
18
+ module UnspecifiednessSupport
19
+
20
+ def unspecified?
21
+ @xml_xpath_unspecified ||= false
22
+ end
23
+
24
+ def unspecified=(x)
25
+ @xml_xpath_unspecified = x
26
+ end
27
+
28
+ def self.append_features(base)
29
+ return if base.included_modules.include? self # avoid aliasing methods more than once
30
+ # (would lead to infinite recursion)
31
+ super
32
+ base.module_eval <<-EOS
33
+ alias_method :_text_orig, :text
34
+ alias_method :_textis_orig, :text=
35
+ def text
36
+ # we're suffering from the "fragile base class"
37
+ # phenomenon here -- we don't know whether the
38
+ # implementation of the class we get mixed into always
39
+ # calls text (instead of just accessing @text or so)
40
+ if unspecified?
41
+ "[UNSPECIFIED]"
42
+ else
43
+ _text_orig
44
+ end
45
+ end
46
+ def text=(x)
47
+ _textis_orig(x)
48
+ self.unspecified=false
49
+ end
50
+
51
+ alias_method :_nameis_orig, :name=
52
+ def name=(x)
53
+ _nameis_orig(x)
54
+ self.unspecified=false
55
+ end
56
+ EOS
57
+ end
58
+
59
+ end
60
+
61
+ class REXML::Element #:nodoc:
62
+ include UnspecifiednessSupport
63
+ end
64
+
65
+ # attribute node, more or less call-compatible with REXML's
66
+ # Element. REXML's Attribute class doesn't provide this...
67
+ #
68
+ # The all/first calls return instances of this class if they
69
+ # matched an attribute node.
70
+ class Attribute
71
+ attr_reader :parent, :name
72
+ attr_writer :name
73
+
74
+ def initialize(parent,name)
75
+ @parent,@name = parent,name
76
+ end
77
+
78
+ def self.new(parent,name,create)
79
+ if parent.attributes[name]
80
+ super(parent,name)
81
+ else
82
+ if create
83
+ parent.attributes[name] = "[unset]"
84
+ super(parent,name)
85
+ else
86
+ nil
87
+ end
88
+ end
89
+ end
90
+
91
+ # the value of the attribute.
92
+ def text
93
+ parent.attributes[@name]
94
+ end
95
+
96
+ def text=(x)
97
+ parent.attributes[@name] = x
98
+ end
99
+
100
+ def ==(other)
101
+ other.kind_of?(Attribute) and other.parent==parent and other.name==name
102
+ end
103
+
104
+ include UnspecifiednessSupport
105
+ end
106
+ end
107
+
108
+ end
109
+
110
+ end
111
+
112
+
113
+
114
+
115
+
116
+
117
+ class REXML::Parent
118
+ def each_on_axis_child
119
+ if respond_to? :attributes
120
+ attributes.each_key do |name|
121
+ yield XML::XXPath::Accessors::Attribute.new(self, name, false)
122
+ end
123
+ end
124
+ each_child do |c|
125
+ yield c
126
+ end
127
+ end
128
+
129
+ def each_on_axis_descendant(&block)
130
+ each_on_axis_child do |c|
131
+ block.call c
132
+ if REXML::Parent===c
133
+ c.each_on_axis_descendant(&block)
134
+ end
135
+ end
136
+ end
137
+
138
+ def each_on_axis_self
139
+ yield self
140
+ end
141
+
142
+ def each_on_axis(axis, &block)
143
+ send :"each_on_axis_#{axis}", &block
144
+ end
145
+ end
146
+
147
+
148
+ ## hotfix for REXML bug #128 -- see http://trac.germane-software.com/rexml/ticket/128
149
+ # a working Element#write is required by several tests and
150
+ # documentation code snippets
151
+ begin
152
+ # temporarily suppress warnings
153
+ class <<Kernel
154
+ alias_method :old_warn, :warn
155
+ def warn(msg)
156
+ end
157
+ end
158
+ begin
159
+ # detect bug
160
+ REXML::Element.new.write("",2)
161
+ ensure
162
+ # unsuppress
163
+ class <<Kernel
164
+ alias_method :warn, :old_warn
165
+ end
166
+ end
167
+ rescue NameError
168
+ # bug is present -- fix it. I use Element#write in numerous tests and rdoc
169
+ # inline code snippets. TODO: switch to REXML::Formatters there sometime.
170
+ class REXML::Element
171
+ def write(output=$stdout, indent=-1, transitive=false, ie_hack=false)
172
+ Kernel.warn("#{self.class.name}.write is deprecated. See REXML::Formatters")
173
+ formatter = if indent > -1
174
+ if transitive
175
+ require "rexml/formatters/transitive"
176
+ REXML::Formatters::Transitive.new( indent, ie_hack )
177
+ else
178
+ REXML::Formatters::Pretty.new( indent, ie_hack )
179
+ end
180
+ else
181
+ REXML::Formatters::Default.new( ie_hack )
182
+ end
183
+ formatter.write( self, output )
184
+ end
185
+ end
186
+ end