sax-machine 0.0.16 → 0.0.20
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|