smg 0.0.1

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/README.rdoc ADDED
@@ -0,0 +1,28 @@
1
+ == REQUIREMENTS:
2
+
3
+ nokogiri (>=1.3)
4
+
5
+ == LICENSE:
6
+
7
+ (The MIT License)
8
+
9
+ Copyright (c) 2009
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining
12
+ a copy of this software and associated documentation files (the
13
+ 'Software'), to deal in the Software without restriction, including
14
+ without limitation the rights to use, copy, modify, merge, publish,
15
+ distribute, sublicense, and/or sell copies of the Software, and to
16
+ permit persons to whom the Software is furnished to do so, subject to
17
+ the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be
20
+ included in all copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
23
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
25
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
26
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
27
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
28
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,64 @@
1
+ module SMG #:nodoc:
2
+ class Document < ::Nokogiri::XML::SAX::Document
3
+
4
+ attr_reader :object, :thing
5
+
6
+ def initialize(object, thing = nil)
7
+ @object = object
8
+ @mapping = object.class.mapping
9
+ @stack = []
10
+ @docs = []
11
+ @thing = thing
12
+ @chars = ""
13
+ end
14
+
15
+ def start_element(name, attrs = [])
16
+
17
+ @stack << name
18
+
19
+ if doc = @docs.last
20
+ doc.start_element(name, attrs)
21
+ elsif thing = @mapping.nested[@stack]
22
+ @docs << doc = Document.new(thing.data_class.new,thing)
23
+ doc.start_element(name, attrs)
24
+ end
25
+
26
+ if !attrs.empty? && maps = @mapping.attributes[@stack]
27
+ maps.values_at(*Hash[*attrs].keys).compact.each do |m|
28
+ ix = attrs.index(m.at)
29
+ @object.__send__(m.accessor, m.cast(attrs.at(ix+=1))) if ix
30
+ end
31
+ end
32
+
33
+ @element = @mapping.elements[@stack]
34
+ @chars = ""
35
+
36
+ end
37
+
38
+ def end_element(name)
39
+ @object.send(@element.accessor, @element.cast(@chars)) if @element && @chars
40
+ @chars, @element = nil, nil
41
+ if doc = @docs.last
42
+ doc.end_element(name)
43
+ if doc.thing.path == @stack
44
+ @object.__send__(doc.thing.accessor, doc.object)
45
+ @docs.pop
46
+ end
47
+ end
48
+ @stack.pop
49
+ end
50
+
51
+ def characters(string)
52
+ if doc = @docs.last
53
+ doc.characters(string)
54
+ @chars ||= ""
55
+ @chars << string
56
+ elsif @element
57
+ @chars << string
58
+ end
59
+ end
60
+
61
+ end
62
+ end
63
+
64
+ # EOF
@@ -0,0 +1,48 @@
1
+ module SMG #:nodoc:
2
+ class Mapping #:nodoc:
3
+
4
+ class Element
5
+ attr_reader :path, :name, :accessor, :data_class, :cast_to, :at
6
+
7
+ def initialize(path, options = {})
8
+
9
+ @name = (options[:as] || options[:at] || path.last).to_sym
10
+ @path = path
11
+ @collection = !!options[:collection]
12
+ @accessor = @collection ? :"attach_#{@name}" : :"#{@name}="
13
+ @data_class = nil
14
+ @cast_to = nil
15
+
16
+ if c = options[:class]
17
+ if Class === c
18
+ raise ArgumentError, "#{c} is not an SMG::Model" unless c.include?(::SMG::Resource)
19
+ @data_class = c
20
+ elsif Symbol === c
21
+ raise ArgumentError, "#{c} is not a valid typecast" unless TypeCasts.key?(c)
22
+ @cast_to = c
23
+ else
24
+ raise ArgumentError, ":class should be an SMG::Model or a Symbol"
25
+ end
26
+ end
27
+
28
+ #ignore :at on nested collections of resources
29
+ @at = options.key?(:at) && !@data_class ? options[:at].to_s : nil
30
+
31
+ end
32
+
33
+ def cast(value)
34
+ @cast_to ? ::SMG::Mapping::TypeCasts[@cast_to, value] : value
35
+ rescue
36
+ #TODO: report about malformed data
37
+ end
38
+
39
+ def collection?
40
+ @collection
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+ end
47
+
48
+ # EOF
@@ -0,0 +1,37 @@
1
+ require 'uri'
2
+ require 'time'
3
+ require 'date'
4
+
5
+ module SMG #:nodoc:
6
+ class Mapping #:nodoc:
7
+ module TypeCasts
8
+
9
+ class << self
10
+ attr_accessor :typecasts
11
+
12
+ def [](key,value)
13
+ return typecasts[key][value] if typecasts.key?(key)
14
+ raise ArgumentError, "Can't typecast to #{key.inspect}"
15
+ end
16
+
17
+ def key?(key)
18
+ typecasts.key?(key)
19
+ end
20
+
21
+ end
22
+
23
+ # same as in extlib
24
+ self.typecasts = {}
25
+ self.typecasts[ :string ] = lambda{ |v| v.to_s }
26
+ self.typecasts[ :integer ] = lambda{ |v| v.to_i }
27
+ self.typecasts[ :boolean ] = lambda{ |v| v.to_s.strip != 'false' }
28
+ self.typecasts[ :symbol ] = lambda{ |v| v.to_sym }
29
+ self.typecasts[ :datetime ] = lambda{ |v| Time.parse(v).utc }
30
+ self.typecasts[ :date ] = lambda{ |v| Date.parse(v) }
31
+ self.typecasts[ :uri ] = lambda{ |v| ::URI.parse(v.to_s) }
32
+
33
+ end
34
+ end
35
+ end
36
+
37
+ # EOF
@@ -0,0 +1,50 @@
1
+ module SMG #:nodoc:
2
+ class Mapping
3
+
4
+ attr_reader :elements, :nested, :attributes
5
+ attr_reader :root
6
+
7
+ def initialize
8
+ @elements = {}
9
+ @nested = {}
10
+ @attributes = {}
11
+ end
12
+
13
+ def attach_element(tag,options)
14
+ chain = handle_path(tag)
15
+ if options.key?(:at)
16
+ ele = Element.new(chain, options)
17
+ @attributes[chain] ||= {}
18
+ @attributes[chain][ele.at] = ele
19
+ else
20
+ @elements[chain] = Element.new(chain, options)
21
+ end
22
+ end
23
+
24
+ def attach_nested(tag,options)
25
+ chain = handle_path(tag)
26
+ @nested[chain] = Element.new(chain, options.merge(:nested => true))
27
+ end
28
+
29
+ def use_root(path)
30
+ raise "root already defined!" if @root
31
+ @root = normalize_path(path)
32
+ end
33
+
34
+ private
35
+
36
+ def normalize_path(path)
37
+ path.to_s.split("/")
38
+ end
39
+
40
+ def handle_path(path)
41
+ ret = normalize_path(path)
42
+ ret.unshift(@root) if @root
43
+ ret.flatten!
44
+ ret
45
+ end
46
+
47
+ end
48
+ end
49
+
50
+ # EOF
data/lib/smg/model.rb ADDED
@@ -0,0 +1,57 @@
1
+ module SMG #:nodoc:
2
+ module Model
3
+
4
+ def extract(tag, options = {})
5
+
6
+ thing = if Class === options[:class]
7
+ options.delete(:collection)
8
+ mapping.attach_nested(tag,options)
9
+ else
10
+ mapping.attach_element(tag,options)
11
+ end
12
+
13
+ attr_reader thing.name if (instance_methods & [thing.name, thing.name.to_s]).empty?
14
+ attr_writer thing.name if (instance_methods & [thing.accessor, thing.accessor.to_s]).empty?
15
+
16
+ end
17
+
18
+ def collect(tag, options = {})
19
+
20
+ options.merge!(:collection => true)
21
+ thing = Class === options[:class] ? mapping.attach_nested(tag,options) : mapping.attach_element(tag,options)
22
+
23
+ if (instance_methods & [thing.accessor, thing.accessor.to_s]).empty?
24
+ class_eval <<-CODE
25
+ def #{thing.accessor}(value)
26
+ @#{thing.name} ||= []
27
+ @#{thing.name} << value
28
+ end
29
+ CODE
30
+ end
31
+
32
+ if (instance_methods & [thing.name, thing.name.to_s]).empty?
33
+ class_eval <<-CODE
34
+ def #{thing.name}
35
+ @#{thing.name} ||= []
36
+ end
37
+ CODE
38
+ end
39
+
40
+ end
41
+
42
+ def root(tag)
43
+ mapping.use_root(tag)
44
+ end
45
+
46
+ def parse(xml)
47
+ self.new.parse(xml)
48
+ end
49
+
50
+ def mapping
51
+ @mapping ||= Mapping.new
52
+ end
53
+
54
+ end
55
+ end
56
+
57
+ # EOF
@@ -0,0 +1,17 @@
1
+ module SMG #:nodoc:
2
+ module Resource
3
+
4
+ def self.included(base)
5
+ base.extend Model
6
+ end
7
+
8
+ def parse(data)
9
+ doc = SMG::Document.new(self)
10
+ ::Nokogiri::XML::SAX::Parser.new(doc).parse(data)
11
+ self
12
+ end
13
+
14
+ end
15
+ end
16
+
17
+ # EOF
@@ -0,0 +1,5 @@
1
+ module SMG
2
+ VERSION = '0.0.1'
3
+ end
4
+
5
+ # EOF
data/lib/smg.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'pathname'
2
+ require 'rubygems'
3
+
4
+ gem 'nokogiri', '>=1.3'
5
+ require 'nokogiri'
6
+
7
+ require 'smg/mapping/element'
8
+ require 'smg/mapping/typecasts'
9
+ require 'smg/mapping'
10
+ require 'smg/model'
11
+ require 'smg/resource'
12
+ require 'smg/document'
13
+
14
+ # EOF
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: smg
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - SSDany
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-04-16 00:00:00 +04:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: nokogiri
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 3
30
+ version: "1.3"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ description: Simple declaratibve XML parsing library. Backed by Nokogiri
34
+ email: inadsence@gmail.com
35
+ executables: []
36
+
37
+ extensions: []
38
+
39
+ extra_rdoc_files:
40
+ - README.rdoc
41
+ files:
42
+ - README.rdoc
43
+ - lib/smg/document.rb
44
+ - lib/smg/mapping/element.rb
45
+ - lib/smg/mapping/typecasts.rb
46
+ - lib/smg/mapping.rb
47
+ - lib/smg/model.rb
48
+ - lib/smg/resource.rb
49
+ - lib/smg/version.rb
50
+ - lib/smg.rb
51
+ has_rdoc: true
52
+ homepage:
53
+ licenses: []
54
+
55
+ post_install_message:
56
+ rdoc_options: []
57
+
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ requirements: []
75
+
76
+ rubyforge_project: smg
77
+ rubygems_version: 1.3.6
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: Simple declaratibve XML parsing library. Backed by Nokogiri
81
+ test_files: []
82
+