roxml 2.5.3 → 3.1.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/.gitignore +7 -0
- data/.gitmodules +3 -0
- data/History.txt +38 -1
- data/README.rdoc +8 -5
- data/Rakefile +35 -36
- data/TODO +12 -35
- data/VERSION +1 -0
- data/examples/amazon.rb +8 -6
- data/examples/posts.rb +1 -1
- data/examples/{active_record.rb → rails.rb} +2 -2
- data/examples/twitter.rb +1 -1
- data/lib/roxml.rb +86 -151
- data/lib/roxml/definition.rb +64 -152
- data/lib/roxml/hash_definition.rb +5 -40
- data/lib/roxml/xml.rb +12 -9
- data/lib/roxml/xml/parsers/libxml.rb +22 -17
- data/lib/roxml/xml/parsers/nokogiri.rb +77 -0
- data/lib/roxml/xml/references.rb +66 -57
- data/roxml.gemspec +170 -19
- data/spec/definition_spec.rb +121 -198
- data/spec/examples/active_record_spec.rb +2 -2
- data/spec/examples/amazon_spec.rb +3 -2
- data/spec/examples/current_weather_spec.rb +2 -2
- data/spec/examples/dashed_elements_spec.rb +2 -2
- data/spec/examples/library_spec.rb +11 -6
- data/spec/examples/post_spec.rb +3 -3
- data/spec/examples/twitter_spec.rb +2 -2
- data/spec/roxml_spec.rb +15 -15
- data/spec/shared_specs.rb +1 -1
- data/spec/spec_helper.rb +8 -27
- data/spec/support/libxml.rb +3 -0
- data/spec/support/nokogiri.rb +3 -0
- data/spec/xml/attributes_spec.rb +36 -0
- data/spec/xml/namespace_spec.rb +240 -0
- data/spec/xml/namespaces_spec.rb +32 -0
- data/spec/xml/parser_spec.rb +9 -30
- data/tasks/rdoc.rake +13 -0
- data/tasks/rspec.rake +21 -17
- data/tasks/test.rake +13 -20
- data/test/mocks/dictionaries.rb +8 -7
- data/test/mocks/mocks.rb +20 -20
- data/test/support/fixtures.rb +11 -0
- data/test/test_helper.rb +3 -14
- data/test/unit/definition_test.rb +21 -95
- data/test/unit/deprecations_test.rb +1 -74
- data/test/unit/to_xml_test.rb +3 -3
- data/test/unit/xml_attribute_test.rb +1 -1
- data/test/unit/xml_block_test.rb +3 -3
- data/test/unit/xml_bool_test.rb +4 -4
- data/test/unit/xml_convention_test.rb +3 -3
- data/test/unit/xml_hash_test.rb +5 -14
- data/test/unit/xml_initialize_test.rb +2 -6
- data/test/unit/xml_name_test.rb +5 -24
- data/test/unit/xml_namespace_test.rb +1 -46
- data/test/unit/xml_object_test.rb +6 -6
- data/test/unit/xml_required_test.rb +3 -2
- data/test/unit/xml_text_test.rb +2 -2
- data/website/index.html +1 -1
- metadata +68 -51
- data/Manifest.txt +0 -106
- data/lib/roxml/extensions.rb +0 -6
- data/lib/roxml/extensions/array.rb +0 -13
- data/lib/roxml/extensions/array/conversions.rb +0 -35
- data/lib/roxml/extensions/deprecation.rb +0 -33
- data/lib/roxml/extensions/string.rb +0 -21
- data/lib/roxml/extensions/string/conversions.rb +0 -43
- data/lib/roxml/extensions/string/iterators.rb +0 -12
- data/lib/roxml/xml/parsers/rexml.rb +0 -84
- data/spec/string_spec.rb +0 -15
- data/test/bugs/rexml_bugs.rb +0 -15
- data/test/release/dependencies_test.rb +0 -32
- data/test/unit/xml_construct_test.rb +0 -77
- data/vendor/override_rake_task/README +0 -30
- data/vendor/override_rake_task/init.rb +0 -1
- data/vendor/override_rake_task/install.rb +0 -46
- data/vendor/override_rake_task/lib/override_rake_task.rb +0 -16
data/lib/roxml/definition.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'lib/roxml/hash_definition'
|
2
2
|
|
3
3
|
class Module
|
4
4
|
def bool_attr_reader(*attrs)
|
@@ -11,48 +11,41 @@ class Module
|
|
11
11
|
end
|
12
12
|
|
13
13
|
module ROXML
|
14
|
+
class ContradictoryNamespaces < StandardError
|
15
|
+
end
|
16
|
+
|
14
17
|
class Definition # :nodoc:
|
15
|
-
attr_reader :name, :type, :wrapper, :hash, :blocks, :accessor, :to_xml
|
18
|
+
attr_reader :name, :type, :wrapper, :hash, :blocks, :accessor, :to_xml, :attr_name, :namespace
|
16
19
|
bool_attr_reader :name_explicit, :array, :cdata, :required, :frozen
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
21
|
+
def initialize(sym, opts = {}, &block)
|
22
|
+
opts.assert_valid_keys(:from, :in, :as, :namespace,
|
23
|
+
:else, :required, :frozen, :cdata, :to_xml)
|
24
|
+
@default = opts.delete(:else)
|
25
|
+
@to_xml = opts.delete(:to_xml)
|
26
|
+
@name_explicit = opts.has_key?(:from) && opts[:from].is_a?(String)
|
27
|
+
@cdata = opts.delete(:cdata)
|
28
|
+
@required = opts.delete(:required)
|
29
|
+
@frozen = opts.delete(:frozen)
|
30
|
+
@wrapper = opts.delete(:in)
|
31
|
+
@namespace = opts.delete(:namespace)
|
32
|
+
|
33
|
+
@accessor = sym.to_s
|
34
|
+
opts[:as] ||=
|
35
|
+
if @accessor.ends_with?('?')
|
36
|
+
:bool
|
37
|
+
elsif @accessor.ends_with?('_on')
|
38
|
+
Date
|
39
|
+
elsif @accessor.ends_with?('_at')
|
40
|
+
DateTime
|
41
|
+
end
|
39
42
|
|
40
|
-
@array = opts[:as].is_a?(Array)
|
43
|
+
@array = opts[:as].is_a?(Array)
|
41
44
|
@blocks = collect_blocks(block, opts[:as])
|
42
45
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
@type = extract_type(args, opts)
|
48
|
-
if @type.respond_to?(:xml_name_without_deprecation?) && @type.xml_name_without_deprecation?
|
49
|
-
unless self.class.silence_xml_name_warning?
|
50
|
-
warn "WARNING: As of 2.3, a breaking change has been in the naming of sub-objects. " +
|
51
|
-
"ROXML now considers the xml_name of the sub-object before falling back to the accessor name of the parent. " +
|
52
|
-
"Use :from on the parent declaration to override this behavior. Set ROXML::SILENCE_XML_NAME_WARNING to avoid this message."
|
53
|
-
self.class.silence_xml_name_warning!
|
54
|
-
end
|
55
|
-
opts[:from] ||= @type.tag_name
|
46
|
+
@type = extract_type(opts[:as])
|
47
|
+
if @type.respond_to?(:roxml_tag_name)
|
48
|
+
opts[:from] ||= @type.roxml_tag_name
|
56
49
|
end
|
57
50
|
|
58
51
|
if opts[:from] == :content
|
@@ -62,22 +55,30 @@ module ROXML
|
|
62
55
|
elsif opts[:from] == :attr
|
63
56
|
@type = :attr
|
64
57
|
opts[:from] = nil
|
58
|
+
elsif opts[:from] == :name
|
59
|
+
opts[:from] = '*'
|
65
60
|
elsif opts[:from].to_s.starts_with?('@')
|
66
61
|
@type = :attr
|
67
62
|
opts[:from].sub!('@', '')
|
68
63
|
end
|
69
64
|
|
70
|
-
@
|
65
|
+
@attr_name = accessor.to_s.chomp('?')
|
66
|
+
@name = (opts[:from] || @attr_name).to_s
|
71
67
|
@name = @name.singularize if hash? || array?
|
72
68
|
if hash? && (hash.key.name? || hash.value.name?)
|
73
69
|
@name = '*'
|
74
70
|
end
|
71
|
+
raise ContradictoryNamespaces if @name.include?(':') && (@namespace.present? || @namespace == false)
|
75
72
|
|
76
73
|
raise ArgumentError, "Can't specify both :else default and :required" if required? && @default
|
77
74
|
end
|
78
75
|
|
79
|
-
def
|
80
|
-
|
76
|
+
def instance_variable_name
|
77
|
+
:"@#{attr_name}"
|
78
|
+
end
|
79
|
+
|
80
|
+
def setter
|
81
|
+
:"#{attr_name}="
|
81
82
|
end
|
82
83
|
|
83
84
|
def hash
|
@@ -127,23 +128,11 @@ module ROXML
|
|
127
128
|
array ? results : results.first
|
128
129
|
end
|
129
130
|
|
130
|
-
BLOCK_TO_FLOAT = lambda do |val|
|
131
|
-
all(val) do |v|
|
132
|
-
Float(v) unless v.blank?
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
BLOCK_TO_INT = lambda do |val|
|
137
|
-
all(val) do |v|
|
138
|
-
Integer(v) unless v.blank?
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
131
|
def self.fetch_bool(value, default)
|
143
132
|
value = value.to_s.downcase
|
144
|
-
if %w{true yes 1}.include? value
|
133
|
+
if %w{true yes 1 t}.include? value
|
145
134
|
true
|
146
|
-
elsif %w{false no 0}.include? value
|
135
|
+
elsif %w{false no 0 f}.include? value
|
147
136
|
false
|
148
137
|
else
|
149
138
|
default
|
@@ -152,10 +141,16 @@ module ROXML
|
|
152
141
|
|
153
142
|
CORE_BLOCK_SHORTHANDS = {
|
154
143
|
# Core Shorthands
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
144
|
+
Integer => lambda do |val|
|
145
|
+
all(val) do |v|
|
146
|
+
Integer(v) unless v.blank?
|
147
|
+
end
|
148
|
+
end,
|
149
|
+
Float => lambda do |val|
|
150
|
+
all(val) do |v|
|
151
|
+
Float(v) unless v.blank?
|
152
|
+
end
|
153
|
+
end,
|
159
154
|
Fixnum => lambda do |val|
|
160
155
|
all(val) do |v|
|
161
156
|
v.to_i unless v.blank?
|
@@ -181,7 +176,7 @@ module ROXML
|
|
181
176
|
def self.block_shorthands
|
182
177
|
# dynamically load these shorthands at class definition time, but
|
183
178
|
# only if they're already availbable
|
184
|
-
|
179
|
+
CORE_BLOCK_SHORTHANDS.tap do |blocks|
|
185
180
|
blocks.reverse_merge!(BigDecimal => lambda do |val|
|
186
181
|
all(val) do |v|
|
187
182
|
BigDecimal.new(v) unless v.blank?
|
@@ -203,11 +198,8 @@ module ROXML
|
|
203
198
|
end
|
204
199
|
|
205
200
|
def collect_blocks(block, as)
|
206
|
-
ActiveSupport::Deprecation.warn ":as => :float is deprecated. Use :as => Float instead" if as == :float
|
207
|
-
ActiveSupport::Deprecation.warn ":as => :integer is deprecated. Use :as => Integer instead" if as == :integer
|
208
|
-
|
209
201
|
if as.is_a?(Array)
|
210
|
-
|
202
|
+
if as.size > 1
|
211
203
|
raise ArgumentError, "multiple :as types (#{as.map(&:inspect).join(', ')}) is not supported. Use a block if you want more complicated behavior."
|
212
204
|
end
|
213
205
|
|
@@ -221,103 +213,23 @@ module ROXML
|
|
221
213
|
end
|
222
214
|
as = self.class.block_shorthands.fetch(as) do
|
223
215
|
unless as.respond_to?(:from_xml) || (as.respond_to?(:first) && as.first.respond_to?(:from_xml)) || (as.is_a?(Hash) && !(as.keys & [:key, :value]).empty?)
|
224
|
-
|
216
|
+
raise ArgumentError, "Invalid :as argument #{as}" unless as.nil?
|
225
217
|
end
|
226
218
|
nil
|
227
219
|
end
|
228
220
|
[as, block].compact
|
229
221
|
end
|
230
222
|
|
231
|
-
def
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
@default = opts.delete(:else)
|
239
|
-
@to_xml = opts.delete(:to_xml)
|
240
|
-
@name_explicit = opts.has_key?(:from) && opts[:from].is_a?(String)
|
241
|
-
@cdata = opts.delete(:cdata)
|
242
|
-
@required = opts.delete(:required)
|
243
|
-
@frozen = opts.delete(:frozen)
|
244
|
-
@wrapper = opts.delete(:in)
|
245
|
-
|
246
|
-
@cdata ||= extract_from_as(opts, :cdata, "Please use :cdata => true")
|
247
|
-
|
248
|
-
if opts[:as].is_a?(Array) && opts[:as].size > 1
|
249
|
-
ActiveSupport::Deprecation.warn ":as should point to a single item. #{opts[:as].join(', ')} should be declared some other way."
|
250
|
-
end
|
251
|
-
|
252
|
-
opts
|
253
|
-
end
|
254
|
-
|
255
|
-
def extract_from_as(opts, entry, message)
|
256
|
-
# remove with deprecateds...
|
257
|
-
if [*opts[:as]].include?(entry)
|
258
|
-
ActiveSupport::Deprecation.warn ":as => #{entry.inspect} is deprecated. #{message}"
|
259
|
-
if opts[:as] == entry
|
260
|
-
opts[:as] = nil
|
261
|
-
else
|
262
|
-
opts[:as].delete(entry)
|
263
|
-
end
|
264
|
-
true
|
265
|
-
end
|
266
|
-
end
|
267
|
-
|
268
|
-
def extract_type(args, opts)
|
269
|
-
types = (opts.keys & TYPE_KEYS)
|
270
|
-
# type arg
|
271
|
-
if args.one? && types.empty?
|
272
|
-
type = args.first
|
273
|
-
if type.is_a? Array
|
274
|
-
ActiveSupport::Deprecation.warn "Array declarations should be passed as the :as parameter, for future release."
|
275
|
-
@array = true
|
276
|
-
return type.first || :text
|
277
|
-
elsif type.is_a? Hash
|
278
|
-
ActiveSupport::Deprecation.warn "Hash declarations should be passed as the :as parameter, for future release."
|
279
|
-
return HashDefinition.new(type)
|
280
|
-
elsif type == :content
|
281
|
-
ActiveSupport::Deprecation.warn ":content as a type declaration is deprecated. Use :from => '.' or :from => :content instead"
|
282
|
-
opts[:from] = :content
|
283
|
-
return :text
|
284
|
-
elsif type == :attr
|
285
|
-
ActiveSupport::Deprecation.warn ":attr as a type declaration is deprecated. Use :from => '@attr_name' or :from => :attr instead"
|
286
|
-
opts[:from].sub!('@', '') if opts[:from].to_s.starts_with?('@') # this is added back next line...
|
287
|
-
opts[:from] = opts[:from].nil? ? :attr : "@#{opts[:from]}"
|
288
|
-
return :attr
|
289
|
-
else
|
290
|
-
ActiveSupport::Deprecation.warn "Type declarations should be passed as the :as parameter, for future release."
|
291
|
-
return type
|
292
|
-
end
|
293
|
-
end
|
294
|
-
|
295
|
-
unless args.empty?
|
296
|
-
raise ArgumentError, "too many arguments (#{(args + types).join(', ')}). Should be name, type, and " +
|
297
|
-
"an options hash, with the type and options optional"
|
298
|
-
end
|
299
|
-
|
300
|
-
if opts[:as].is_a?(Hash)
|
301
|
-
return HashDefinition.new(opts[:as])
|
302
|
-
elsif opts[:as].respond_to?(:from_xml)
|
303
|
-
return opts[:as]
|
304
|
-
elsif opts[:as].is_a?(Array) && opts[:as].first.respond_to?(:from_xml)
|
223
|
+
def extract_type(as)
|
224
|
+
if as.is_a?(Hash)
|
225
|
+
return HashDefinition.new(as)
|
226
|
+
elsif as.respond_to?(:from_xml)
|
227
|
+
return as
|
228
|
+
elsif as.is_a?(Array) && as.first.respond_to?(:from_xml)
|
305
229
|
@array = true
|
306
|
-
return
|
307
|
-
end
|
308
|
-
|
309
|
-
# type options
|
310
|
-
if types.one?
|
311
|
-
opts[:from] = opts.delete(types.first)
|
312
|
-
if opts[:from] == :content
|
313
|
-
opts[:from] = 'content'
|
314
|
-
ActiveSupport::Deprecation.warn ":content is now a reserved as an alias for '.'. Use the string 'content' instead"
|
315
|
-
end
|
316
|
-
types.first
|
317
|
-
elsif types.empty?
|
318
|
-
:text
|
230
|
+
return as.first
|
319
231
|
else
|
320
|
-
|
232
|
+
:text
|
321
233
|
end
|
322
234
|
end
|
323
235
|
end
|
@@ -1,60 +1,25 @@
|
|
1
1
|
module ROXML
|
2
|
-
HASH_KEYS = [:attrs, :key, :value].freeze
|
3
|
-
TYPE_KEYS = [:attr, :text, :hash, :content].freeze
|
4
|
-
|
5
2
|
class HashDefinition # :nodoc:
|
6
3
|
attr_reader :key, :value
|
7
4
|
attr_accessor :wrapper
|
8
5
|
|
9
6
|
def initialize(opts)
|
10
|
-
|
11
|
-
raise ArgumentError, "Invalid Hash description keys: #{invalid_keys.join(', ')}"
|
12
|
-
end
|
7
|
+
opts.assert_valid_keys(:key, :value)
|
13
8
|
|
14
|
-
|
15
|
-
|
16
|
-
@key = to_hash_args(opts, :from => "@#{opts[:attrs][0]}")
|
17
|
-
@value = to_hash_args(opts, :from => "@#{opts[:attrs][1]}")
|
18
|
-
else
|
19
|
-
@key = to_hash_args opts, fetch_element(opts, :key)
|
20
|
-
@value = to_hash_args opts, fetch_element(opts, :value)
|
21
|
-
end
|
9
|
+
@key = Definition.new(nil, to_definition_options(opts, :key))
|
10
|
+
@value = Definition.new(nil, to_definition_options(opts, :value))
|
22
11
|
end
|
23
12
|
|
24
13
|
private
|
25
|
-
def
|
14
|
+
def to_definition_options(opts, what)
|
26
15
|
case opts[what]
|
27
16
|
when Hash
|
28
|
-
|
29
|
-
ActiveSupport::Deprecation.warn(":as => {:key => {Type => 'name'} ... } is going away in 3.0. Use explicit :key => {:from => 'name', :as => Type} instead.")
|
30
|
-
type = opts[what].keys.first
|
31
|
-
case type
|
32
|
-
when :attr
|
33
|
-
{:from => "@#{opts[what][type]}"}
|
34
|
-
when :text
|
35
|
-
{:from => opts[what][type]}
|
36
|
-
else
|
37
|
-
{:as => type, :from => opts[what][type]}
|
38
|
-
end
|
39
|
-
else
|
40
|
-
opts[what]
|
41
|
-
end
|
17
|
+
opts[what]
|
42
18
|
when String, Symbol
|
43
19
|
{:from => opts[what]}
|
44
20
|
else
|
45
21
|
raise ArgumentError, "unrecognized hash parameter: #{what} => #{opts[what]}"
|
46
22
|
end
|
47
23
|
end
|
48
|
-
|
49
|
-
def to_hash_args(args, opts)
|
50
|
-
args = [args] unless args.is_a? Array
|
51
|
-
|
52
|
-
if args.one? && !(args.first.keys & HASH_KEYS).empty?
|
53
|
-
Definition.new(nil, opts)
|
54
|
-
else
|
55
|
-
opts = args.extract_options!
|
56
|
-
raise opts.inspect
|
57
|
-
end
|
58
|
-
end
|
59
24
|
end
|
60
25
|
end
|
data/lib/roxml/xml.rb
CHANGED
@@ -1,18 +1,21 @@
|
|
1
1
|
module ROXML
|
2
2
|
unless const_defined? 'XML_PARSER'
|
3
|
+
PREFERRED_PARSERS = %w[nokogiri libxml].freeze
|
4
|
+
parsers = PREFERRED_PARSERS.dup
|
3
5
|
begin
|
4
|
-
require
|
5
|
-
XML_PARSER =
|
6
|
+
require parsers.first
|
7
|
+
XML_PARSER = parsers.first # :nodoc:
|
6
8
|
rescue LoadError
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
if parsers.size > 1
|
10
|
+
parsers.shift
|
11
|
+
retry
|
12
|
+
else
|
13
|
+
raise "Could not load either nokogiri or libxml"
|
14
|
+
end
|
12
15
|
end
|
13
16
|
end
|
14
17
|
|
15
|
-
require File.join(
|
18
|
+
require File.join('lib/roxml/xml/parsers', XML_PARSER)
|
16
19
|
|
17
20
|
module XML
|
18
21
|
class Node
|
@@ -37,4 +40,4 @@ WARNING
|
|
37
40
|
end
|
38
41
|
end
|
39
42
|
|
40
|
-
require
|
43
|
+
require 'lib/roxml/xml/references'
|
@@ -9,37 +9,36 @@ module ROXML
|
|
9
9
|
Error = LibXML::XML::Error
|
10
10
|
|
11
11
|
module NamespacedSearch
|
12
|
-
def search(xpath)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
rescue Exception => ex
|
21
|
-
raise ex, xpath
|
12
|
+
def search(xpath, roxml_namespaces = {})
|
13
|
+
if namespaces.default
|
14
|
+
roxml_namespaces = {:xmlns => namespaces.default.href}.merge(roxml_namespaces)
|
15
|
+
end
|
16
|
+
if roxml_namespaces.present?
|
17
|
+
find(xpath, roxml_namespaces.map {|prefix, href| [prefix, href].join(':') })
|
18
|
+
else
|
19
|
+
find(xpath)
|
22
20
|
end
|
23
21
|
end
|
24
22
|
|
25
23
|
private
|
26
24
|
def namespaced(xpath)
|
27
|
-
xpath.
|
25
|
+
xpath.split('/').map do |component|
|
28
26
|
if component =~ /\w+/ && !component.include?(':') && !component.starts_with?('@')
|
29
|
-
|
27
|
+
"xmlns:#{component}"
|
30
28
|
else
|
31
29
|
component
|
32
30
|
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def in_default_namespace(name)
|
37
|
-
"roxmldefaultnamespace:#{name}"
|
31
|
+
end.join('/')
|
38
32
|
end
|
39
33
|
end
|
40
34
|
|
41
35
|
class Document
|
42
36
|
include NamespacedSearch
|
37
|
+
|
38
|
+
def default_namespace
|
39
|
+
default = namespaces.default
|
40
|
+
default.prefix || 'xmlns' if default
|
41
|
+
end
|
43
42
|
|
44
43
|
private
|
45
44
|
delegate :namespaces, :to => :root
|
@@ -53,6 +52,12 @@ module ROXML
|
|
53
52
|
new_without_entity_escaping(name, content && CGI.escapeHTML(content), namespace)
|
54
53
|
end
|
55
54
|
alias_method_chain :new, :entity_escaping
|
55
|
+
|
56
|
+
alias :create :new
|
57
|
+
end
|
58
|
+
|
59
|
+
def default_namespace
|
60
|
+
doc.default_namespace
|
56
61
|
end
|
57
62
|
|
58
63
|
def add_child(child)
|