sxrb 0.1.0
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/sxrb/callbacks.rb +93 -0
- data/lib/sxrb/errors.rb +3 -0
- data/lib/sxrb/node.rb +14 -0
- data/lib/sxrb/parser.rb +25 -0
- data/lib/sxrb/proxy.rb +41 -0
- data/lib/sxrb/text_node.rb +15 -0
- data/lib/sxrb.rb +4 -0
- metadata +71 -0
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'libxml'
|
2
|
+
require 'sxrb/node'
|
3
|
+
require 'sxrb/text_node'
|
4
|
+
|
5
|
+
# This class provides callbacks for SAX API which are configured with sxrb DSL.
|
6
|
+
|
7
|
+
module SXRB
|
8
|
+
class Callbacks
|
9
|
+
#include LibXML::XML::SaxParser::VerboseCallbacks
|
10
|
+
include LibXML::XML::SaxParser::Callbacks
|
11
|
+
def initialize
|
12
|
+
@stack = []
|
13
|
+
@rules_map = Hash.new {|h,k| h[k] = Rules.new}
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_start_element_ns(name, attributes, prefix, uri, namespaces)
|
17
|
+
Node.new(name, attributes, prefix, uri, namespaces).tap do |node|
|
18
|
+
@stack.push(node)
|
19
|
+
invoke_callback(:start, node)
|
20
|
+
@current_element.append node if @current_element
|
21
|
+
@current_element = node if current_matching_rules.any?(&:recursive) || @current_element
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_characters(chars)
|
26
|
+
if @stack.last.is_a? TextNode
|
27
|
+
@stack.last.append_text chars
|
28
|
+
else
|
29
|
+
TextNode.new(chars).tap do |node|
|
30
|
+
invoke_callback(:characters, node)
|
31
|
+
@stack.push node
|
32
|
+
@current_element.append node if @current_element
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def on_end_element_ns(name, prefix, uri)
|
38
|
+
@stack.pop if @stack.last.is_a? TextNode
|
39
|
+
|
40
|
+
if @current_element
|
41
|
+
invoke_callback(:element, @current_element) if current_matching_rules.any?(&:recursive)
|
42
|
+
@current_element = @current_element.parent
|
43
|
+
end
|
44
|
+
|
45
|
+
invoke_callback(:end, @stack.last)
|
46
|
+
@stack.pop.tap do |node|
|
47
|
+
raise SXRB::TagMismatchError if node.name != name
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def add_callback(type, rule_path, &block)
|
52
|
+
@rules_map[Regexp.new "^#{rule_path}$"].tap do |rules|
|
53
|
+
rules.rules[type] = block
|
54
|
+
rules.recursive = (type == :element) # true / false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# options:
|
59
|
+
# recursive
|
60
|
+
#
|
61
|
+
def add_rule(rule, rule_path, options)
|
62
|
+
operator = options[:recursive] ? '.*' : ' '
|
63
|
+
new_rule = rule_path + operator + rule
|
64
|
+
new_rule.strip
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def invoke_callback(type, *args)
|
70
|
+
current_matching_rules.
|
71
|
+
map {|value| value.rules[type]}.
|
72
|
+
compact.each {|callback| callback.call(*args)}
|
73
|
+
end
|
74
|
+
|
75
|
+
def current_matching_rules
|
76
|
+
@rules_map.each_pair.
|
77
|
+
select {|rule, value| current_rule_path =~ rule}.
|
78
|
+
map {|rule, value| value}
|
79
|
+
end
|
80
|
+
|
81
|
+
def current_rule_path
|
82
|
+
@stack.map(&:name).join(' ')
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class Rules
|
87
|
+
attr_accessor :rules, :recursive
|
88
|
+
def initialize
|
89
|
+
@rules = {}
|
90
|
+
@recursive = false
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/lib/sxrb/errors.rb
ADDED
data/lib/sxrb/node.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
module SXRB
|
2
|
+
class Node < Struct.new(:name, :attributes, :prefix, :uri, :namespaces, :parent)
|
3
|
+
attr_accessor :children
|
4
|
+
def initialize(*args, &block)
|
5
|
+
super(*args, &block)
|
6
|
+
@children = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def append(node)
|
10
|
+
node.parent = self
|
11
|
+
@children << node
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/sxrb/parser.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'sxrb/callbacks'
|
2
|
+
require 'sxrb/proxy'
|
3
|
+
|
4
|
+
module SXRB
|
5
|
+
class Parser
|
6
|
+
def initialize(input, opts = {}, &block)
|
7
|
+
raise ArgumentError.new("Block expected") if block.nil?
|
8
|
+
options = {:mode => :string}.merge(opts)
|
9
|
+
|
10
|
+
# Create parser according to options
|
11
|
+
|
12
|
+
parser = case options[:mode]
|
13
|
+
when :string
|
14
|
+
LibXML::XML::SaxParser.string(input)
|
15
|
+
end
|
16
|
+
|
17
|
+
callbacks = Callbacks.new.tap do |cb|
|
18
|
+
yield Proxy.new(cb)
|
19
|
+
end
|
20
|
+
|
21
|
+
parser.callbacks = callbacks
|
22
|
+
parser.parse
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/sxrb/proxy.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
module SXRB
|
2
|
+
class Proxy
|
3
|
+
def initialize(callback_tree, current_path = '')
|
4
|
+
@callback_tree = callback_tree
|
5
|
+
@current_path = current_path
|
6
|
+
end
|
7
|
+
|
8
|
+
def child(*args, &block)
|
9
|
+
args.each do |tag|
|
10
|
+
@callback_tree.add_rule(tag, @current_path, :recursive => false).tap do |new_path|
|
11
|
+
block.call(Proxy.new(@callback_tree, new_path))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def descendant(*args, &block)
|
17
|
+
args.each do |tag|
|
18
|
+
@callback_tree.add_rule(tag, @current_path, :recursive => true).tap do |new_path|
|
19
|
+
block.call(Proxy.new(@callback_tree, new_path))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def on_element(&block)
|
25
|
+
@callback_tree.add_callback(:element, @current_path, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def on_start(&block)
|
29
|
+
@callback_tree.add_callback(:start, @current_path, &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def on_characters(&block)
|
33
|
+
@callback_tree.add_callback(:characters, @current_path, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def on_end(&block)
|
37
|
+
@callback_tree.add_callback(:end, @current_path, &block)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
data/lib/sxrb.rb
ADDED
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sxrb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Bartek Borkowski
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-08-05 00:00:00 Z
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Robust XML parser that allows defining desired behavior with fancy DSL
|
22
|
+
email:
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files: []
|
28
|
+
|
29
|
+
files:
|
30
|
+
- lib/sxrb/text_node.rb
|
31
|
+
- lib/sxrb/callbacks.rb
|
32
|
+
- lib/sxrb/node.rb
|
33
|
+
- lib/sxrb/parser.rb
|
34
|
+
- lib/sxrb/errors.rb
|
35
|
+
- lib/sxrb/proxy.rb
|
36
|
+
- lib/sxrb.rb
|
37
|
+
homepage: http://rubygems.org/gems/sxrb
|
38
|
+
licenses: []
|
39
|
+
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
none: false
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
hash: 3
|
51
|
+
segments:
|
52
|
+
- 0
|
53
|
+
version: "0"
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 3
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.8.24
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: Smart XML parser
|
70
|
+
test_files: []
|
71
|
+
|