roxml 2.4.1 → 2.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +21 -0
- data/MIT-LICENSE +1 -1
- data/Manifest.txt +27 -6
- data/Rakefile +2 -2
- data/TODO +35 -14
- data/config/website.yml +2 -0
- data/examples/amazon.rb +33 -0
- data/examples/current_weather.rb +27 -0
- data/examples/dashed_elements.rb +20 -0
- data/examples/posts.rb +27 -0
- data/examples/twitter.rb +37 -0
- data/examples/xml/amazon.xml +133 -0
- data/examples/xml/current_weather.xml +89 -0
- data/examples/xml/dashed_elements.xml +52 -0
- data/examples/xml/posts.xml +23 -0
- data/examples/xml/twitter.xml +422 -0
- data/lib/roxml.rb +35 -6
- data/lib/roxml/{options.rb → definition.rb} +60 -88
- data/lib/roxml/extensions.rb +3 -0
- data/lib/roxml/hash_definition.rb +59 -0
- data/lib/roxml/xml.rb +15 -272
- data/lib/roxml/xml/{libxml.rb → parsers/libxml.rb} +13 -5
- data/lib/roxml/xml/{rexml.rb → parsers/rexml.rb} +13 -4
- data/lib/roxml/xml/references.rb +290 -0
- data/roxml.gemspec +4 -4
- data/spec/examples/amazon_spec.rb +53 -0
- data/spec/examples/current_weather_spec.rb +37 -0
- data/spec/examples/dashed_elements_spec.rb +20 -0
- data/spec/examples/post_spec.rb +24 -0
- data/spec/examples/twitter_spec.rb +32 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +16 -0
- data/tasks/rspec.rake +21 -0
- data/test/unit/definition_test.rb +160 -0
- data/test/unit/inheritance_test.rb +22 -1
- data/test/unit/roxml_test.rb +30 -1
- data/test/unit/xml_name_test.rb +29 -0
- data/test/unit/xml_namespace_test.rb +38 -1
- data/website/index.html +98 -0
- metadata +30 -9
- data/html/index.html +0 -278
- data/html/style.css +0 -79
- data/test/unit/options_test.rb +0 -103
data/lib/roxml.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$LOAD_PATH.include?(File.dirname(__FILE__)) || $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
%w(extensions definition xml).each do |file|
|
5
|
+
require File.join('roxml', file)
|
3
6
|
end
|
4
7
|
|
5
8
|
module ROXML # :nodoc:
|
6
|
-
VERSION = '2.4.
|
9
|
+
VERSION = '2.4.2'
|
7
10
|
|
8
11
|
def self.included(base) # :nodoc:
|
9
12
|
base.extend ClassMethods::Accessors,
|
@@ -108,6 +111,28 @@ module ROXML # :nodoc:
|
|
108
111
|
@roxml_tag_name = name
|
109
112
|
end
|
110
113
|
|
114
|
+
# Sets the namemespace for attributes and elements of this class. You can override
|
115
|
+
# this value on individual elements via the :from option
|
116
|
+
#
|
117
|
+
# Example:
|
118
|
+
# class Book
|
119
|
+
# xml_namespace :aws
|
120
|
+
#
|
121
|
+
# xml_reader :default_namespace
|
122
|
+
# xml_reader :different_namespace, :from => 'different:namespace'
|
123
|
+
# xml_reader :no_namespace, :from => ':no_namespace'
|
124
|
+
# end
|
125
|
+
#
|
126
|
+
# <aws:book xmlns:aws="http://www.aws.com/aws" xmlns:different="http://www.aws.com/different">
|
127
|
+
# <aws:default_namespace>value</aws:default_namespace>
|
128
|
+
# <different:namespace>value</different:namespace>
|
129
|
+
# <no_namespace>value</no_namespace>
|
130
|
+
# </aws:book>
|
131
|
+
#
|
132
|
+
def xml_namespace(namespace)
|
133
|
+
@roxml_namespace = namespace.to_s
|
134
|
+
end
|
135
|
+
|
111
136
|
# Most xml documents have a consistent naming convention, for example, the node and
|
112
137
|
# and attribute names might appear in CamelCase. xml_convention enables you to adapt
|
113
138
|
# the roxml default names for this object to suit this convention. For example,
|
@@ -410,14 +435,14 @@ module ROXML # :nodoc:
|
|
410
435
|
#
|
411
436
|
# == Other options
|
412
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.
|
413
|
-
# [:as] :cdata for character data;
|
438
|
+
# [:as] one or more of the following: :cdata for character data; Integer, Float, Date, Time or DateTime to coerce to the respective type
|
414
439
|
# [:in] An optional name of a wrapping tag for this XML accessor
|
415
440
|
# [:else] Default value for attribute, if missing
|
416
441
|
# [:required] If true, throws RequiredElementMissing when the element isn't present
|
417
442
|
# [:frozen] If true, all results are frozen (using #freeze) at parse-time.
|
418
443
|
#
|
419
444
|
def xml_reference(writable, sym, type_and_or_opts = :text, opts = nil, &block)
|
420
|
-
opts =
|
445
|
+
opts = Definition.new(sym, *[type_and_or_opts, opts].compact, &block)
|
421
446
|
|
422
447
|
add_accessor(opts, writable)
|
423
448
|
end
|
@@ -512,6 +537,10 @@ module ROXML # :nodoc:
|
|
512
537
|
@roxml_tag_name || superclass.try(:roxml_tag_name)
|
513
538
|
end
|
514
539
|
|
540
|
+
def roxml_namespace # :nodoc:
|
541
|
+
@roxml_namespace || superclass.try(:roxml_namespace)
|
542
|
+
end
|
543
|
+
|
515
544
|
# Returns array of internal reference objects, such as attributes
|
516
545
|
# and composed XML objects
|
517
546
|
def roxml_attrs
|
@@ -544,7 +573,7 @@ module ROXML # :nodoc:
|
|
544
573
|
# See also: xml_initialize
|
545
574
|
#
|
546
575
|
def from_xml(data, *initialization_args)
|
547
|
-
xml =
|
576
|
+
xml = XML::Node.from(data)
|
548
577
|
|
549
578
|
unless xml_construction_args_without_deprecation.empty?
|
550
579
|
args = xml_construction_args_without_deprecation.map do |arg|
|
@@ -1,63 +1,7 @@
|
|
1
|
-
|
2
|
-
HASH_KEYS = [:attrs, :key, :value].freeze
|
3
|
-
TYPE_KEYS = [:attr, :text, :hash, :content].freeze
|
4
|
-
|
5
|
-
class HashDesc # :nodoc:
|
6
|
-
attr_reader :key, :value, :wrapper
|
7
|
-
|
8
|
-
def initialize(opts, wrapper)
|
9
|
-
unless (invalid_keys = opts.keys - HASH_KEYS).empty?
|
10
|
-
raise ArgumentError, "Invalid Hash description keys: #{invalid_keys.join(', ')}"
|
11
|
-
end
|
12
|
-
|
13
|
-
@wrapper = wrapper
|
14
|
-
if opts.has_key? :attrs
|
15
|
-
@key = to_hash_args(opts, :attr, opts[:attrs][0])
|
16
|
-
@value = to_hash_args(opts, :attr, opts[:attrs][1])
|
17
|
-
else
|
18
|
-
@key = to_hash_args opts, *fetch_element(opts, :key)
|
19
|
-
@value = to_hash_args opts, *fetch_element(opts, :value)
|
20
|
-
end
|
21
|
-
end
|
1
|
+
require File.join(File.dirname(__FILE__), 'hash_definition')
|
22
2
|
|
23
|
-
|
24
|
-
|
25
|
-
case opts[what]
|
26
|
-
when Hash
|
27
|
-
raise ArgumentError, "Hash #{what} is over-specified: #{opts[what].inspect}" unless opts[what].keys.one?
|
28
|
-
type = opts[what].keys.first
|
29
|
-
[type, opts[what][type]]
|
30
|
-
when :content
|
31
|
-
[:content, opts[:name]]
|
32
|
-
when :name
|
33
|
-
[:name, '*']
|
34
|
-
when String
|
35
|
-
[:text, opts[what]]
|
36
|
-
when Symbol
|
37
|
-
[:text, opts[what]]
|
38
|
-
else
|
39
|
-
raise ArgumentError, "unrecognized hash parameter: #{what} => #{opts[what]}"
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def to_hash_args(args, type, name)
|
44
|
-
args = [args] unless args.is_a? Array
|
45
|
-
|
46
|
-
if args.one? && !(args.first.keys & HASH_KEYS).empty?
|
47
|
-
opts = {type => name}
|
48
|
-
if type == :content
|
49
|
-
opts[:type] = :text
|
50
|
-
(opts[:as] ||= []) << :content
|
51
|
-
end
|
52
|
-
Opts.new(name, opts)
|
53
|
-
else
|
54
|
-
opts = args.extract_options!
|
55
|
-
raise opts.inspect
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
class Opts # :nodoc:
|
3
|
+
module ROXML
|
4
|
+
class Definition # :nodoc:
|
61
5
|
attr_reader :name, :type, :hash, :blocks, :accessor, :to_xml
|
62
6
|
|
63
7
|
class << self
|
@@ -75,6 +19,7 @@ module ROXML
|
|
75
19
|
@opts = extract_options!(args)
|
76
20
|
@default = @opts.delete(:else)
|
77
21
|
@to_xml = @opts.delete(:to_xml)
|
22
|
+
@name_explicit = @opts.has_key?(:from)
|
78
23
|
|
79
24
|
if @opts.has_key?(:readonly)
|
80
25
|
raise ArgumentError, "There is no 'readonly' option. You probably mean to use :frozen => true"
|
@@ -114,7 +59,7 @@ module ROXML
|
|
114
59
|
end
|
115
60
|
|
116
61
|
def hash
|
117
|
-
@hash ||=
|
62
|
+
@hash ||= HashDefinition.new(@opts.delete(:hash), name) if hash?
|
118
63
|
end
|
119
64
|
|
120
65
|
def hash?
|
@@ -125,6 +70,10 @@ module ROXML
|
|
125
70
|
@name == '*'
|
126
71
|
end
|
127
72
|
|
73
|
+
def name_explicit?
|
74
|
+
@name_explicit
|
75
|
+
end
|
76
|
+
|
128
77
|
def content?
|
129
78
|
@type == :content
|
130
79
|
end
|
@@ -167,57 +116,80 @@ module ROXML
|
|
167
116
|
end
|
168
117
|
|
169
118
|
private
|
119
|
+
def self.all(items, &block)
|
120
|
+
array = items.is_a?(Array)
|
121
|
+
results = (array ? items : [items]).map do |item|
|
122
|
+
yield item
|
123
|
+
end
|
124
|
+
|
125
|
+
array ? results : results.first
|
126
|
+
end
|
127
|
+
|
170
128
|
BLOCK_TO_FLOAT = lambda do |val|
|
171
|
-
|
172
|
-
|
173
|
-
Float(v)
|
174
|
-
end
|
175
|
-
else
|
176
|
-
Float(val)
|
129
|
+
all(val) do |v|
|
130
|
+
Float(v) unless blank_string?(v)
|
177
131
|
end
|
178
132
|
end
|
179
133
|
|
180
134
|
BLOCK_TO_INT = lambda do |val|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
135
|
+
all(val) do |v|
|
136
|
+
Integer(v) unless blank_string?(v)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.fetch_bool(value, default)
|
141
|
+
value = value.try(:downcase)
|
142
|
+
if %w{true yes 1}.include? value
|
143
|
+
true
|
144
|
+
elsif %w{false no 0}.include? value
|
145
|
+
false
|
185
146
|
else
|
186
|
-
|
147
|
+
default
|
187
148
|
end
|
188
149
|
end
|
189
150
|
|
190
|
-
|
191
|
-
|
151
|
+
def self.blank_string?(value)
|
152
|
+
value.is_a?(String) && value.blank?
|
153
|
+
end
|
192
154
|
|
193
155
|
BLOCK_SHORTHANDS = {
|
194
|
-
:integer => BLOCK_TO_INT,
|
156
|
+
:integer => BLOCK_TO_INT, # deprecated
|
195
157
|
Integer => BLOCK_TO_INT,
|
196
|
-
:float => BLOCK_TO_FLOAT,
|
158
|
+
:float => BLOCK_TO_FLOAT, # deprecated
|
197
159
|
Float => BLOCK_TO_FLOAT,
|
160
|
+
Date => lambda do |val|
|
161
|
+
if defined?(Date)
|
162
|
+
all(val) {|v| Date.parse(v) unless blank_string?(v) }
|
163
|
+
end
|
164
|
+
end,
|
165
|
+
DateTime => lambda do |val|
|
166
|
+
if defined?(DateTime)
|
167
|
+
all(val) {|v| DateTime.parse(v) unless blank_string?(v) }
|
168
|
+
end
|
169
|
+
end,
|
170
|
+
Time => lambda do |val|
|
171
|
+
if defined?(Time)
|
172
|
+
all(val) {|v| Time.parse(v) unless blank_string?(v) }
|
173
|
+
end
|
174
|
+
end,
|
175
|
+
|
198
176
|
:bool => nil,
|
199
177
|
:bool_standalone => lambda do |val|
|
200
|
-
|
201
|
-
|
202
|
-
elsif FALSE_VALS.include? val
|
203
|
-
false
|
204
|
-
else
|
205
|
-
nil
|
178
|
+
all(val) do |v|
|
179
|
+
fetch_bool(v, nil)
|
206
180
|
end
|
207
181
|
end,
|
208
|
-
|
209
182
|
:bool_combined => lambda do |val|
|
210
|
-
|
211
|
-
|
212
|
-
elsif FALSE_VALS.include? val
|
213
|
-
false
|
214
|
-
else
|
215
|
-
val
|
183
|
+
all(val) do |v|
|
184
|
+
fetch_bool(v, v)
|
216
185
|
end
|
217
186
|
end
|
218
187
|
}
|
219
188
|
|
220
189
|
def collect_blocks(block, as)
|
190
|
+
ActiveSupport::Deprecation.warn ":as => :float is deprecated. Use :as => Float instead" if as.include?(:float)
|
191
|
+
ActiveSupport::Deprecation.warn ":as => :integer is deprecated. Use :as => Integer instead" if as.include?(:integer)
|
192
|
+
|
221
193
|
shorthands = as & BLOCK_SHORTHANDS.keys
|
222
194
|
if shorthands.size > 1
|
223
195
|
raise ArgumentError, "multiple block shorthands supplied #{shorthands.map(&:to_s).join(', ')}"
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module ROXML
|
2
|
+
HASH_KEYS = [:attrs, :key, :value].freeze
|
3
|
+
TYPE_KEYS = [:attr, :text, :hash, :content].freeze
|
4
|
+
|
5
|
+
class HashDefinition # :nodoc:
|
6
|
+
attr_reader :key, :value, :wrapper
|
7
|
+
|
8
|
+
def initialize(opts, wrapper)
|
9
|
+
unless (invalid_keys = opts.keys - HASH_KEYS).empty?
|
10
|
+
raise ArgumentError, "Invalid Hash description keys: #{invalid_keys.join(', ')}"
|
11
|
+
end
|
12
|
+
|
13
|
+
@wrapper = wrapper
|
14
|
+
if opts.has_key? :attrs
|
15
|
+
@key = to_hash_args(opts, :attr, opts[:attrs][0])
|
16
|
+
@value = to_hash_args(opts, :attr, opts[:attrs][1])
|
17
|
+
else
|
18
|
+
@key = to_hash_args opts, *fetch_element(opts, :key)
|
19
|
+
@value = to_hash_args opts, *fetch_element(opts, :value)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def fetch_element(opts, what)
|
25
|
+
case opts[what]
|
26
|
+
when Hash
|
27
|
+
raise ArgumentError, "Hash #{what} is over-specified: #{opts[what].inspect}" unless opts[what].keys.one?
|
28
|
+
type = opts[what].keys.first
|
29
|
+
[type, opts[what][type]]
|
30
|
+
when :content
|
31
|
+
[:content, opts[:name]]
|
32
|
+
when :name
|
33
|
+
[:name, '*']
|
34
|
+
when String
|
35
|
+
[:text, opts[what]]
|
36
|
+
when Symbol
|
37
|
+
[:text, opts[what]]
|
38
|
+
else
|
39
|
+
raise ArgumentError, "unrecognized hash parameter: #{what} => #{opts[what]}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_hash_args(args, type, name)
|
44
|
+
args = [args] unless args.is_a? Array
|
45
|
+
|
46
|
+
if args.one? && !(args.first.keys & HASH_KEYS).empty?
|
47
|
+
opts = {type => name}
|
48
|
+
if type == :content
|
49
|
+
opts[:type] = :text
|
50
|
+
(opts[:as] ||= []) << :content
|
51
|
+
end
|
52
|
+
Definition.new(name, opts)
|
53
|
+
else
|
54
|
+
opts = args.extract_options!
|
55
|
+
raise opts.inspect
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/roxml/xml.rb
CHANGED
@@ -7,282 +7,25 @@ module ROXML
|
|
7
7
|
XML_PARSER = 'rexml' # :nodoc:
|
8
8
|
end
|
9
9
|
end
|
10
|
-
require File.join(File.dirname(__FILE__), 'xml', XML_PARSER)
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
def to_xml
|
27
|
-
val = @instance.__send__(accessor)
|
28
|
-
opts.to_xml.respond_to?(:call) ? opts.to_xml.call(val) : val
|
29
|
-
end
|
30
|
-
|
31
|
-
def update_xml(xml, value)
|
32
|
-
returning wrap(xml) do |xml|
|
33
|
-
write_xml(xml, value)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def name
|
38
|
-
conventionize(opts.name)
|
39
|
-
end
|
40
|
-
alias_method :xpath_name, :name
|
41
|
-
|
42
|
-
def default
|
43
|
-
@default ||= @opts.default || (@opts.array? ? Array.new : nil)
|
44
|
-
@default.duplicable? ? @default.dup : @default
|
45
|
-
end
|
46
|
-
|
47
|
-
def value_in(xml)
|
48
|
-
value = fetch_value(xml)
|
49
|
-
freeze(apply_blocks(value))
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
attr_reader :opts
|
54
|
-
|
55
|
-
def conventionize(what)
|
56
|
-
if !what.blank? && @instance.try(:class).try(:roxml_naming_convention).respond_to?(:call)
|
57
|
-
@instance.class.roxml_naming_convention.call(what)
|
58
|
-
else
|
59
|
-
what
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def wrapper
|
64
|
-
conventionize(opts.wrapper)
|
65
|
-
end
|
66
|
-
|
67
|
-
def apply_blocks(val)
|
68
|
-
blocks.apply_to(val)
|
69
|
-
end
|
70
|
-
|
71
|
-
def freeze(val)
|
72
|
-
if opts.freeze?
|
73
|
-
val.each(&:freeze) if val.is_a?(Enumerable)
|
74
|
-
val.freeze
|
75
|
-
else
|
76
|
-
val
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def xpath
|
81
|
-
wrapper ? "#{wrapper}/#{xpath_name}" : xpath_name.to_s
|
82
|
-
end
|
83
|
-
|
84
|
-
def auto_xpath
|
85
|
-
"#{conventionize(opts.name.pluralize)}/#{xpath_name}" if array?
|
86
|
-
end
|
87
|
-
|
88
|
-
def wrap(xml)
|
89
|
-
return xml if !wrapper || xml.name == wrapper
|
90
|
-
if child = xml.children.find {|c| c.name == wrapper }
|
91
|
-
return child
|
92
|
-
end
|
93
|
-
xml.child_add(XML::Node.new_element(wrapper))
|
94
|
-
end
|
95
|
-
|
96
|
-
def nodes_in(xml)
|
97
|
-
vals = xml.search(xpath)
|
98
|
-
|
99
|
-
if (opts.hash? || opts.array?) && vals.empty? && !wrapper && auto_xpath
|
100
|
-
vals = xml.search(auto_xpath)
|
101
|
-
@auto_vals = !vals.empty?
|
102
|
-
end
|
103
|
-
|
104
|
-
if vals.empty?
|
105
|
-
raise RequiredElementMissing, "#{name} from #{xml} for #{accessor}" if required?
|
106
|
-
default
|
107
|
-
else
|
108
|
-
yield(vals)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
# Interal class representing an XML attribute binding
|
114
|
-
#
|
115
|
-
# In context:
|
116
|
-
# <element attribute="XMLAttributeRef">
|
117
|
-
# XMLTextRef
|
118
|
-
# </element>
|
119
|
-
class XMLAttributeRef < XMLRef # :nodoc:
|
120
|
-
private
|
121
|
-
# Updates the attribute in the given XML block to
|
122
|
-
# the value provided.
|
123
|
-
def write_xml(xml, value)
|
124
|
-
xml.attributes[name] = value.to_s.to_utf
|
125
|
-
end
|
126
|
-
|
127
|
-
def fetch_value(xml)
|
128
|
-
nodes_in(xml) do |nodes|
|
129
|
-
nodes.first.value
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
def xpath_name
|
134
|
-
"@#{name}"
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
# Interal class representing XML content text binding
|
139
|
-
#
|
140
|
-
# In context:
|
141
|
-
# <element attribute="XMLAttributeRef">
|
142
|
-
# XMLTextRef
|
143
|
-
# </element>
|
144
|
-
class XMLTextRef < XMLRef # :nodoc:
|
145
|
-
delegate :cdata?, :content?, :name?, :to => :opts
|
146
|
-
|
147
|
-
private
|
148
|
-
# Updates the text in the given _xml_ block to
|
149
|
-
# the _value_ provided.
|
150
|
-
def write_xml(xml, value)
|
151
|
-
if content?
|
152
|
-
add(xml, value)
|
153
|
-
elsif name?
|
154
|
-
xml.name = value
|
155
|
-
elsif array?
|
156
|
-
value.each do |v|
|
157
|
-
add(xml.child_add(XML::Node.new_element(name)), v)
|
158
|
-
end
|
159
|
-
else
|
160
|
-
add(xml.child_add(XML::Node.new_element(name)), value)
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
def fetch_value(xml)
|
165
|
-
if content? || name?
|
166
|
-
value =
|
167
|
-
if content?
|
168
|
-
xml.content.to_s.strip
|
169
|
-
elsif name?
|
170
|
-
xml.name
|
171
|
-
end
|
172
|
-
|
173
|
-
if value.empty?
|
174
|
-
raise RequiredElementMissing, "#{name} from #{xml} for #{accessor}" if required?
|
175
|
-
default
|
11
|
+
require File.join(File.dirname(__FILE__), 'xml/parsers', XML_PARSER)
|
12
|
+
|
13
|
+
module XML
|
14
|
+
class Node
|
15
|
+
def self.from(data)
|
16
|
+
if data.is_a?(XML::Node)
|
17
|
+
data
|
18
|
+
elsif data.is_a?(File) || data.is_a?(IO)
|
19
|
+
Parser.parse_io(data).root
|
20
|
+
elsif (defined?(URI) && data.is_a?(URI::Generic)) ||
|
21
|
+
(defined?(Pathname) && data.is_a?(Pathname))
|
22
|
+
Parser.parse_file(data.to_s).root
|
176
23
|
else
|
177
|
-
|
178
|
-
end
|
179
|
-
else
|
180
|
-
nodes_in(xml) do |nodes|
|
181
|
-
if array?
|
182
|
-
nodes.collect do |e|
|
183
|
-
e.content.strip.to_latin
|
184
|
-
end
|
185
|
-
else
|
186
|
-
nodes.first.content
|
187
|
-
end
|
24
|
+
Parser.parse(data).root
|
188
25
|
end
|
189
26
|
end
|
190
27
|
end
|
191
|
-
|
192
|
-
def add(dest, value)
|
193
|
-
if cdata?
|
194
|
-
dest.child_add(XML::Node.new_cdata(value.to_s.to_utf))
|
195
|
-
else
|
196
|
-
dest.content = value.to_s.to_utf
|
197
|
-
end
|
198
|
-
end
|
199
28
|
end
|
29
|
+
end
|
200
30
|
|
201
|
-
|
202
|
-
delegate :hash, :to => :opts
|
203
|
-
|
204
|
-
def initialize(opts, inst)
|
205
|
-
super(opts, inst)
|
206
|
-
@key = opts.hash.key.to_ref(inst)
|
207
|
-
@value = opts.hash.value.to_ref(inst)
|
208
|
-
end
|
209
|
-
|
210
|
-
private
|
211
|
-
# Updates the composed XML object in the given XML block to
|
212
|
-
# the value provided.
|
213
|
-
def write_xml(xml, value)
|
214
|
-
value.each_pair do |k, v|
|
215
|
-
node = xml.child_add(XML::Node.new_element(hash.wrapper))
|
216
|
-
@key.update_xml(node, k)
|
217
|
-
@value.update_xml(node, v)
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
def fetch_value(xml)
|
222
|
-
nodes_in(xml) do |nodes|
|
223
|
-
nodes.collect do |e|
|
224
|
-
[@key.value_in(e), @value.value_in(e)]
|
225
|
-
end
|
226
|
-
end
|
227
|
-
end
|
228
|
-
|
229
|
-
def apply_blocks(vals)
|
230
|
-
unless blocks.empty?
|
231
|
-
vals.collect! do |kvp|
|
232
|
-
super(kvp)
|
233
|
-
end
|
234
|
-
end
|
235
|
-
vals.to_hash if vals
|
236
|
-
end
|
237
|
-
|
238
|
-
def freeze(vals)
|
239
|
-
if opts.freeze?
|
240
|
-
vals.each_pair{|k, v| k.freeze; v.freeze }
|
241
|
-
vals.freeze
|
242
|
-
else
|
243
|
-
vals
|
244
|
-
end
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
|
-
class XMLObjectRef < XMLTextRef # :nodoc:
|
249
|
-
delegate :type, :to => :opts
|
250
|
-
|
251
|
-
private
|
252
|
-
# Updates the composed XML object in the given XML block to
|
253
|
-
# the value provided.
|
254
|
-
def write_xml(xml, value)
|
255
|
-
if array?
|
256
|
-
value.each do |v|
|
257
|
-
xml.child_add(v.to_xml(name))
|
258
|
-
end
|
259
|
-
elsif value.is_a?(ROXML)
|
260
|
-
xml.child_add(value.to_xml(name))
|
261
|
-
else
|
262
|
-
node = XML::Node.new_element(name)
|
263
|
-
node.content = value.to_xml
|
264
|
-
xml.child_add(node)
|
265
|
-
end
|
266
|
-
end
|
267
|
-
|
268
|
-
def fetch_value(xml)
|
269
|
-
nodes_in(xml) do |nodes|
|
270
|
-
unless array?
|
271
|
-
instantiate(nodes.first)
|
272
|
-
else
|
273
|
-
nodes.collect do |e|
|
274
|
-
instantiate(e)
|
275
|
-
end
|
276
|
-
end
|
277
|
-
end
|
278
|
-
end
|
279
|
-
|
280
|
-
def instantiate(elem)
|
281
|
-
if type.respond_to? :from_xml
|
282
|
-
type.from_xml(elem)
|
283
|
-
else
|
284
|
-
type.new(elem)
|
285
|
-
end
|
286
|
-
end
|
287
|
-
end
|
288
|
-
end
|
31
|
+
require File.join(File.dirname(__FILE__), 'xml/references')
|