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 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