sax-machine-patched 0.2.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ .idea
2
+ .bundle
3
+ *.gem
4
+ Gemfile.lock
5
+ .rvmrc
6
+ .DS_STORE
7
+ pkg/
8
+ coverage/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - ruby-head
7
+ - jruby-19mode
8
+ - jruby-head
9
+ - rbx-19mode
10
+ matrix:
11
+ allow_failures:
12
+ - rvm: ruby-head
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :development, :test do
6
+ gem 'rake'
7
+ gem 'guard-rspec'
8
+ gem 'simplecov', :require => false, :platforms => :mri_19
9
+ end
@@ -0,0 +1,5 @@
1
+ guard 'rspec', :version => 2 do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
@@ -0,0 +1,13 @@
1
+ # 0.2.0.rc1
2
+ * Tried to reduce the number of instances of respond_to? in the code by
3
+ pulling common uses of it out to methods. [[#32](https://github.com/pauldix/sax-machine/pull/32)]
4
+ * The parse stack is now composed of simple objects instead of it being
5
+ an array of arrays. [[#32](https://github.com/pauldix/sax-machine/pull/32)]
6
+ * Now using an identifier for an empty buffer instead of empty string. [[#32](https://github.com/pauldix/sax-machine/pull/32)]
7
+ * Cleaned up several variables that were not being used. [[#32](https://github.com/pauldix/sax-machine/pull/32)]
8
+ * Encapsulated stack so it's not being exposed as part of the API. [[#32](https://github.com/pauldix/sax-machine/pull/32)]
9
+ * #cdata_block is now an alias instead of delegating to characters. [[#32](https://github.com/pauldix/sax-machine/pull/32)]
10
+
11
+ # 0.1.0
12
+ * rename parent to ancestor
13
+ * added SAXMachine.configure
@@ -0,0 +1,107 @@
1
+ # SAX Machine [![Build Status](https://secure.travis-ci.org/pauldix/sax-machine.png?branch=master)](http://travis-ci.org/pauldix/sax-machine)
2
+
3
+ [Wiki](https://github.com/pauldix/sax-machine/wiki)
4
+
5
+ ## Description
6
+
7
+ A declarative SAX parsing library backed by Nokogiri
8
+
9
+ ## Usage
10
+ ```ruby
11
+ require 'sax-machine'
12
+
13
+ # Class for information associated with content parts in a feed.
14
+ # Ex: <content type="text">sample</content>
15
+ # instance.type will be "text", instance.text will be "sample"
16
+ class AtomContent
17
+ include SAXMachine
18
+ attribute :type
19
+ value :text
20
+ end
21
+
22
+ # Class for parsing an atom entry out of a feedburner atom feed
23
+ class AtomEntry
24
+ include SAXMachine
25
+ element :title
26
+ # the :as argument makes this available through atom_entry.author instead of .name
27
+ element :name, :as => :author
28
+ element "feedburner:origLink", :as => :url
29
+ element :summary
30
+ element :content, :class => AtomContent
31
+ element :published
32
+ ancestor :ancestor
33
+ end
34
+
35
+ # Class for parsing Atom feeds
36
+ class Atom
37
+ include SAXMachine
38
+ element :title
39
+ # the :with argument means that you only match a link tag that has an attribute of :type => "text/html"
40
+ # the :value argument means that instead of setting the value to the text between the tag,
41
+ # it sets it to the attribute value of :href
42
+ element :link, :value => :href, :as => :url, :with => {:type => "text/html"}
43
+ element :link, :value => :href, :as => :feed_url, :with => {:type => "application/atom+xml"}
44
+ elements :entry, :as => :entries, :class => AtomEntry
45
+ end
46
+
47
+ # you can then parse like this
48
+ feed = Atom.parse(xml_text)
49
+ # then you're ready to rock
50
+ feed.title # => whatever the title of the blog is
51
+ feed.url # => the main url of the blog
52
+ feed.feed_url # => goes to the feedburner feed
53
+
54
+ feed.entries.first.title # => title of the first entry
55
+ feed.entries.first.author # => the author of the first entry
56
+ feed.entries.first.url # => the permalink on the blog for this entry
57
+ feed.entries.first.ancestor # => the Atom ancestor
58
+ # etc ...
59
+
60
+ # you can also use the elements method without specifying a class like so
61
+ class SomeServiceResponse
62
+ include SAXMachine
63
+ elements :message, :as => :messages
64
+ end
65
+
66
+ response = SomeServiceResponse.parse("<response><message>hi</message><message>world</message></response>")
67
+ response.messages.first # => "hi"
68
+ response.messages.last # => "world"
69
+
70
+ # To limit conflicts in the class used for mappping, you can use the alternate SAXMachine.configure syntax
71
+
72
+ class X < ActiveRecord::Base
73
+ # this way no element, elements or ancestor method will be added to X
74
+ SAXMachine.configure(X) do |c|
75
+ c.element :title
76
+ end
77
+ end
78
+ ```
79
+
80
+ ## LICENSE
81
+
82
+ The MIT License
83
+
84
+ Copyright (c) 2009-2012:
85
+
86
+ * [Paul Dix](http://www.pauldix.net)
87
+ * [Julien Kirch](http://www.archiloque.net)
88
+ * [Ezekiel Templin](http://zeke.templ.in)
89
+
90
+ Permission is hereby granted, free of charge, to any person obtaining
91
+ a copy of this software and associated documentation files (the
92
+ 'Software'), to deal in the Software without restriction, including
93
+ without limitation the rights to use, copy, modify, merge, publish,
94
+ distribute, sublicense, and/or sell copies of the Software, and to
95
+ permit persons to whom the Software is furnished to do so, subject to
96
+ the following conditions:
97
+
98
+ The above copyright notice and this permission notice shall be
99
+ included in all copies or substantial portions of the Software.
100
+
101
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
102
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
103
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
104
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
105
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
106
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
107
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ task :test => :spec
6
+ task :default => :test
@@ -0,0 +1,8 @@
1
+ require "sax-machine/version"
2
+ require "sax-machine/sax_document"
3
+ require "sax-machine/sax_configure"
4
+ require "sax-machine/sax_handler"
5
+ require "sax-machine/sax_config"
6
+
7
+ module SAXMachine
8
+ end
@@ -0,0 +1,21 @@
1
+ module SAXMachine
2
+ class SAXConfig
3
+
4
+ class AncestorConfig
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
@@ -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.fetch(@name, nil)
24
+ end
25
+
26
+ def attrs_match?(attrs)
27
+ attrs.key?(@name) || attrs.value?(@name)
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
@@ -0,0 +1,37 @@
1
+ module SAXMachine
2
+ class SAXConfig
3
+
4
+ class CollectionConfig
5
+ attr_reader :name
6
+
7
+ def initialize(name, options)
8
+ @name = name.to_s
9
+ @class = options[:class]
10
+ @as = options[:as].to_s
11
+ @with = options.fetch(:with, {})
12
+ end
13
+
14
+ def accessor
15
+ as
16
+ end
17
+
18
+ def attrs_match?(attrs)
19
+ @with.all? do |key, value|
20
+ value === attrs[key.to_s]
21
+ end
22
+ end
23
+
24
+ def data_class
25
+ @class || @name
26
+ end
27
+
28
+ protected
29
+
30
+ def as
31
+ @as
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,74 @@
1
+ require "sax-machine/sax_attribute_config"
2
+ require "sax-machine/sax_element_value_config"
3
+ require "sax-machine/sax_element_config"
4
+ require "sax-machine/sax_collection_config"
5
+ require "sax-machine/sax_ancestor_config"
6
+
7
+ module SAXMachine
8
+ class SAXConfig
9
+
10
+ attr_accessor :top_level_elements, :top_level_attributes, :top_level_element_value, :collection_elements, :ancestors
11
+
12
+ def initialize
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
+ @ancestors = []
19
+ end
20
+
21
+ def columns
22
+ @top_level_elements.map {|name, ecs| ecs }.flatten
23
+ end
24
+
25
+ def initialize_copy(sax_config)
26
+ super
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
30
+ @collection_elements = sax_config.collection_elements.clone
31
+ @ancestors = sax_config.ancestors.clone
32
+ end
33
+
34
+ def add_top_level_element(name, options)
35
+ @top_level_elements[name.to_s] << ElementConfig.new(name, options)
36
+ end
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
+
46
+ def add_collection_element(name, options)
47
+ @collection_elements[name.to_s] << CollectionConfig.new(name, options)
48
+ end
49
+
50
+ def add_ancestor(name, options)
51
+ @ancestors << AncestorConfig.new(name, options)
52
+ end
53
+
54
+ def collection_config(name, attrs)
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
64
+ end
65
+
66
+ def element_configs_for_attribute(name, attrs)
67
+ @top_level_elements[name.to_s].select { |ec| ec.has_value_and_attrs_match?(attrs) }
68
+ end
69
+
70
+ def element_config_for_tag(name, attrs)
71
+ @top_level_elements[name.to_s].detect { |ec| ec.attrs_match?(attrs) }
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,38 @@
1
+ module SAXMachine
2
+
3
+ def self.configure(clazz)
4
+ extended_clazz = Class.new(clazz)
5
+ extended_clazz.send(:include, SAXMachine)
6
+
7
+ # override create_attr to create attributes on the original class
8
+ def extended_clazz.create_attr real_name
9
+ superclass.send(:attr_reader, real_name) unless superclass.method_defined?(real_name)
10
+ superclass.send(:attr_writer, real_name) unless superclass.method_defined?("#{real_name}=")
11
+ end
12
+
13
+ yield(extended_clazz)
14
+
15
+ clazz.extend LightWeightSaxMachine
16
+ clazz.sax_config = extended_clazz.sax_config
17
+
18
+ (class << clazz;self;end).send(:define_method, :parse) do |xml_text|
19
+ extended_clazz.parse(xml_text)
20
+ end
21
+
22
+ end
23
+
24
+ module LightWeightSaxMachine
25
+
26
+ attr_writer :sax_config
27
+
28
+ def sax_config
29
+ @sax_config ||= SAXConfig.new
30
+ end
31
+
32
+ def inherited(subclass)
33
+ subclass.sax_config.send(:initialize_copy, self.sax_config)
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,118 @@
1
+ require "nokogiri"
2
+
3
+ module SAXMachine
4
+
5
+ def self.included(base)
6
+ base.send(:include, InstanceMethods)
7
+ base.extend ClassMethods
8
+ end
9
+
10
+ def parse(xml_text, on_error = nil, on_warning = nil)
11
+ sax_handler = SAXHandler.new(self, on_error, on_warning)
12
+ parser = Nokogiri::XML::SAX::Parser.new(sax_handler)
13
+ parser.parse(xml_text) do |ctx|
14
+ ctx.replace_entities = true
15
+ end
16
+ self
17
+ end
18
+
19
+ module InstanceMethods
20
+ def initialize(attributes = {})
21
+ attributes.each do |name, value|
22
+ send("#{name}=", value)
23
+ end
24
+ end
25
+ end
26
+
27
+ module ClassMethods
28
+
29
+ def inherited(subclass)
30
+ subclass.sax_config.send(:initialize_copy, self.sax_config)
31
+ end
32
+
33
+ def parse(xml_text, on_error = nil, on_warning = nil)
34
+ new.parse(xml_text, on_error, on_warning)
35
+ end
36
+
37
+ def element(name, options = {})
38
+ real_name = (options[:as] ||= name).to_s
39
+ sax_config.add_top_level_element(name, options)
40
+ create_attr real_name
41
+ end
42
+
43
+ def attribute(name, options = {})
44
+ real_name = (options[:as] ||= name).to_s
45
+ sax_config.add_top_level_attribute(self.class.to_s, options.merge(:name => name))
46
+ create_attr real_name
47
+ end
48
+
49
+ def value(name, options = {})
50
+ real_name = (options[:as] ||= name).to_s
51
+ sax_config.add_top_level_element_value(self.class.to_s, options.merge(:name => name))
52
+ create_attr real_name
53
+ end
54
+
55
+ def ancestor(name, options = {})
56
+ real_name = (options[:as] ||= name).to_s
57
+ sax_config.add_ancestor(name, options)
58
+ create_attr(real_name)
59
+ end
60
+
61
+ def columns
62
+ sax_config.columns
63
+ end
64
+
65
+ def column(sym)
66
+ columns.select { |c| c.column == sym }[0]
67
+ end
68
+
69
+ def data_class(sym)
70
+ column(sym).data_class
71
+ end
72
+
73
+ def required?(sym)
74
+ column(sym).required?
75
+ end
76
+
77
+ def column_names
78
+ columns.map { |e| e.column }
79
+ end
80
+
81
+ def elements(name, options = {})
82
+ options[:as] ||= name
83
+ if options[:class]
84
+ sax_config.add_collection_element(name, options)
85
+ else
86
+ class_eval <<-SRC
87
+ def add_#{options[:as]}(value)
88
+ #{options[:as]} << value
89
+ end
90
+ SRC
91
+ sax_config.add_top_level_element(name, options.merge(:collection => true))
92
+ end
93
+
94
+ if !method_defined?(options[:as].to_s)
95
+ class_eval <<-SRC
96
+ def #{options[:as]}
97
+ @#{options[:as]} ||= []
98
+ end
99
+ SRC
100
+ end
101
+
102
+ attr_writer options[:as] unless method_defined?("#{options[:as]}=")
103
+ end
104
+
105
+ def sax_config
106
+ @sax_config ||= SAXConfig.new
107
+ end
108
+
109
+ # we only want to insert the getter and setter if they haven't defined it from elsewhere.
110
+ # this is how we allow custom parsing behavior. So you could define the setter
111
+ # and have it parse the string into a date or whatever.
112
+ def create_attr real_name
113
+ attr_reader real_name unless method_defined?(real_name)
114
+ attr_writer real_name unless method_defined?("#{real_name}=")
115
+ end
116
+ end
117
+
118
+ end