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.
@@ -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
@@ -0,0 +1,3 @@
1
+ module SXRB
2
+ class TagMismatchError < StandardError; end
3
+ end
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
@@ -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
@@ -0,0 +1,15 @@
1
+ module SXRB
2
+ class TextNode < Node
3
+ def initialize(text)
4
+ @text = text || ""
5
+ end
6
+
7
+ def append_text(text)
8
+ @text << text
9
+ end
10
+
11
+ def text
12
+ @text
13
+ end
14
+ end
15
+ end
data/lib/sxrb.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'sxrb/parser'
2
+ require 'sxrb/errors'
3
+
4
+ module SXRB; end
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
+