sax-machine 0.0.16 → 0.0.20
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/Gemfile +1 -1
- data/README.textile +9 -7
- data/Rakefile +3 -1
- data/lib/sax-machine/sax_attribute_config.rb +40 -0
- data/lib/sax-machine/sax_config.rb +41 -14
- data/lib/sax-machine/sax_document.rb +51 -26
- data/lib/sax-machine/sax_element_config.rb +9 -1
- data/lib/sax-machine/sax_element_value_config.rb +24 -0
- data/lib/sax-machine/sax_handler.rb +45 -5
- data/lib/sax-machine/sax_parent_config.rb +21 -0
- data/spec/sax-machine/sax_document_spec.rb +213 -2
- data/spec/spec_helper.rb +4 -5
- metadata +11 -11
- data/.rspec +0 -2
- data/Gemfile.lock +0 -20
data/Gemfile
CHANGED
data/README.textile
CHANGED
@@ -23,6 +23,7 @@ class AtomEntry
|
|
23
23
|
element :summary
|
24
24
|
element :content
|
25
25
|
element :published
|
26
|
+
parent :parent
|
26
27
|
end
|
27
28
|
|
28
29
|
# Class for parsing Atom feeds
|
@@ -43,10 +44,11 @@ feed = Atom.parse(xml_text)
|
|
43
44
|
feed.title # => whatever the title of the blog is
|
44
45
|
feed.url # => the main url of the blog
|
45
46
|
feed.feed_url # => goes to the feedburner feed
|
46
|
-
|
47
|
+
|
47
48
|
feed.entries.first.title # => title of the first entry
|
48
49
|
feed.entries.first.author # => the author of the first entry
|
49
50
|
feed.entries.first.url # => the permalink on the blog for this entry
|
51
|
+
feed.entries.first.parent # => the Atom parent
|
50
52
|
# etc ...
|
51
53
|
|
52
54
|
# you can also use the elements method without specifying a class like so
|
@@ -62,11 +64,11 @@ response.messages.last # => "world"
|
|
62
64
|
h2. LICENSE
|
63
65
|
|
64
66
|
(The MIT License)
|
65
|
-
|
66
|
-
Copyright (c) 2009:
|
67
|
-
|
67
|
+
|
68
|
+
Copyright (c) 2009 - 2011:
|
69
|
+
|
68
70
|
"Paul Dix":http://pauldix.net
|
69
|
-
|
71
|
+
|
70
72
|
Permission is hereby granted, free of charge, to any person obtaining
|
71
73
|
a copy of this software and associated documentation files (the
|
72
74
|
'Software'), to deal in the Software without restriction, including
|
@@ -74,10 +76,10 @@ without limitation the rights to use, copy, modify, merge, publish,
|
|
74
76
|
distribute, sublicense, and/or sell copies of the Software, and to
|
75
77
|
permit persons to whom the Software is furnished to do so, subject to
|
76
78
|
the following conditions:
|
77
|
-
|
79
|
+
|
78
80
|
The above copyright notice and this permission notice shall be
|
79
81
|
included in all copies or substantial portions of the Software.
|
80
|
-
|
82
|
+
|
81
83
|
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
82
84
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
83
85
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
data/Rakefile
CHANGED
@@ -0,0 +1,40 @@
|
|
1
|
+
module SAXMachine
|
2
|
+
class SAXConfig
|
3
|
+
|
4
|
+
class AttributeConfig
|
5
|
+
attr_reader :name, :setter
|
6
|
+
|
7
|
+
def initialize(name, options)
|
8
|
+
@name = name.to_s
|
9
|
+
@as = options[:as]
|
10
|
+
@setter = "#{@as}="
|
11
|
+
@required = options[:required]
|
12
|
+
end
|
13
|
+
|
14
|
+
def column
|
15
|
+
@as || @name.to_sym
|
16
|
+
end
|
17
|
+
|
18
|
+
def required?
|
19
|
+
@required
|
20
|
+
end
|
21
|
+
|
22
|
+
def value_from_attrs(attrs)
|
23
|
+
attrs.index(@name) ? attrs[attrs.index(@name) + 1] : nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def attrs_match?(attrs)
|
27
|
+
attrs.index(@name) ? true : false
|
28
|
+
end
|
29
|
+
|
30
|
+
def has_value_and_attrs_match?(attrs)
|
31
|
+
attrs_match?(attrs)
|
32
|
+
end
|
33
|
+
|
34
|
+
def collection?
|
35
|
+
false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -1,47 +1,74 @@
|
|
1
|
+
require "sax-machine/sax_attribute_config"
|
2
|
+
require "sax-machine/sax_element_value_config"
|
1
3
|
require "sax-machine/sax_element_config"
|
2
4
|
require "sax-machine/sax_collection_config"
|
5
|
+
require "sax-machine/sax_parent_config"
|
3
6
|
|
4
7
|
module SAXMachine
|
5
8
|
class SAXConfig
|
6
|
-
|
7
|
-
|
9
|
+
|
10
|
+
attr_accessor :top_level_elements, :top_level_attributes, :top_level_element_value, :collection_elements, :parents
|
11
|
+
|
8
12
|
def initialize
|
9
|
-
|
10
|
-
@
|
13
|
+
# Default value is an empty array
|
14
|
+
@top_level_elements = Hash.new { |hash, key| hash[key] = [] }
|
15
|
+
@top_level_attributes = []
|
16
|
+
@top_level_element_value = []
|
17
|
+
@collection_elements = Hash.new { |hash, key| hash[key] = [] }
|
18
|
+
@parents = []
|
11
19
|
end
|
12
|
-
|
20
|
+
|
13
21
|
def columns
|
14
22
|
@top_level_elements.map {|name, ecs| ecs }.flatten
|
15
23
|
end
|
16
|
-
|
24
|
+
|
17
25
|
def initialize_copy(sax_config)
|
26
|
+
super
|
18
27
|
@top_level_elements = sax_config.top_level_elements.clone
|
28
|
+
@top_level_attributes = sax_config.top_level_attributes.clone
|
29
|
+
@top_level_element_value = sax_config.top_level_element_value.clone
|
19
30
|
@collection_elements = sax_config.collection_elements.clone
|
31
|
+
@parents = sax_config.parents.clone
|
20
32
|
end
|
21
33
|
|
22
34
|
def add_top_level_element(name, options)
|
23
|
-
@top_level_elements[name.to_s] = [] unless @top_level_elements[name.to_s]
|
24
35
|
@top_level_elements[name.to_s] << ElementConfig.new(name, options)
|
25
36
|
end
|
26
37
|
|
38
|
+
def add_top_level_attribute(name, options)
|
39
|
+
@top_level_attributes << AttributeConfig.new(options.delete(:name), options)
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_top_level_element_value(name, options)
|
43
|
+
@top_level_element_value << ElementValueConfig.new(options.delete(:name), options)
|
44
|
+
end
|
45
|
+
|
27
46
|
def add_collection_element(name, options)
|
28
|
-
@collection_elements[name.to_s] = [] unless @collection_elements[name.to_s]
|
29
47
|
@collection_elements[name.to_s] << CollectionConfig.new(name, options)
|
30
48
|
end
|
31
49
|
|
50
|
+
def add_parent(name, options)
|
51
|
+
@parents << ParentConfig.new(name, options)
|
52
|
+
end
|
53
|
+
|
32
54
|
def collection_config(name, attrs)
|
33
|
-
|
34
|
-
|
55
|
+
@collection_elements[name.to_s].detect { |cc| cc.attrs_match?(attrs) }
|
56
|
+
end
|
57
|
+
|
58
|
+
def attribute_configs_for_element(attrs)
|
59
|
+
@top_level_attributes.select { |aa| aa.attrs_match?(attrs) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def element_values_for_element
|
63
|
+
@top_level_element_value
|
35
64
|
end
|
36
65
|
|
37
66
|
def element_configs_for_attribute(name, attrs)
|
38
|
-
|
39
|
-
tes && tes.select { |ec| ec.has_value_and_attrs_match?(attrs) } || []
|
67
|
+
@top_level_elements[name.to_s].select { |ec| ec.has_value_and_attrs_match?(attrs) }
|
40
68
|
end
|
41
69
|
|
42
70
|
def element_config_for_tag(name, attrs)
|
43
|
-
|
44
|
-
tes && tes.detect { |ec| ec.attrs_match?(attrs) }
|
71
|
+
@top_level_elements[name.to_s].detect { |ec| ec.attrs_match?(attrs) }
|
45
72
|
end
|
46
73
|
end
|
47
74
|
end
|
@@ -1,33 +1,50 @@
|
|
1
1
|
require "nokogiri"
|
2
2
|
|
3
3
|
module SAXMachine
|
4
|
-
|
4
|
+
|
5
5
|
def self.included(base)
|
6
6
|
base.extend ClassMethods
|
7
7
|
end
|
8
|
-
|
9
|
-
def parse(xml_text)
|
10
|
-
sax_handler = SAXHandler.new(self)
|
8
|
+
|
9
|
+
def parse(xml_text, on_error = nil, on_warning = nil)
|
10
|
+
sax_handler = SAXHandler.new(self, on_error, on_warning)
|
11
11
|
parser = Nokogiri::XML::SAX::Parser.new(sax_handler)
|
12
12
|
parser.parse(xml_text)
|
13
13
|
self
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
module ClassMethods
|
17
17
|
|
18
|
-
def
|
19
|
-
|
18
|
+
def inherited(subclass)
|
19
|
+
subclass.sax_config.send(:initialize_copy, self.sax_config)
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
|
+
def parse(xml_text, on_error = nil, on_warning = nil)
|
23
|
+
new.parse(xml_text, on_error, on_warning)
|
24
|
+
end
|
25
|
+
|
22
26
|
def element(name, options = {})
|
23
|
-
options[:as] ||= name
|
27
|
+
real_name = (options[:as] ||= name).to_s
|
24
28
|
sax_config.add_top_level_element(name, options)
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
create_attr real_name
|
30
|
+
end
|
31
|
+
|
32
|
+
def attribute(name, options = {})
|
33
|
+
real_name = (options[:as] ||= name).to_s
|
34
|
+
sax_config.add_top_level_attribute(self.class.to_s, options.merge(:name => name))
|
35
|
+
create_attr real_name
|
36
|
+
end
|
37
|
+
|
38
|
+
def value(name, options = {})
|
39
|
+
real_name = (options[:as] ||= name).to_s
|
40
|
+
sax_config.add_top_level_element_value(self.class.to_s, options.merge(:name => name))
|
41
|
+
create_attr real_name
|
42
|
+
end
|
43
|
+
|
44
|
+
def parent(name, options = {})
|
45
|
+
real_name = (options[:as] ||= name).to_s
|
46
|
+
sax_config.add_parent(name, options)
|
47
|
+
create_attr(real_name)
|
31
48
|
end
|
32
49
|
|
33
50
|
def columns
|
@@ -35,7 +52,7 @@ module SAXMachine
|
|
35
52
|
end
|
36
53
|
|
37
54
|
def column(sym)
|
38
|
-
columns.select{|c| c.column == sym}[0]
|
55
|
+
columns.select { |c| c.column == sym }[0]
|
39
56
|
end
|
40
57
|
|
41
58
|
def data_class(sym)
|
@@ -47,9 +64,9 @@ module SAXMachine
|
|
47
64
|
end
|
48
65
|
|
49
66
|
def column_names
|
50
|
-
columns.map{|e| e.column}
|
67
|
+
columns.map { |e| e.column }
|
51
68
|
end
|
52
|
-
|
69
|
+
|
53
70
|
def elements(name, options = {})
|
54
71
|
options[:as] ||= name
|
55
72
|
if options[:class]
|
@@ -62,21 +79,29 @@ module SAXMachine
|
|
62
79
|
SRC
|
63
80
|
sax_config.add_top_level_element(name, options.merge(:collection => true))
|
64
81
|
end
|
65
|
-
|
66
|
-
if !
|
67
|
-
|
82
|
+
|
83
|
+
if !method_defined?(options[:as].to_s)
|
84
|
+
class_eval <<-SRC
|
68
85
|
def #{options[:as]}
|
69
86
|
@#{options[:as]} ||= []
|
70
87
|
end
|
71
88
|
SRC
|
72
89
|
end
|
73
|
-
|
74
|
-
attr_writer options[:as] unless
|
90
|
+
|
91
|
+
attr_writer options[:as] unless method_defined?("#{options[:as]}=")
|
75
92
|
end
|
76
|
-
|
93
|
+
|
77
94
|
def sax_config
|
78
95
|
@sax_config ||= SAXConfig.new
|
79
96
|
end
|
97
|
+
|
98
|
+
# we only want to insert the getter and setter if they haven't defined it from elsewhere.
|
99
|
+
# this is how we allow custom parsing behavior. So you could define the setter
|
100
|
+
# and have it parse the string into a date or whatever.
|
101
|
+
def create_attr real_name
|
102
|
+
attr_reader real_name unless method_defined?(real_name)
|
103
|
+
attr_writer real_name unless method_defined?("#{real_name}=")
|
104
|
+
end
|
80
105
|
end
|
81
|
-
|
82
|
-
end
|
106
|
+
|
107
|
+
end
|
@@ -31,6 +31,14 @@ module SAXMachine
|
|
31
31
|
@data_class = options[:class]
|
32
32
|
@required = options[:required]
|
33
33
|
end
|
34
|
+
|
35
|
+
def value_configured?
|
36
|
+
!@value.nil?
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
"name: #{@name} dataclass: #{@data_class} setter: #{@setter} required: #{@required} value: #{@value} as:#{@as} collection: #{@collection} with: #{@with}"
|
41
|
+
end
|
34
42
|
|
35
43
|
def column
|
36
44
|
@as || @name.to_sym
|
@@ -62,4 +70,4 @@ module SAXMachine
|
|
62
70
|
end
|
63
71
|
|
64
72
|
end
|
65
|
-
end
|
73
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module SAXMachine
|
2
|
+
class SAXConfig
|
3
|
+
|
4
|
+
class ElementValueConfig
|
5
|
+
attr_reader :name, :setter
|
6
|
+
|
7
|
+
def initialize(name, options)
|
8
|
+
@name = name.to_s
|
9
|
+
@as = options[:as]
|
10
|
+
@setter = "#{@as}="
|
11
|
+
@required = options[:required]
|
12
|
+
end
|
13
|
+
|
14
|
+
def column
|
15
|
+
@as || @name.to_sym
|
16
|
+
end
|
17
|
+
|
18
|
+
def required?
|
19
|
+
@required
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -4,9 +4,11 @@ module SAXMachine
|
|
4
4
|
class SAXHandler < Nokogiri::XML::SAX::Document
|
5
5
|
attr_reader :stack
|
6
6
|
|
7
|
-
def initialize(object)
|
8
|
-
@stack = [[object, nil,
|
7
|
+
def initialize(object, on_error = nil, on_warning = nil)
|
8
|
+
@stack = [[object, nil, String.new]]
|
9
9
|
@parsed_configs = {}
|
10
|
+
@on_error = on_error
|
11
|
+
@on_warning = on_warning
|
10
12
|
end
|
11
13
|
|
12
14
|
def characters(string)
|
@@ -25,8 +27,12 @@ module SAXMachine
|
|
25
27
|
|
26
28
|
if sax_config
|
27
29
|
if collection_config = sax_config.collection_config(name, attrs)
|
28
|
-
stack.push [object = collection_config.data_class.new, collection_config,
|
30
|
+
stack.push [object = collection_config.data_class.new, collection_config, String.new]
|
29
31
|
object, sax_config, is_collection = object, object.class.sax_config, true
|
32
|
+
|
33
|
+
if (attribute_config = object.class.respond_to?(:sax_config) && object.class.sax_config.attribute_configs_for_element(attrs))
|
34
|
+
attribute_config.each { |ac| object.send(ac.setter, ac.value_from_attrs(attrs)) }
|
35
|
+
end
|
30
36
|
end
|
31
37
|
sax_config.element_configs_for_attribute(name, attrs).each do |ec|
|
32
38
|
unless parsed_config?(object, ec)
|
@@ -35,7 +41,12 @@ module SAXMachine
|
|
35
41
|
end
|
36
42
|
end
|
37
43
|
if !collection_config && element_config = sax_config.element_config_for_tag(name, attrs)
|
38
|
-
|
44
|
+
new_object = element_config.data_class ? element_config.data_class.new : object
|
45
|
+
stack.push [new_object, element_config, String.new]
|
46
|
+
|
47
|
+
if (attribute_config = new_object.class.respond_to?(:sax_config) && new_object.class.sax_config.attribute_configs_for_element(attrs))
|
48
|
+
attribute_config.each { |ac| new_object.send(ac.setter, ac.value_from_attrs(attrs)) }
|
49
|
+
end
|
39
50
|
end
|
40
51
|
end
|
41
52
|
end
|
@@ -45,13 +56,29 @@ module SAXMachine
|
|
45
56
|
return unless stack.size > 1 && config && config.name.to_s == name.to_s
|
46
57
|
|
47
58
|
unless parsed_config?(object, config)
|
59
|
+
if (element_value_config = config.data_class.respond_to?(:sax_config) && config.data_class.sax_config.element_values_for_element)
|
60
|
+
element_value_config.each { |evc| element.send(evc.setter, value) }
|
61
|
+
end
|
62
|
+
|
48
63
|
if config.respond_to?(:accessor)
|
64
|
+
subconfig = element.class.sax_config if element.class.respond_to?(:sax_config)
|
65
|
+
if econf = subconfig.element_config_for_tag(name, [])
|
66
|
+
element.send(econf.setter, value) unless econf.value_configured?
|
67
|
+
end
|
49
68
|
object.send(config.accessor) << element
|
50
69
|
else
|
51
70
|
value = config.data_class ? element : value
|
52
71
|
object.send(config.setter, value) unless value == ""
|
53
72
|
mark_as_parsed(object, config)
|
54
73
|
end
|
74
|
+
|
75
|
+
# try to set the parent
|
76
|
+
sax_config = element.class.respond_to?(:sax_config) ? element.class.sax_config : nil
|
77
|
+
if sax_config
|
78
|
+
sax_config.parents.each do |parent|
|
79
|
+
element.send(parent.setter, object)
|
80
|
+
end
|
81
|
+
end
|
55
82
|
end
|
56
83
|
stack.pop
|
57
84
|
end
|
@@ -63,5 +90,18 @@ module SAXMachine
|
|
63
90
|
def parsed_config?(object, element_config)
|
64
91
|
@parsed_configs[[object.object_id, element_config.object_id]]
|
65
92
|
end
|
93
|
+
|
94
|
+
def warning string
|
95
|
+
if @on_warning
|
96
|
+
@on_warning.call(string)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def error string
|
101
|
+
if @on_error
|
102
|
+
@on_error.call(string)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
66
106
|
end
|
67
|
-
end
|
107
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module SAXMachine
|
2
|
+
class SAXConfig
|
3
|
+
|
4
|
+
class ParentConfig
|
5
|
+
attr_reader :name, :setter
|
6
|
+
|
7
|
+
def initialize(name, options)
|
8
|
+
@name = name.to_s
|
9
|
+
|
10
|
+
@as = options[:as]
|
11
|
+
@setter = "#{@as}="
|
12
|
+
end
|
13
|
+
|
14
|
+
def column
|
15
|
+
@as || @name.to_sym
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -20,11 +20,26 @@ describe "SAXMachine" do
|
|
20
20
|
@klass.column_names.should =~ [:title]
|
21
21
|
end
|
22
22
|
|
23
|
+
it "should not overwrite the getter is there is already one present" do
|
24
|
+
@klass = Class.new do
|
25
|
+
def title
|
26
|
+
"#{@title} ***"
|
27
|
+
end
|
28
|
+
|
29
|
+
include SAXMachine
|
30
|
+
element :title
|
31
|
+
end
|
32
|
+
document = @klass.new
|
33
|
+
document.title = "Title"
|
34
|
+
document.title.should == "Title ***"
|
35
|
+
end
|
36
|
+
|
23
37
|
it "should not overwrite the setter if there is already one present" do
|
24
38
|
@klass = Class.new do
|
25
39
|
def title=(val)
|
26
40
|
@title = "#{val} **"
|
27
41
|
end
|
42
|
+
|
28
43
|
include SAXMachine
|
29
44
|
element :title
|
30
45
|
end
|
@@ -105,6 +120,36 @@ describe "SAXMachine" do
|
|
105
120
|
document.name.should == "Paul"
|
106
121
|
document.title.should == "My Title"
|
107
122
|
end
|
123
|
+
|
124
|
+
|
125
|
+
it "should not overwrite the getter is there is already one present" do
|
126
|
+
@klass = Class.new do
|
127
|
+
def items
|
128
|
+
[]
|
129
|
+
end
|
130
|
+
|
131
|
+
include SAXMachine
|
132
|
+
elements :items
|
133
|
+
end
|
134
|
+
document = @klass.new
|
135
|
+
document.items = [1, 2, 3, 4]
|
136
|
+
document.items.should == []
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should not overwrite the setter if there is already one present" do
|
140
|
+
@klass = Class.new do
|
141
|
+
def items=(val)
|
142
|
+
@items = [1, *val]
|
143
|
+
end
|
144
|
+
|
145
|
+
include SAXMachine
|
146
|
+
elements :items
|
147
|
+
end
|
148
|
+
document = @klass.new
|
149
|
+
document.items = [2, 3]
|
150
|
+
document.items.should == [1, 2, 3]
|
151
|
+
end
|
152
|
+
|
108
153
|
end
|
109
154
|
|
110
155
|
describe "when using options for parsing elements" do
|
@@ -382,6 +427,23 @@ describe "SAXMachine" do
|
|
382
427
|
document.entries.first.title.should == "correct title"
|
383
428
|
end
|
384
429
|
|
430
|
+
it "should parse elements, and make attributes and inner text available" do
|
431
|
+
class Related
|
432
|
+
include SAXMachine
|
433
|
+
element 'related', :as=>:item
|
434
|
+
element 'related', :as=>:attr, :value=>'attr'
|
435
|
+
end
|
436
|
+
class Foo
|
437
|
+
elements 'related', :as=>'items', :class=>Related
|
438
|
+
end
|
439
|
+
|
440
|
+
doc = Foo.parse(%{<xml><collection><related attr='baz'>something</related><related>somethingelse</related></collection></xml>})
|
441
|
+
doc.items.first.should_not be_nil
|
442
|
+
doc.items.size.should == 2
|
443
|
+
doc.items.first.item.should == 'something'
|
444
|
+
doc.items.last.item.should == 'somethingelse'
|
445
|
+
end
|
446
|
+
|
385
447
|
it "should parse out an attribute value from the tag that starts the collection" do
|
386
448
|
class Foo
|
387
449
|
element :entry, :value => :href, :as => :url
|
@@ -392,7 +454,6 @@ describe "SAXMachine" do
|
|
392
454
|
document.entries.first.url.should == "http://pauldix.net"
|
393
455
|
end
|
394
456
|
end
|
395
|
-
|
396
457
|
end
|
397
458
|
|
398
459
|
describe "full example" do
|
@@ -437,13 +498,15 @@ describe "SAXMachine" do
|
|
437
498
|
</category>
|
438
499
|
</categories>
|
439
500
|
]
|
440
|
-
class CategoryCollection;
|
501
|
+
class CategoryCollection;
|
502
|
+
end
|
441
503
|
class Category
|
442
504
|
include SAXMachine
|
443
505
|
attr_accessor :id
|
444
506
|
element :category, :value => :id, :as => :id
|
445
507
|
element :title
|
446
508
|
element :categories, :as => :collection, :class => CategoryCollection
|
509
|
+
parent :parent
|
447
510
|
end
|
448
511
|
class CategoryCollection
|
449
512
|
include SAXMachine
|
@@ -455,6 +518,7 @@ describe "SAXMachine" do
|
|
455
518
|
it "should parse the first category" do
|
456
519
|
@collection.categories.first.id.should == "1"
|
457
520
|
@collection.categories.first.title.should == "First"
|
521
|
+
@collection.categories.first.parent.should == @collection
|
458
522
|
end
|
459
523
|
|
460
524
|
it "should parse the nested category" do
|
@@ -524,4 +588,151 @@ describe "SAXMachine" do
|
|
524
588
|
@item.title.should == "Hello"
|
525
589
|
end
|
526
590
|
end
|
591
|
+
|
592
|
+
describe "with config to pull multiple attributes" do
|
593
|
+
before do
|
594
|
+
@xml = %[
|
595
|
+
<item id="1">
|
596
|
+
<author name="John Doe" role="writer" />
|
597
|
+
</item>
|
598
|
+
]
|
599
|
+
class AuthorElement
|
600
|
+
include SAXMachine
|
601
|
+
attribute :name
|
602
|
+
attribute :role
|
603
|
+
end
|
604
|
+
class ItemElement
|
605
|
+
include SAXMachine
|
606
|
+
element :author, :class => AuthorElement
|
607
|
+
end
|
608
|
+
@item = ItemElement.parse(@xml)
|
609
|
+
end
|
610
|
+
|
611
|
+
it 'should have the child element' do
|
612
|
+
@item.author.should_not be_nil
|
613
|
+
end
|
614
|
+
|
615
|
+
it 'should have the author name' do
|
616
|
+
@item.author.name.should == 'John Doe'
|
617
|
+
end
|
618
|
+
|
619
|
+
it 'should have the author role' do
|
620
|
+
@item.author.role.should == 'writer'
|
621
|
+
end
|
622
|
+
end
|
623
|
+
|
624
|
+
describe "with multiple elements and multiple attributes" do
|
625
|
+
before do
|
626
|
+
@xml = %[
|
627
|
+
<item id="1">
|
628
|
+
<author name="John Doe" role="writer" />
|
629
|
+
<author name="Jane Doe" role="artist" />
|
630
|
+
</item>
|
631
|
+
]
|
632
|
+
class AuthorElement2
|
633
|
+
include SAXMachine
|
634
|
+
attribute :name
|
635
|
+
attribute :role
|
636
|
+
end
|
637
|
+
class ItemElement2
|
638
|
+
include SAXMachine
|
639
|
+
elements :author, :as => :authors, :class => AuthorElement2
|
640
|
+
end
|
641
|
+
@item = ItemElement2.parse(@xml)
|
642
|
+
end
|
643
|
+
|
644
|
+
it 'should have the child elements' do
|
645
|
+
@item.authors.should_not be_nil
|
646
|
+
@item.authors.count.should == 2
|
647
|
+
end
|
648
|
+
|
649
|
+
it 'should have the author names' do
|
650
|
+
@item.authors.first.name.should == 'John Doe'
|
651
|
+
@item.authors.last.name.should == 'Jane Doe'
|
652
|
+
end
|
653
|
+
|
654
|
+
it 'should have the author roles' do
|
655
|
+
@item.authors.first.role.should == 'writer'
|
656
|
+
@item.authors.last.role.should == 'artist'
|
657
|
+
end
|
658
|
+
end
|
659
|
+
|
660
|
+
describe "with mixed attributes and element values" do
|
661
|
+
before do
|
662
|
+
@xml = %[
|
663
|
+
<item id="1">
|
664
|
+
<author role="writer">John Doe</author>
|
665
|
+
</item>
|
666
|
+
]
|
667
|
+
class AuthorElement3
|
668
|
+
include SAXMachine
|
669
|
+
value :name
|
670
|
+
attribute :role
|
671
|
+
end
|
672
|
+
class ItemElement3
|
673
|
+
include SAXMachine
|
674
|
+
element :author, :class => AuthorElement3
|
675
|
+
end
|
676
|
+
@item = ItemElement3.parse(@xml)
|
677
|
+
end
|
678
|
+
|
679
|
+
it 'should have the child elements' do
|
680
|
+
@item.author.should_not be_nil
|
681
|
+
end
|
682
|
+
|
683
|
+
it 'should have the author names' do
|
684
|
+
@item.author.name.should == 'John Doe'
|
685
|
+
end
|
686
|
+
|
687
|
+
it 'should have the author roles' do
|
688
|
+
@item.author.role.should == 'writer'
|
689
|
+
end
|
690
|
+
end
|
691
|
+
|
692
|
+
describe "with multiple mixed attributes and element values" do
|
693
|
+
before do
|
694
|
+
@xml = %[
|
695
|
+
<item id="1">
|
696
|
+
<title>sweet</title>
|
697
|
+
<author role="writer">John Doe</author>
|
698
|
+
<author role="artist">Jane Doe</author>
|
699
|
+
</item>
|
700
|
+
]
|
701
|
+
class AuthorElement4
|
702
|
+
include SAXMachine
|
703
|
+
value :name
|
704
|
+
attribute :role
|
705
|
+
end
|
706
|
+
class ItemElement4
|
707
|
+
include SAXMachine
|
708
|
+
element :title
|
709
|
+
elements :author, :as => :authors, :class => AuthorElement4
|
710
|
+
|
711
|
+
def title=(blah)
|
712
|
+
#raise 'woo'
|
713
|
+
@title = blah
|
714
|
+
end
|
715
|
+
end
|
716
|
+
@item = ItemElement4.parse(@xml)
|
717
|
+
end
|
718
|
+
|
719
|
+
it 'should have the title' do
|
720
|
+
@item.title.should == 'sweet'
|
721
|
+
end
|
722
|
+
|
723
|
+
it 'should have the child elements' do
|
724
|
+
@item.authors.should_not be_nil
|
725
|
+
@item.authors.count.should == 2
|
726
|
+
end
|
727
|
+
|
728
|
+
it 'should have the author names' do
|
729
|
+
@item.authors.first.name.should == 'John Doe'
|
730
|
+
@item.authors.last.name.should == 'Jane Doe'
|
731
|
+
end
|
732
|
+
|
733
|
+
it 'should have the author roles' do
|
734
|
+
@item.authors.first.role.should == 'writer'
|
735
|
+
@item.authors.last.role.should == 'artist'
|
736
|
+
end
|
737
|
+
end
|
527
738
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
require 'date'
|
2
2
|
|
3
3
|
# gem install redgreen for colored test output
|
4
|
-
begin require "redgreen" unless ENV['TM_CURRENT_LINE']
|
4
|
+
begin require "redgreen" unless ENV['TM_CURRENT_LINE']
|
5
|
+
rescue LoadError
|
6
|
+
end
|
5
7
|
|
6
8
|
path = File.expand_path(File.dirname(__FILE__) + "/../lib/")
|
7
9
|
$LOAD_PATH.unshift(path) unless $LOAD_PATH.include?(path)
|
8
10
|
|
9
|
-
require "
|
10
|
-
|
11
|
-
# Spec::Runner.configure do |config|
|
12
|
-
# end
|
11
|
+
require "sax-machine"
|
metadata
CHANGED
@@ -1,22 +1,22 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sax-machine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 55
|
5
|
+
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 20
|
10
|
+
version: 0.0.20
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Paul Dix
|
14
|
+
- Julien Kirch
|
14
15
|
autorequire:
|
15
16
|
bindir: bin
|
16
17
|
cert_chain: []
|
17
18
|
|
18
|
-
date:
|
19
|
-
default_executable:
|
19
|
+
date: 2011-07-20 00:00:00 Z
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
22
|
name: nokogiri
|
@@ -44,19 +44,19 @@ extra_rdoc_files: []
|
|
44
44
|
|
45
45
|
files:
|
46
46
|
- lib/sax-machine.rb
|
47
|
+
- lib/sax-machine/sax_attribute_config.rb
|
47
48
|
- lib/sax-machine/sax_config.rb
|
49
|
+
- lib/sax-machine/sax_document.rb
|
48
50
|
- lib/sax-machine/sax_collection_config.rb
|
49
51
|
- lib/sax-machine/sax_element_config.rb
|
50
|
-
- lib/sax-machine/
|
52
|
+
- lib/sax-machine/sax_element_value_config.rb
|
51
53
|
- lib/sax-machine/sax_handler.rb
|
54
|
+
- lib/sax-machine/sax_parent_config.rb
|
52
55
|
- README.textile
|
53
56
|
- Rakefile
|
54
|
-
- .rspec
|
55
57
|
- Gemfile
|
56
|
-
- Gemfile.lock
|
57
58
|
- spec/spec_helper.rb
|
58
59
|
- spec/sax-machine/sax_document_spec.rb
|
59
|
-
has_rdoc: true
|
60
60
|
homepage: http://github.com/pauldix/sax-machine
|
61
61
|
licenses: []
|
62
62
|
|
@@ -86,7 +86,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
86
86
|
requirements: []
|
87
87
|
|
88
88
|
rubyforge_project:
|
89
|
-
rubygems_version: 1.
|
89
|
+
rubygems_version: 1.8.5
|
90
90
|
signing_key:
|
91
91
|
specification_version: 2
|
92
92
|
summary: Declarative SAX Parsing with Nokogiri
|
data/.rspec
DELETED
data/Gemfile.lock
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
GEM
|
2
|
-
remote: http://rubygems.org/
|
3
|
-
specs:
|
4
|
-
diff-lcs (1.1.2)
|
5
|
-
nokogiri (1.4.4)
|
6
|
-
rspec (2.4.0)
|
7
|
-
rspec-core (~> 2.4.0)
|
8
|
-
rspec-expectations (~> 2.4.0)
|
9
|
-
rspec-mocks (~> 2.4.0)
|
10
|
-
rspec-core (2.4.0)
|
11
|
-
rspec-expectations (2.4.0)
|
12
|
-
diff-lcs (~> 1.1.2)
|
13
|
-
rspec-mocks (2.4.0)
|
14
|
-
|
15
|
-
PLATFORMS
|
16
|
-
ruby
|
17
|
-
|
18
|
-
DEPENDENCIES
|
19
|
-
nokogiri (>= 1.4.4)
|
20
|
-
rspec (>= 2.4.0)
|