smg 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+