xmlcodec 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|