astro-sax-machine 0.0.12.20090419 → 0.0.14
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.
@@ -3,37 +3,38 @@ require "sax-machine/sax_collection_config"
|
|
3
3
|
|
4
4
|
module SAXMachine
|
5
5
|
class SAXConfig
|
6
|
+
attr_reader :top_level_elements, :collection_elements
|
7
|
+
|
6
8
|
def initialize
|
7
9
|
@top_level_elements = []
|
8
10
|
@collection_elements = []
|
9
11
|
end
|
10
|
-
|
12
|
+
|
11
13
|
def add_top_level_element(name, options)
|
12
14
|
@top_level_elements << ElementConfig.new(name, options)
|
13
15
|
end
|
14
|
-
|
16
|
+
|
15
17
|
def add_collection_element(name, options)
|
16
18
|
@collection_elements << CollectionConfig.new(name, options)
|
17
19
|
end
|
18
|
-
|
20
|
+
|
19
21
|
def collection_config(name)
|
20
22
|
@collection_elements.detect { |ce| ce.name.to_s == name.to_s }
|
21
23
|
end
|
22
24
|
|
23
|
-
def
|
24
|
-
|
25
|
+
def element_configs_for_attribute(name, attrs)
|
26
|
+
@top_level_elements.select do |element_config|
|
25
27
|
element_config.name == name &&
|
26
28
|
element_config.has_value_and_attrs_match?(attrs)
|
27
29
|
end
|
28
|
-
element_configs.empty? ? nil : element_configs
|
29
30
|
end
|
30
|
-
|
31
|
+
|
31
32
|
def element_config_for_tag(name, attrs)
|
32
33
|
@top_level_elements.detect do |element_config|
|
33
34
|
element_config.name == name &&
|
34
35
|
element_config.attrs_match?(attrs)
|
35
36
|
end
|
36
37
|
end
|
37
|
-
|
38
|
+
|
38
39
|
end
|
39
40
|
end
|
@@ -41,6 +41,26 @@ module SAXMachine
|
|
41
41
|
attr_reader options[:as] unless instance_methods.include?(options[:as].to_s)
|
42
42
|
attr_writer options[:as] unless instance_methods.include?("#{options[:as]}=")
|
43
43
|
end
|
44
|
+
|
45
|
+
def columns
|
46
|
+
sax_config.top_level_elements
|
47
|
+
end
|
48
|
+
|
49
|
+
def column(sym)
|
50
|
+
columns.select{|c| c.column == sym}[0]
|
51
|
+
end
|
52
|
+
|
53
|
+
def data_class(sym)
|
54
|
+
column(sym).data_class
|
55
|
+
end
|
56
|
+
|
57
|
+
def required?(sym)
|
58
|
+
column(sym).required?
|
59
|
+
end
|
60
|
+
|
61
|
+
def column_names
|
62
|
+
columns.map{|e| e.column}
|
63
|
+
end
|
44
64
|
|
45
65
|
def elements(name, options = {})
|
46
66
|
options[:as] ||= name
|
@@ -2,7 +2,7 @@ module SAXMachine
|
|
2
2
|
class SAXConfig
|
3
3
|
|
4
4
|
class ElementConfig
|
5
|
-
attr_reader :name, :setter
|
5
|
+
attr_reader :name, :setter, :data_class
|
6
6
|
|
7
7
|
def initialize(name, options)
|
8
8
|
@name = name.to_s
|
@@ -28,6 +28,16 @@ module SAXMachine
|
|
28
28
|
else
|
29
29
|
@setter = "#{@as}="
|
30
30
|
end
|
31
|
+
@data_class = options[:class]
|
32
|
+
@required = options[:required]
|
33
|
+
end
|
34
|
+
|
35
|
+
def column
|
36
|
+
@as || @name.to_sym
|
37
|
+
end
|
38
|
+
|
39
|
+
def required?
|
40
|
+
@required
|
31
41
|
end
|
32
42
|
|
33
43
|
def value_from_attrs(attrs)
|
@@ -3,12 +3,12 @@ require "nokogiri"
|
|
3
3
|
module SAXMachine
|
4
4
|
class SAXHandler < Nokogiri::XML::SAX::Document
|
5
5
|
attr_reader :object
|
6
|
-
|
6
|
+
|
7
7
|
def initialize(object)
|
8
8
|
@object = object
|
9
9
|
@parsed_configs = {}
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
def characters(string)
|
13
13
|
if parsing_collection?
|
14
14
|
@collection_handler.characters(string)
|
@@ -16,90 +16,117 @@ module SAXMachine
|
|
16
16
|
@value << string
|
17
17
|
end
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
def cdata_block(string)
|
21
21
|
characters(string)
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
def start_element(name, attrs = [])
|
25
|
-
@name = name
|
26
|
-
@attrs = attrs
|
27
25
|
|
26
|
+
@name = name
|
27
|
+
@attrs = attrs.map { |a| SAXHandler.decode_xml(a) }
|
28
|
+
|
28
29
|
if parsing_collection?
|
29
30
|
@collection_handler.start_element(@name, @attrs)
|
30
|
-
|
31
|
+
|
31
32
|
elsif @collection_config = sax_config.collection_config(@name)
|
32
33
|
@collection_handler = @collection_config.handler
|
33
34
|
@collection_handler.start_element(@name, @attrs)
|
34
|
-
|
35
|
-
elsif
|
36
|
-
|
37
|
-
|
35
|
+
|
36
|
+
elsif (element_configs = sax_config.element_configs_for_attribute(@name, @attrs)).any?
|
37
|
+
parse_element_attributes(element_configs)
|
38
|
+
set_element_config_for_element_value
|
39
|
+
|
38
40
|
else
|
39
|
-
|
40
|
-
@element_config = sax_config.element_config_for_tag(@name, @attrs)
|
41
|
+
set_element_config_for_element_value
|
41
42
|
end
|
42
43
|
end
|
43
|
-
|
44
|
+
|
44
45
|
def end_element(name)
|
45
46
|
if parsing_collection? && @collection_config.name == name
|
46
47
|
@object.send(@collection_config.accessor) << @collection_handler.object
|
47
48
|
reset_current_collection
|
48
|
-
|
49
|
+
|
49
50
|
elsif parsing_collection?
|
50
51
|
@collection_handler.end_element(name)
|
51
|
-
|
52
|
-
elsif characaters_captured?
|
52
|
+
|
53
|
+
elsif characaters_captured?
|
53
54
|
mark_as_parsed
|
54
55
|
@object.send(@element_config.setter, @value)
|
55
56
|
end
|
56
|
-
|
57
|
+
|
57
58
|
reset_current_tag
|
58
59
|
end
|
59
|
-
|
60
|
+
|
60
61
|
def characaters_captured?
|
61
62
|
!@value.nil? && !@value.empty?
|
62
63
|
end
|
63
|
-
|
64
|
+
|
64
65
|
def parsing_collection?
|
65
66
|
!@collection_handler.nil?
|
66
67
|
end
|
67
|
-
|
68
|
-
def
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
68
|
+
|
69
|
+
def parse_collection_instance_attributes
|
70
|
+
instance = @collection_handler.object
|
71
|
+
@attrs.each_with_index do |attr_name,index|
|
72
|
+
instance.send("#{attr_name}=", @attrs[index + 1]) if index % 2 == 0 && instance.methods.include?("#{attr_name}=")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def parse_element_attributes(element_configs)
|
77
|
+
element_configs.each do |ec|
|
78
|
+
unless parsed_config?(ec)
|
79
|
+
@object.send(ec.setter, ec.value_from_attrs(@attrs))
|
80
|
+
mark_as_parsed(ec)
|
73
81
|
end
|
74
82
|
end
|
75
|
-
|
76
83
|
@element_config = nil
|
77
84
|
end
|
78
|
-
|
79
|
-
def
|
80
|
-
|
81
|
-
@
|
82
|
-
|
85
|
+
|
86
|
+
def set_element_config_for_element_value
|
87
|
+
@value = ""
|
88
|
+
@element_config = sax_config.element_config_for_tag(@name, @attrs)
|
89
|
+
end
|
90
|
+
|
91
|
+
def mark_as_parsed(element_config=nil)
|
92
|
+
element_config ||= @element_config
|
93
|
+
@parsed_configs[element_config] = true unless element_config.collection?
|
83
94
|
end
|
84
|
-
|
85
|
-
def parsed_config?
|
86
|
-
@
|
95
|
+
|
96
|
+
def parsed_config?(element_config=nil)
|
97
|
+
element_config ||= @element_config
|
98
|
+
@parsed_configs[element_config]
|
87
99
|
end
|
88
|
-
|
100
|
+
|
89
101
|
def reset_current_collection
|
90
102
|
@collection_handler = nil
|
91
103
|
@collection_config = nil
|
92
104
|
end
|
93
|
-
|
105
|
+
|
94
106
|
def reset_current_tag
|
95
107
|
@name = nil
|
96
108
|
@attrs = nil
|
97
109
|
@value = nil
|
98
110
|
@element_config = nil
|
99
111
|
end
|
100
|
-
|
112
|
+
|
101
113
|
def sax_config
|
102
114
|
@object.class.sax_config
|
103
115
|
end
|
116
|
+
|
117
|
+
##
|
118
|
+
# Decodes XML special characters.
|
119
|
+
def self.decode_xml(str)
|
120
|
+
return str.map &method(:decode_xml) if str.kind_of?(Array)
|
121
|
+
|
122
|
+
entities = {
|
123
|
+
'#38' => '&',
|
124
|
+
'#13' => "\r",
|
125
|
+
}
|
126
|
+
entities.keys.inject(str) { |string, key|
|
127
|
+
string.gsub(/&#{key};/, entities[key])
|
128
|
+
}
|
129
|
+
end
|
130
|
+
|
104
131
|
end
|
105
132
|
end
|
data/lib/sax-machine.rb
CHANGED
@@ -15,6 +15,10 @@ describe "SAXMachine" do
|
|
15
15
|
document.title = "Title"
|
16
16
|
document.title.should == "Title"
|
17
17
|
end
|
18
|
+
|
19
|
+
it "should allow introspection of the elements" do
|
20
|
+
@klass.column_names.should =~ [:title]
|
21
|
+
end
|
18
22
|
|
19
23
|
it "should not overwrite the setter if there is already one present" do
|
20
24
|
@klass = Class.new do
|
@@ -28,6 +32,28 @@ describe "SAXMachine" do
|
|
28
32
|
document.title = "Title"
|
29
33
|
document.title.should == "Title **"
|
30
34
|
end
|
35
|
+
describe "the class attribute" do
|
36
|
+
before(:each) do
|
37
|
+
@klass = Class.new do
|
38
|
+
include SAXMachine
|
39
|
+
element :date, :class => DateTime
|
40
|
+
end
|
41
|
+
@document = @klass.new
|
42
|
+
@document.date = DateTime.now.to_s
|
43
|
+
end
|
44
|
+
it "should be available" do
|
45
|
+
@klass.data_class(:date).should == DateTime
|
46
|
+
end
|
47
|
+
end
|
48
|
+
describe "the required attribute" do
|
49
|
+
it "should be available" do
|
50
|
+
@klass = Class.new do
|
51
|
+
include SAXMachine
|
52
|
+
element :date, :required => true
|
53
|
+
end
|
54
|
+
@klass.required?(:date).should be_true
|
55
|
+
end
|
56
|
+
end
|
31
57
|
|
32
58
|
it "should not overwrite the accessor when the element is not present" do
|
33
59
|
document = @klass.new
|
@@ -110,6 +136,11 @@ describe "SAXMachine" do
|
|
110
136
|
end
|
111
137
|
end
|
112
138
|
|
139
|
+
it "should escape correctly the ampersand" do
|
140
|
+
document = @klass.parse("<link href='http://api.flickr.com/services/feeds/photos_public.gne?id=49724566@N00&lang=en-us&format=atom' foo='bar'>asdf</link>")
|
141
|
+
document.link.should == "http://api.flickr.com/services/feeds/photos_public.gne?id=49724566@N00&lang=en-us&format=atom"
|
142
|
+
end
|
143
|
+
|
113
144
|
it "should save the value of a matching element" do
|
114
145
|
document = @klass.parse("<link href='test' foo='bar'>asdf</link>")
|
115
146
|
document.link.should == "test"
|
@@ -232,6 +263,25 @@ describe "SAXMachine" do
|
|
232
263
|
document.second.should be_nil
|
233
264
|
end
|
234
265
|
end
|
266
|
+
|
267
|
+
describe "when desiring both the content and attributes of an element" do
|
268
|
+
before :each do
|
269
|
+
@klass = Class.new do
|
270
|
+
include SAXMachine
|
271
|
+
element :link
|
272
|
+
element :link, :value => :foo, :as => :link_foo
|
273
|
+
element :link, :value => :bar, :as => :link_bar
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
it "should parse the element and attribute values" do
|
278
|
+
document = @klass.parse("<link foo='test1' bar='test2'>hello</link>")
|
279
|
+
document.link.should == 'hello'
|
280
|
+
document.link_foo.should == 'test1'
|
281
|
+
document.link_bar.should == 'test2'
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
235
285
|
end
|
236
286
|
end
|
237
287
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: astro-sax-machine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Dix
|
@@ -44,6 +44,7 @@ files:
|
|
44
44
|
- spec/sax-machine/sax_document_spec.rb
|
45
45
|
has_rdoc: false
|
46
46
|
homepage: http://github.com/pauldix/sax-machine
|
47
|
+
licenses:
|
47
48
|
post_install_message:
|
48
49
|
rdoc_options: []
|
49
50
|
|
@@ -64,7 +65,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
65
|
requirements: []
|
65
66
|
|
66
67
|
rubyforge_project:
|
67
|
-
rubygems_version: 1.
|
68
|
+
rubygems_version: 1.3.5
|
68
69
|
signing_key:
|
69
70
|
specification_version: 2
|
70
71
|
summary: Declarative SAX Parsing with Nokogiri
|