xmlcodec 0.3.0 → 0.3.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.
- data/lib/XMLUtils.rb +5 -0
- data/lib/element.rb +56 -34
- data/lib/stream_object_parser.rb +40 -20
- data/lib/xmlcodec.rb +1 -1
- data/test/consume_all_as_text_test.rb +26 -0
- data/test/element_types_test.rb +48 -0
- data/test/partial_import_test.rb +15 -23
- data/test/test_helper.rb +12 -0
- data/xmlcodec.gemspec +3 -1
- metadata +7 -3
data/lib/XMLUtils.rb
CHANGED
data/lib/element.rb
CHANGED
@@ -173,17 +173,14 @@ module XMLCodec
|
|
173
173
|
|
174
174
|
# returns a string with the closing tag for the element
|
175
175
|
def create_close_tag
|
176
|
-
|
176
|
+
XMLCodec::XMLUtils::create_close_tag(elname.to_s)
|
177
177
|
end
|
178
178
|
|
179
179
|
# Declare the class as having many subelements. Instances will have a
|
180
180
|
# method called #subelements that will return an instance of XMLSubElements
|
181
181
|
def self.xmlsubelements #:doc:
|
182
182
|
define_method(:subelements) {
|
183
|
-
|
184
|
-
@subelements = XMLSubElements.new(self)
|
185
|
-
end
|
186
|
-
@subelements
|
183
|
+
@subelements ||= XMLSubElements.new(self)
|
187
184
|
}
|
188
185
|
define_method('<<') {|value|
|
189
186
|
subelements << value
|
@@ -197,7 +194,9 @@ module XMLCodec
|
|
197
194
|
define_method(:find_all_named) {|name|
|
198
195
|
subelements.find_all_named(name)
|
199
196
|
}
|
200
|
-
|
197
|
+
self.class_eval do
|
198
|
+
def self.has_subelements?; true; end
|
199
|
+
end
|
201
200
|
end
|
202
201
|
|
203
202
|
# Add a xmlattr type attribute (wrapper around attr_accessor)
|
@@ -209,16 +208,16 @@ module XMLCodec
|
|
209
208
|
# Defines a new xml format (like XHTML or DocBook). This should be used in
|
210
209
|
# a class that's the super class of all the elements of a format
|
211
210
|
def self.xmlformat(name=nil)
|
212
|
-
class_variable_set(
|
213
|
-
class_variable_set(
|
211
|
+
class_variable_set(:@@elclasses, {})
|
212
|
+
class_variable_set(:@@strict_parsing, false)
|
214
213
|
end
|
215
214
|
|
216
215
|
def self.xml_strict_parsing
|
217
|
-
class_variable_set(
|
216
|
+
class_variable_set(:@@strict_parsing, true)
|
218
217
|
end
|
219
218
|
|
220
219
|
def self.elclasses
|
221
|
-
class_variable_get(
|
220
|
+
class_variable_get(:@@elclasses)
|
222
221
|
end
|
223
222
|
|
224
223
|
# Sets the element name for the element
|
@@ -244,7 +243,9 @@ module XMLCodec
|
|
244
243
|
# that takes a value as argument and an accessor named #value. This should
|
245
244
|
# be used for elements that contain only text and no subelements
|
246
245
|
def self.elwithvalue
|
247
|
-
|
246
|
+
self.class_eval do
|
247
|
+
def self.hasvalue?; true; end
|
248
|
+
end
|
248
249
|
self.class_eval do
|
249
250
|
def initialize(value=nil)
|
250
251
|
@value = value
|
@@ -252,6 +253,19 @@ module XMLCodec
|
|
252
253
|
end
|
253
254
|
attr_accessor :value
|
254
255
|
end
|
256
|
+
|
257
|
+
# Set the element as having a value that eats up any subelements as if they
|
258
|
+
# were text. The element will behave similarly to "elwithvalue" with an
|
259
|
+
# initializar that takes a value as argument and an accessor named #value
|
260
|
+
# and will consume all its subelements as if they were text. This should
|
261
|
+
# be used for elements that contain subelements that you want to treat as
|
262
|
+
# text like <content> in Atom
|
263
|
+
def self.elallvalue
|
264
|
+
self.elwithvalue
|
265
|
+
self.class_eval do
|
266
|
+
def self.allvalue?; true; end
|
267
|
+
end
|
268
|
+
end
|
255
269
|
|
256
270
|
# Creates the XML subelements
|
257
271
|
def create_xml_subel(parent)
|
@@ -337,7 +351,7 @@ module XMLCodec
|
|
337
351
|
# Gets the class for a certain element name.
|
338
352
|
def self.get_element_class(name)
|
339
353
|
cl = elclasses[name.to_sym]
|
340
|
-
if not cl and class_variable_get(
|
354
|
+
if not cl and class_variable_get(:@@strict_parsing)
|
341
355
|
raise ElementClassNotFound, "No class defined for element type: '" + name.to_s + "'"
|
342
356
|
end
|
343
357
|
cl
|
@@ -350,13 +364,16 @@ module XMLCodec
|
|
350
364
|
|
351
365
|
# Method that checks if a given class has subelements. This is usually only
|
352
366
|
# used when exporting stuff.
|
353
|
-
def has_subelements?; false
|
367
|
+
def self.has_subelements?; false end
|
368
|
+
def has_subelements?; self.class.has_subelements? end
|
354
369
|
|
355
370
|
# tests if the element is a value element as defined by 'elwithvalue'
|
356
|
-
def hasvalue
|
357
|
-
|
358
|
-
|
359
|
-
|
371
|
+
def self.hasvalue?; false end
|
372
|
+
def hasvalue?; self.class.hasvalue? end
|
373
|
+
|
374
|
+
# tests if the element is a value element as defined by 'elallvalue'
|
375
|
+
def self.allvalue?; false end
|
376
|
+
def allvalue?; self.class.allvalue?; end
|
360
377
|
|
361
378
|
# Creates the xml for the element inside the parent element. The parent
|
362
379
|
# passed should be a Nokogiri XML Node or Document. This call is recursive
|
@@ -369,7 +386,7 @@ module XMLCodec
|
|
369
386
|
create_xml_attr(xmlel)
|
370
387
|
create_xml_subel(xmlel)
|
371
388
|
|
372
|
-
if has_subelements?
|
389
|
+
if self.has_subelements?
|
373
390
|
create_xml_subelements(xmlel)
|
374
391
|
end
|
375
392
|
|
@@ -396,19 +413,26 @@ module XMLCodec
|
|
396
413
|
if xmlel.is_a? Nokogiri::XML::Document
|
397
414
|
xmlel = xmlel.root
|
398
415
|
end
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
if
|
403
|
-
|
416
|
+
|
417
|
+
elclass = get_element_class(xmlel.name)
|
418
|
+
if not elclass
|
419
|
+
if class_variable_get(:@@strict_parsing)
|
420
|
+
raise ElementClassNotFound, "No class defined for element type: '#{e.name}'"
|
404
421
|
else
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
422
|
+
return nil
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
if elclass.allvalue?
|
427
|
+
elements = [xmlel.children.map{|c| c.to_xml(:save_with=>0)}.join]
|
428
|
+
else
|
429
|
+
elements = []
|
430
|
+
xmlel.children.each do |e|
|
431
|
+
if e.text?
|
432
|
+
elements << e.text
|
433
|
+
else
|
434
|
+
element = _import_xml_dom(e)
|
435
|
+
elements << element if element
|
412
436
|
end
|
413
437
|
end
|
414
438
|
end
|
@@ -418,8 +442,6 @@ module XMLCodec
|
|
418
442
|
attributes[name] = attr.value
|
419
443
|
end
|
420
444
|
|
421
|
-
elclass = get_element_class(xmlel.name)
|
422
|
-
return nil if not elclass
|
423
445
|
elclass.new_with_content(attributes, elements)
|
424
446
|
end
|
425
447
|
|
@@ -457,7 +479,7 @@ module XMLCodec
|
|
457
479
|
def add_attr(attrs)
|
458
480
|
attrs.each do |name, value|
|
459
481
|
if not self.class.attr_names.include?(name.to_sym)
|
460
|
-
if self.class.class_variable_get(
|
482
|
+
if self.class.class_variable_get(:@@strict_parsing)
|
461
483
|
raise ElementAttributeNotFound, "No attribute '#{name}' defined for class '#{self.class}'"
|
462
484
|
end
|
463
485
|
else
|
@@ -468,7 +490,7 @@ module XMLCodec
|
|
468
490
|
|
469
491
|
# add the text elements into the element
|
470
492
|
def add_texts(texts)
|
471
|
-
if hasvalue?
|
493
|
+
if self.hasvalue?
|
472
494
|
@value = texts.join
|
473
495
|
end
|
474
496
|
end
|
data/lib/stream_object_parser.rb
CHANGED
@@ -70,6 +70,7 @@ module XMLCodec
|
|
70
70
|
@elements = [XMLSOParserElement.new(nil, nil, nil, nil, nil, 0)]
|
71
71
|
@id = 0
|
72
72
|
@top_element = nil
|
73
|
+
@allvalue = 0
|
73
74
|
end
|
74
75
|
|
75
76
|
private
|
@@ -104,35 +105,54 @@ module XMLCodec
|
|
104
105
|
end
|
105
106
|
|
106
107
|
def start_element(name, attrs) #:nodoc:
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
108
|
+
elclass = get_elclass(name)
|
109
|
+
if @allvalue > 0
|
110
|
+
curr_element.get_object.value << XMLUtils.create_open_tag(name,attrs)
|
111
|
+
@allvalue += 1
|
112
|
+
else
|
113
|
+
@elements << XMLSOParserElement.new(name, attrs, elclass,
|
114
|
+
curr_element, next_id,
|
115
|
+
curr_element.depth+1)
|
116
|
+
@currel += 1
|
117
|
+
@allvalue = 1 if elclass && elclass.allvalue?
|
118
|
+
end
|
111
119
|
end
|
112
120
|
|
113
121
|
def characters(text) #:nodoc:
|
114
|
-
|
122
|
+
if @allvalue > 0
|
123
|
+
curr_element.get_object.value << text
|
124
|
+
else
|
125
|
+
curr_element.add_child(text)
|
126
|
+
end
|
115
127
|
end
|
116
128
|
|
117
129
|
def end_element(name) #:nodoc:
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
prev_element.add_child(real_obj)
|
130
|
+
elclass = get_elclass(name)
|
131
|
+
if @allvalue > 1
|
132
|
+
# We're closing an allvalue subelement, just output it and pop
|
133
|
+
curr_element.get_object.value << XMLUtils.create_close_tag(name)
|
134
|
+
@allvalue -= 1
|
135
|
+
else
|
136
|
+
obj = curr_element
|
137
|
+
|
138
|
+
if @listener.respond_to?("el_"+name)
|
139
|
+
@listener.send("el_"+name, obj)
|
129
140
|
end
|
130
141
|
|
131
|
-
|
132
|
-
|
142
|
+
if not obj.consumed
|
143
|
+
real_obj = obj.get_object
|
144
|
+
|
145
|
+
if prev_element && real_obj
|
146
|
+
prev_element.add_child(real_obj)
|
147
|
+
end
|
133
148
|
|
134
|
-
|
135
|
-
|
149
|
+
@top_element = obj
|
150
|
+
end
|
151
|
+
|
152
|
+
@elements.pop
|
153
|
+
@currel -= 1
|
154
|
+
@allvalue = 0
|
155
|
+
end
|
136
156
|
end
|
137
157
|
end
|
138
158
|
end
|
data/lib/xmlcodec.rb
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
class ConsumeAllAsTextTest < Test::Unit::TestCase
|
5
|
+
class BaseFormat < XMLElement
|
6
|
+
xmlformat 'Base Format'
|
7
|
+
end
|
8
|
+
|
9
|
+
class SimpleElement < BaseFormat
|
10
|
+
elallvalue
|
11
|
+
elname 'abc'
|
12
|
+
xmlattr :myattr
|
13
|
+
end
|
14
|
+
|
15
|
+
double_import_test(:test_import_elements, BaseFormat,
|
16
|
+
"<abc myattr='real'><otherel>foo</otherel>text</abc>") do |sel|
|
17
|
+
assert_equal 'real', sel.myattr
|
18
|
+
assert_equal "<otherel>foo</otherel>text", sel.value
|
19
|
+
end
|
20
|
+
|
21
|
+
double_import_test(:test_nested_elements, BaseFormat,
|
22
|
+
"<abc myattr='real'><otherel><abc>foo</abc></otherel>text</abc>") do |sel|
|
23
|
+
assert_equal 'real', sel.myattr
|
24
|
+
assert_equal "<otherel><abc>foo</abc></otherel>text", sel.value
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
class TestElementTypes < Test::Unit::TestCase
|
5
|
+
class ElementTypesBaseFormat < XMLElement
|
6
|
+
xmlformat 'Base Format'
|
7
|
+
end
|
8
|
+
|
9
|
+
class EmptyElement < ElementTypesBaseFormat
|
10
|
+
elname 'empty'
|
11
|
+
end
|
12
|
+
|
13
|
+
class ValueElement < ElementTypesBaseFormat
|
14
|
+
elwithvalue
|
15
|
+
elname 'value'
|
16
|
+
end
|
17
|
+
|
18
|
+
class AllValueElement < ElementTypesBaseFormat
|
19
|
+
elallvalue
|
20
|
+
elname 'allvalue'
|
21
|
+
end
|
22
|
+
|
23
|
+
class SubelementsElement < ElementTypesBaseFormat
|
24
|
+
elname 'subelements'
|
25
|
+
xmlsubelements
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_elwithvalue
|
29
|
+
assert_equal true, ValueElement.hasvalue?
|
30
|
+
assert_equal true, ValueElement.new.hasvalue?
|
31
|
+
assert_equal false, EmptyElement.hasvalue?
|
32
|
+
assert_equal false, EmptyElement.new.hasvalue?
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_elallvaluevalue
|
36
|
+
assert_equal true, AllValueElement.allvalue?
|
37
|
+
assert_equal true, AllValueElement.new.allvalue?
|
38
|
+
assert_equal false, EmptyElement.allvalue?
|
39
|
+
assert_equal false, EmptyElement.new.allvalue?
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_xmlsubelements
|
43
|
+
assert_equal true, SubelementsElement.has_subelements?
|
44
|
+
assert_equal true, SubelementsElement.has_subelements?
|
45
|
+
assert_equal false, EmptyElement.has_subelements?
|
46
|
+
assert_equal false, EmptyElement.has_subelements?
|
47
|
+
end
|
48
|
+
end
|
data/test/partial_import_test.rb
CHANGED
@@ -1,38 +1,30 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
2
|
require 'nokogiri'
|
3
3
|
|
4
|
-
class PartialBaseFormat < XMLElement
|
5
|
-
xmlformat 'Base Format'
|
6
|
-
end
|
7
|
-
|
8
|
-
class PartialSimpleElement < PartialBaseFormat
|
9
|
-
elwithvalue
|
10
|
-
elname 'abc'
|
11
|
-
xmlattr :myattr
|
12
|
-
end
|
13
|
-
|
14
4
|
class TestPartialImport < Test::Unit::TestCase
|
15
|
-
|
16
|
-
|
17
|
-
assert_equal "text", sel.value
|
5
|
+
class PartialBaseFormat < XMLElement
|
6
|
+
xmlformat 'Base Format'
|
18
7
|
end
|
19
8
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
9
|
+
class PartialSimpleElement < PartialBaseFormat
|
10
|
+
elwithvalue
|
11
|
+
elname 'abc'
|
12
|
+
xmlattr :myattr
|
24
13
|
end
|
25
14
|
|
26
|
-
|
27
|
-
|
15
|
+
double_import_test(:test_partial_elements, PartialBaseFormat,
|
16
|
+
"<abc><otherel></otherel>text</abc>") do |sel|
|
28
17
|
assert_equal "text", sel.value
|
29
|
-
assert_equal "real", sel.myattr
|
30
18
|
end
|
31
19
|
|
32
|
-
|
33
|
-
|
34
|
-
sel = PartialBaseFormat.import_xml Nokogiri::XML::Document.parse(xml_text)
|
20
|
+
double_import_test(:test_partial_attributes_in_text, PartialBaseFormat,
|
21
|
+
"<abc myattr='real' someattr='xpto'>text</abc>") do |sel|
|
35
22
|
assert_equal "text", sel.value
|
36
23
|
assert_equal "real", sel.myattr
|
37
24
|
end
|
25
|
+
|
26
|
+
double_import_test(:test_stress_valid_subelements_in_text, PartialBaseFormat,
|
27
|
+
"<abc><otherel><abc></abc></otherel>text</abc>") do |sel|
|
28
|
+
assert_equal "<abc>text</abc>", sel.xml_text
|
29
|
+
end
|
38
30
|
end
|
data/test/test_helper.rb
CHANGED
@@ -27,4 +27,16 @@ class Test::Unit::TestCase
|
|
27
27
|
def compare_xpath(value, path)
|
28
28
|
assert_equal(value.strip, XMLCodec::XMLUtils::select_path(path, @temp_path).strip)
|
29
29
|
end
|
30
|
+
|
31
|
+
# Tests both through DOM and text to make sure both code paths are working
|
32
|
+
def self.double_import_test(name, klass, text,&block)
|
33
|
+
define_method(name.to_s+"_text") do
|
34
|
+
sel = klass.import_xml text
|
35
|
+
instance_exec(sel,&block)
|
36
|
+
end
|
37
|
+
define_method(name.to_s+"_dom") do
|
38
|
+
sel = klass.import_xml Nokogiri::XML::Document.parse(text)
|
39
|
+
instance_exec(sel,&block)
|
40
|
+
end
|
41
|
+
end
|
30
42
|
end
|
data/xmlcodec.gemspec
CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |s|
|
|
6
6
|
s.platform = Gem::Platform::RUBY
|
7
7
|
|
8
8
|
s.name = 'xmlcodec'
|
9
|
-
s.version = '0.3.
|
9
|
+
s.version = '0.3.1'
|
10
10
|
s.date = '2013-08-30'
|
11
11
|
|
12
12
|
s.summary = "Generic Importer/Exporter of XML formats"
|
@@ -39,7 +39,9 @@ EOF
|
|
39
39
|
lib/stream_parser.rb
|
40
40
|
lib/subelements.rb
|
41
41
|
lib/xmlcodec.rb
|
42
|
+
test/consume_all_as_text_test.rb
|
42
43
|
test/element_test.rb
|
44
|
+
test/element_types_test.rb
|
43
45
|
test/multi_format_test.rb
|
44
46
|
test/partial_export_test.rb
|
45
47
|
test/partial_import_test.rb
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xmlcodec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2013-08-30 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: nokogiri
|
16
|
-
requirement: &
|
16
|
+
requirement: &84598170 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *84598170
|
25
25
|
description: ! 'A framework to write object to XML mappers in Ruby that can then function
|
26
26
|
both in whole-document manipulation as well as constant memory unlimited size importing
|
27
27
|
and exporting of XML.
|
@@ -43,7 +43,9 @@ files:
|
|
43
43
|
- lib/stream_parser.rb
|
44
44
|
- lib/subelements.rb
|
45
45
|
- lib/xmlcodec.rb
|
46
|
+
- test/consume_all_as_text_test.rb
|
46
47
|
- test/element_test.rb
|
48
|
+
- test/element_types_test.rb
|
47
49
|
- test/multi_format_test.rb
|
48
50
|
- test/partial_export_test.rb
|
49
51
|
- test/partial_import_test.rb
|
@@ -83,7 +85,9 @@ signing_key:
|
|
83
85
|
specification_version: 2
|
84
86
|
summary: Generic Importer/Exporter of XML formats
|
85
87
|
test_files:
|
88
|
+
- test/consume_all_as_text_test.rb
|
86
89
|
- test/element_test.rb
|
90
|
+
- test/element_types_test.rb
|
87
91
|
- test/multi_format_test.rb
|
88
92
|
- test/partial_export_test.rb
|
89
93
|
- test/partial_import_test.rb
|