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 CHANGED
@@ -42,6 +42,11 @@ module XMLCodec
42
42
  str << ">"
43
43
  str
44
44
  end
45
+
46
+ # Create a close tag.
47
+ def self.create_close_tag(name)
48
+ "</"+name.to_s+">"
49
+ end
45
50
 
46
51
  # Escape a string so that it can be included in a XML document
47
52
  def self.escape_xml(string)
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
- "</"+elname.to_s+">"
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
- if not self.instance_variables.index("@subelements".to_sym)
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
- define_method(:has_subelements?) {true}
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('@@elclasses', {})
213
- class_variable_set('@@strict_parsing', false)
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('@@strict_parsing', true)
216
+ class_variable_set(:@@strict_parsing, true)
218
217
  end
219
218
 
220
219
  def self.elclasses
221
- class_variable_get('@@elclasses')
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
- define_method(:hasvalue?){true}
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('@@strict_parsing')
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; end
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
- false
358
- end
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
- elements = []
401
- xmlel.children.each do |e|
402
- if e.text?
403
- elements << e.text
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
- elclass = get_element_class(e.name)
406
- if not elclass
407
- if class_variable_get('@@strict_parsing')
408
- raise ElementClassNotFound, "No class defined for element type: '#{e.name}'"
409
- end
410
- else
411
- elements << elclass._import_xml_dom(e)
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('@@strict_parsing')
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
@@ -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
- @elements << XMLSOParserElement.new(name, attrs, get_elclass(name),
108
- curr_element, next_id,
109
- curr_element.depth+1)
110
- @currel += 1
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
- curr_element.add_child(text)
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
- obj = curr_element
119
-
120
- if @listener.respond_to?("el_"+name)
121
- @listener.send("el_"+name, obj)
122
- end
123
-
124
- if not obj.consumed
125
- real_obj = obj.get_object
126
-
127
- if prev_element && real_obj
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
- @top_element = obj
132
- end
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
- @elements.pop
135
- @currel -= 1
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
@@ -1,7 +1,7 @@
1
1
  require 'nokogiri'
2
2
 
3
3
  module XMLCodec
4
- VERSION = '0.3.0'
4
+ VERSION = '0.3.1'
5
5
  end
6
6
 
7
7
  require File.dirname(__FILE__) + '/XMLUtils'
@@ -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
@@ -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
- def test_partial_elements_in_text
16
- sel = PartialBaseFormat.import_xml "<abc><otherel></otherel>text</abc>"
17
- assert_equal "text", sel.value
5
+ class PartialBaseFormat < XMLElement
6
+ xmlformat 'Base Format'
18
7
  end
19
8
 
20
- def test_partial_elements_in_dom
21
- xml_text = "<abc><otherel></otherel>text</abc>"
22
- sel = PartialBaseFormat.import_xml Nokogiri::XML::Document.parse(xml_text)
23
- assert_equal "text", sel.value
9
+ class PartialSimpleElement < PartialBaseFormat
10
+ elwithvalue
11
+ elname 'abc'
12
+ xmlattr :myattr
24
13
  end
25
14
 
26
- def test_partial_attributes_in_text
27
- sel = PartialBaseFormat.import_xml "<abc myattr='real' someattr='xpto'>text</abc>"
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
- def test_partial_attributes_in_dom
33
- xml_text = "<abc myattr='real' someattr='xpto'>text</abc>"
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.0'
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.0
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: &79189020 !ruby/object:Gem::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: *79189020
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