ox 1.3.5 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of ox might be problematic. Click here for more details.

data/README.md CHANGED
@@ -26,9 +26,10 @@ A fast XML parser and Object marshaller as a Ruby gem.
26
26
 
27
27
  ## <a name="release">Release Notes</a>
28
28
 
29
- ### Release 1.3.4
29
+ ### Release 1.4.0
30
30
 
31
- - Made Ox SAX compatible with Ruby 1.8.7 when readpartial is not implemented on IO.
31
+ - Fixed bug in parsing the xml instruction.
32
+ - Added locate() method that provides simple and limited xpath like functionality.
32
33
 
33
34
  ## <a name="description">Description</a>
34
35
 
@@ -189,6 +189,8 @@ read_instruction(PInfo pi) {
189
189
  if ('?' == *pi->s) {
190
190
  pi->s++;
191
191
  }
192
+ } else {
193
+ pi->s++;
192
194
  }
193
195
  if ('>' != *pi->s++) {
194
196
  raise_error("invalid format, processing instruction not terminated", pi->str, pi->s);
data/lib/ox.rb CHANGED
@@ -88,6 +88,7 @@ module Ox
88
88
  end
89
89
 
90
90
  require 'ox/version'
91
+ require 'ox/invalidpath'
91
92
  require 'ox/node'
92
93
  require 'ox/comment'
93
94
  require 'ox/cdata'
@@ -1,5 +1,6 @@
1
1
 
2
2
  module Ox
3
+
3
4
  # An Element represents a element of an XML document. It has a name,
4
5
  # attributes, and sub-nodes.
5
6
  class Element < Node
@@ -64,6 +65,108 @@ module Ox
64
65
  true
65
66
  end
66
67
  alias == eql?
68
+
69
+ # Returns an array of Nodes or Strings that correspond to the locations
70
+ # specified by the path parameter. The path parameter describes the path
71
+ # to the return values which can be either nodes in the XML or
72
+ # attributes. The path is a relative description. There are similarities
73
+ # between the locate() method and XPath but locate does not follow the
74
+ # same rules as XPath. The syntax is meant to be simpler and more Ruby
75
+ # like.
76
+ #
77
+ # Like XPath the path delimiters are the slash (/) character. The path is
78
+ # split on the delimiter and each element of the path then describes the
79
+ # child of the current Element to traverse.
80
+ #
81
+ # Attributes are specified with an @ prefix.
82
+ #
83
+ # Each element name in the path can be followed by a bracket expression
84
+ # that narrows the paths to traverse. Supported expressions are numbers
85
+ # with a preceeding qualifier. Qualifiers are -, +, <, and >. The +
86
+ # qualifier is the default. A - qualifier indicates the index begins at
87
+ # the end of the children just like for Ruby Arrays. The < and >
88
+ # qualifiers indicates all elements either less than or greater than
89
+ # should be matched. Note that unlike XPath, the element index starts at 0
90
+ # similar to Ruby be contrary to XPath.
91
+ #
92
+ # Element names can also be wildcard characters. A * indicates any
93
+ # decendent should be followed. A ? indicates any single Element can
94
+ # match the wildcard.
95
+ #
96
+ # Examples are:
97
+ # * <code>element.locate("Family/Pete/*")</code> returns all children of the Pete Element.
98
+ # * <code>element.locate("Family/?[1]")</code> returns the first element in the Family Element.
99
+ # * <code>element.locate("Family/?[<3]")</code> returns the first 3 elements in the Family Element.
100
+ # * <code>element.locate("Family/?/@age")</code> returns the arg attribute for each child in the Family Element.
101
+ # * <code>element.locate("Family/*/@type")</code> returns the type attribute value for decendents of the Family.
102
+ #
103
+ # @param [String] path path to the Nodes to locate
104
+ def locate(path)
105
+ return [self] if path.nil?
106
+ found = []
107
+ pa = path.split('/')
108
+ alocate(pa, found)
109
+ found
110
+ end
111
+
112
+ # @param [Array] path array of steps in a path
113
+ # @param [Array] found matching nodes
114
+ def alocate(path, found)
115
+ #puts "*** locate_dig(#{path}, #{found})"
116
+ step = path[0]
117
+ #puts "*** #{step}"
118
+ if step.start_with?('@') # attribute
119
+ raise InvalidPath.new(path) unless 1 == path.size
120
+ step = step[1..-1]
121
+ sym_step = step.to_sym
122
+ @attributes.each do |k,v|
123
+ found << v if ('?' == step or k == step or k == sym_step)
124
+ end
125
+ else # element name
126
+ if (i = step.index('[')).nil? # just name
127
+ name = step
128
+ qual = nil
129
+ else
130
+ name = step[0..i-1]
131
+ raise InvalidPath.new(path) unless step.end_with?(']')
132
+ i += 1
133
+ qual = step[i]
134
+ if '0' <= qual and qual <= '9'
135
+ qual = '+'
136
+ else
137
+ i += 1
138
+ end
139
+ index = step[i..-2].to_i
140
+ end
141
+ if '?' == name or '*' == name
142
+ match = nodes
143
+ else
144
+ match = @nodes.select { |e| e.is_a?(Element) and name == e.name }
145
+ end
146
+ unless qual.nil? or match.empty?
147
+ case qual
148
+ when '+'
149
+ match = index < match.size ? [match[index]] : []
150
+ when '-'
151
+ match = index <= match.size ? [match[-index]] : []
152
+ when '<'
153
+ match = 0 < index ? match[0..index - 1] : []
154
+ when '>'
155
+ match = index <= match.size ? match[index + 1..-1] : []
156
+ else
157
+ raise InvalidPath.new(path)
158
+ end
159
+ end
160
+ if (1 == path.size)
161
+ match.each { |n| found << n }
162
+ elsif '*' == name
163
+ match.each { |n| n.alocate(path, found) if n.is_a?(Element) }
164
+ match.each { |n| n.alocate(path[1..-1], found) if n.is_a?(Element) }
165
+ else
166
+ match.each { |n| n.alocate(path[1..-1], found) if n.is_a?(Element) }
167
+ end
168
+ end
169
+ end
67
170
 
68
171
  end # Element
69
172
  end # Ox
@@ -0,0 +1,10 @@
1
+ module Ox
2
+
3
+ #
4
+ class InvalidPath < Exception
5
+ def initialize(path)
6
+ super("#{path.join('/')} is not a valid location.")
7
+ end
8
+
9
+ end # InvalidPath
10
+ end # Ox
@@ -1,5 +1,5 @@
1
1
 
2
2
  module Ox
3
3
  # Current version of the module.
4
- VERSION = '1.3.5'
4
+ VERSION = '1.4.0'
5
5
  end
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby -wW1
2
+
3
+ $: << '../lib'
4
+ $: << '../ext'
5
+
6
+ require 'ox'
7
+
8
+ def name_it()
9
+ begin
10
+ "x".foo
11
+ rescue Exception => e
12
+ #puts e.message
13
+ xml = Ox.dump(e, effort: :tolerant)
14
+ puts xml
15
+ o = Ox.load(xml, mode: :object)
16
+ puts o.message
17
+ puts Ox.dump(e)
18
+ end
19
+ end
20
+
21
+ name_it()
@@ -383,7 +383,137 @@ class Func < ::Test::Unit::TestCase
383
383
  obj = Ox.load(xml, :mode => :object) # should convert it to an object
384
384
  assert_equal(nil, obj)
385
385
  end
386
+
387
+ def locate_xml()
388
+ %{<?xml?>
389
+ <Family real="false">
390
+ <Pete age="57" type="male">
391
+ <Kid1 age="32"/>
392
+ <Kid2 age="31"/>
393
+ </Pete>
394
+ </Family>
395
+ }
396
+ end
397
+
398
+ def test_locate_self
399
+ doc = Ox.parse(locate_xml)
400
+ nodes = doc.locate(nil)
401
+ assert_equal(doc, nodes[0])
402
+ end
403
+
404
+ def test_locate_top
405
+ doc = Ox.parse(locate_xml)
406
+ nodes = doc.locate('Family')
407
+ assert_equal([doc.nodes[0]], nodes)
408
+ end
409
+
410
+ def test_locate_top_wild
411
+ doc = Ox.parse(locate_xml)
412
+ nodes = doc.locate('?')
413
+ assert_equal([doc.nodes[0]], nodes)
414
+ end
415
+
416
+ def test_locate_child
417
+ doc = Ox.parse(locate_xml)
418
+
419
+ nodes = doc.locate('Family/?')
420
+ assert_equal(['Pete'], nodes.map { |n| n.name })
421
+
422
+ nodes = doc.locate('Family/?/?')
423
+ assert_equal(['Kid1', 'Kid2'], nodes.map { |n| n.name })
424
+
425
+ nodes = doc.locate('Family/Pete/?')
426
+ assert_equal(['Kid1', 'Kid2'], nodes.map { |n| n.name })
427
+
428
+ nodes = doc.locate('Family/Makie/?')
429
+ assert_equal([], nodes.map { |n| n.name })
430
+ end
431
+
432
+ def test_locate_child_wild_wild
433
+ doc = Ox.parse(locate_xml)
434
+ nodes = doc.locate('Family/?/?')
435
+ assert_equal(['Kid1', 'Kid2'], nodes.map { |n| n.name })
436
+ end
437
+
438
+ def test_locate_child_wild
439
+ doc = Ox.parse(locate_xml)
440
+ nodes = doc.locate('Family/Pete/?')
441
+ assert_equal(['Kid1', 'Kid2'], nodes.map { |n| n.name })
442
+ end
443
+
444
+ def test_locate_child_wild_missing
445
+ doc = Ox.parse(locate_xml)
446
+ nodes = doc.locate('Family/Makie/?')
447
+ assert_equal([], nodes.map { |n| n.name })
448
+ end
449
+
450
+ def test_locate_attribute
451
+ doc = Ox.parse(locate_xml)
386
452
 
453
+ nodes = doc.locate('Family/@?')
454
+ assert_equal(['false'], nodes)
455
+
456
+ nodes = doc.locate('Family/@real')
457
+ assert_equal(['false'], nodes)
458
+
459
+ nodes = doc.locate('Family/Pete/@?')
460
+ assert_equal(['57', 'male'], nodes.sort)
461
+
462
+ nodes = doc.locate('Family/Pete/@age')
463
+ assert_equal(['57'], nodes)
464
+
465
+ nodes = doc.locate('Family/Makie/@?')
466
+ assert_equal([], nodes)
467
+
468
+ nodes = doc.locate('Family/Pete/?/@age')
469
+ assert_equal(['31', '32'], nodes.sort)
470
+
471
+ nodes = doc.locate('Family/*/@age')
472
+ assert_equal(['31', '32', '57'], nodes.sort)
473
+
474
+ nodes = doc.locate('*/@?')
475
+ assert_equal(['31', '32', '57', 'false', 'male'], nodes.sort)
476
+
477
+ assert_raise(::Ox::InvalidPath) {
478
+ nodes = doc.locate('Family/@age/?')
479
+ }
480
+
481
+ assert_raise(::Ox::InvalidPath) {
482
+ nodes = doc.locate('Family/?[/?')
483
+ }
484
+ end
485
+
486
+ def test_locate_qual_index
487
+ doc = Ox.parse(locate_xml)
488
+ nodes = doc.locate('Family/Pete/?[0]')
489
+ assert_equal(['Kid1'], nodes.map { |e| e.name } )
490
+ nodes = doc.locate('Family/Pete/?[1]')
491
+ assert_equal(['Kid2'], nodes.map { |e| e.name } )
492
+ end
493
+
494
+ def test_locate_qual_less
495
+ doc = Ox.parse(locate_xml)
496
+ nodes = doc.locate('Family/Pete/?[<1]')
497
+ assert_equal(['Kid1'], nodes.map { |e| e.name } )
498
+ end
499
+
500
+ def test_locate_qual_great
501
+ doc = Ox.parse(locate_xml)
502
+ nodes = doc.locate('Family/Pete/?[>0]')
503
+ assert_equal(['Kid2'], nodes.map { |e| e.name } )
504
+ end
505
+
506
+ def test_locate_qual_last
507
+ doc = Ox.parse(locate_xml)
508
+ nodes = doc.locate('Family/Pete/?[-1]')
509
+ assert_equal(['Kid2'], nodes.map { |e| e.name } )
510
+ end
511
+
512
+ def test_locate_qual_last_attr
513
+ doc = Ox.parse(locate_xml)
514
+ nodes = doc.locate('Family/Pete/?[-1]/@age')
515
+ assert_equal(['31'], nodes )
516
+ end
387
517
  end
388
518
 
389
519
  class Bag
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby -wW1
2
+
3
+ $: << '../lib'
4
+ $: << '../ext'
5
+
6
+ require 'stringio'
7
+ require 'ox'
8
+
9
+ class Sample < ::Ox::Sax
10
+ def start_element(name); puts "start: #{name}"; end
11
+ def end_element(name); puts "end: #{name}"; end
12
+ def attr(name, value); puts " #{name} => #{value}"; end
13
+ def text(value); puts "text #{value}"; end
14
+ end
15
+
16
+ io = StringIO.new(%{
17
+ <top name="sample">
18
+ <middle name="second">
19
+ <bottom name="third"/>
20
+ </middle>
21
+ </top>
22
+ })
23
+
24
+ handler = Sample.new()
25
+ Ox.sax_parse(handler, io)
26
+
27
+ # outputs
28
+ # start: top
29
+ # name => sample
30
+ # start: middle
31
+ # name => second
32
+ # start: bottom
33
+ # name => third
34
+ # end: bottom
35
+ # end: middle
36
+ # end: top
37
+
metadata CHANGED
@@ -1,30 +1,39 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: ox
3
- version: !ruby/object:Gem::Version
4
- version: 1.3.5
5
- prerelease:
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 4
8
+ - 0
9
+ version: 1.4.0
6
10
  platform: ruby
7
- authors:
11
+ authors:
8
12
  - Peter Ohler
9
13
  autorequire:
10
14
  bindir: bin
11
15
  cert_chain: []
12
- date: 2011-10-04 00:00:00.000000000Z
16
+
17
+ date: 2011-10-18 00:00:00 +09:00
18
+ default_executable:
13
19
  dependencies: []
20
+
14
21
  description: A fast XML parser and object serializer that uses only standard C lib.
15
22
  email: peter@ohler.com
16
23
  executables: []
17
- extensions:
24
+
25
+ extensions:
18
26
  - ext/ox/extconf.rb
19
- extra_rdoc_files:
27
+ extra_rdoc_files:
20
28
  - README.md
21
- files:
29
+ files:
22
30
  - lib/ox/bag.rb
23
31
  - lib/ox/cdata.rb
24
32
  - lib/ox/comment.rb
25
33
  - lib/ox/doctype.rb
26
34
  - lib/ox/document.rb
27
35
  - lib/ox/element.rb
36
+ - lib/ox/invalidpath.rb
28
37
  - lib/ox/node.rb
29
38
  - lib/ox/sax.rb
30
39
  - lib/ox/version.rb
@@ -47,6 +56,7 @@ files:
47
56
  - ext/ox/sax.c
48
57
  - test/bug1.rb
49
58
  - test/bug2.rb
59
+ - test/bug3.rb
50
60
  - test/cache16_test.rb
51
61
  - test/cache8_test.rb
52
62
  - test/cache_test.rb
@@ -74,36 +84,45 @@ files:
74
84
  - test/perf_sax.rb
75
85
  - test/perf_write.rb
76
86
  - test/sample.rb
87
+ - test/sax_example.rb
77
88
  - test/sax_test.rb
78
89
  - test/test.rb
79
90
  - test/Sample.graffle
80
91
  - LICENSE
81
92
  - README.md
93
+ has_rdoc: true
82
94
  homepage: https://github.com/ohler55/ox
83
95
  licenses: []
96
+
84
97
  post_install_message:
85
- rdoc_options:
98
+ rdoc_options:
86
99
  - --main
87
100
  - README.md
88
- require_paths:
101
+ require_paths:
89
102
  - lib
90
103
  - ext
91
- required_ruby_version: !ruby/object:Gem::Requirement
104
+ required_ruby_version: !ruby/object:Gem::Requirement
92
105
  none: false
93
- requirements:
94
- - - ! '>='
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
- required_rubygems_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ segments:
110
+ - 0
111
+ version: "0"
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
113
  none: false
99
- requirements:
100
- - - ! '>='
101
- - !ruby/object:Gem::Version
102
- version: '0'
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ segments:
118
+ - 0
119
+ version: "0"
103
120
  requirements: []
121
+
104
122
  rubyforge_project: ox
105
- rubygems_version: 1.8.6
123
+ rubygems_version: 1.3.7
106
124
  signing_key:
107
125
  specification_version: 3
108
126
  summary: A fast XML parser and object serializer.
109
127
  test_files: []
128
+