smg 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -8,6 +8,7 @@ Backed by Nokogiri's SAX Parser.
8
8
  * Typecasting
9
9
  * Nested resources (aka has_one)
10
10
  * Collections (aka has_many)
11
+ * Contextual parsing
11
12
 
12
13
  == EXAMPLES:
13
14
 
@@ -38,7 +39,7 @@ Backed by Nokogiri's SAX Parser.
38
39
 
39
40
  end
40
41
 
41
- === discogs.com
42
+ === discogs.com (with context)
42
43
 
43
44
  class Label
44
45
 
@@ -49,10 +50,9 @@ Backed by Nokogiri's SAX Parser.
49
50
  extract :release , :at => :id, :as => :discogs_id, :class => :integer
50
51
  extract :release , :at => :status
51
52
 
52
- root 'release'
53
- extract :title
54
- extract :catno
55
- extract :artist
53
+ extract 'release/title'
54
+ extract 'release/catno'
55
+ extract 'release/artist'
56
56
 
57
57
  end
58
58
 
@@ -61,10 +61,18 @@ Backed by Nokogiri's SAX Parser.
61
61
  root 'resp/label'
62
62
  extract :name
63
63
  extract :profile
64
- collect 'releases/release' , :as => :releases, :class => Release
64
+ collect 'releases/release' , :as => :releases, :class => Release, :context => [:with_releases]
65
65
 
66
66
  end
67
67
 
68
+ label = Label.parse(xml)
69
+ label.name #=> "name of the label"
70
+ label.releases #=> []
71
+
72
+ label = Label.parse(xml, :with_releases)
73
+ label.name #=> "name of the label"
74
+ label.releases #=> [#<Label::Release...>, ... #<Label::Release...>]
75
+
68
76
  == REQUIREMENTS:
69
77
 
70
78
  nokogiri (>=1.3)
@@ -0,0 +1,39 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
+
3
+ class Label
4
+
5
+ class Release
6
+
7
+ include SMG::Resource
8
+
9
+ extract 'release' , :at => :id, :as => :discogs_id, :class => :integer
10
+ extract 'release' , :at => :status
11
+
12
+ extract 'release/title'
13
+ extract 'release/catno'
14
+ extract 'release/artist'
15
+
16
+ end
17
+
18
+ include SMG::Resource
19
+
20
+ root 'resp/label'
21
+ extract 'name'
22
+ extract 'profile'
23
+ collect 'releases/release' , :as => :releases, :class => Release, :context => [:with_releases]
24
+
25
+ end
26
+
27
+ data = File.read(ROOT.join('spec/fixtures/discogs/Genosha+Recordings.xml'))
28
+
29
+ label = Label.parse(data)
30
+ puts label.name
31
+ puts label.profile
32
+ puts label.releases.map { |release| "#{release.catno}: #{release.title}" }.join("\n")
33
+
34
+ label = Label.parse(data,:with_releases)
35
+ puts label.name
36
+ puts label.profile
37
+ puts label.releases.map { |release| "#{release.catno}: #{release.title}" }.join("\n")
38
+
39
+ # EOF
@@ -0,0 +1,10 @@
1
+ require 'pathname'
2
+
3
+ ROOT = Pathname(__FILE__).dirname.expand_path.parent
4
+
5
+ dir = ROOT.join('lib').to_s
6
+ $:.unshift(dir) unless $:.include?(dir)
7
+
8
+ require 'smg'
9
+
10
+ # EOF
data/examples/plant.rb ADDED
@@ -0,0 +1,89 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
+
3
+ class Conservation
4
+
5
+ include SMG::Resource
6
+
7
+ extract :conservation , :at => :code
8
+ extract :conservation , :at => :year , :context => [:conservation]
9
+ extract :conservation , :as => :status , :context => [:conservation]
10
+
11
+ end
12
+
13
+ class Plant
14
+
15
+ include SMG::Resource
16
+
17
+ root 'spec'
18
+
19
+ extract 'family' , :context => [:classification]
20
+ extract 'genus' , :context => [:classification]
21
+ extract 'binomial'
22
+ extract 'conservation' , :context => [:conservation, :info], :class => Conservation
23
+ collect 'synonims/binomial' , :context => [:synonims], :as => :synonims,
24
+
25
+ end
26
+
27
+ data = <<-XML
28
+ <?xml version="1.0" encoding="UTF-8"?>
29
+ <spec>
30
+ <family>Rosaceae</family>
31
+ <genus>Malus</genus>
32
+ <conservation code="NE" year="2007">Not Evaluated</conservation>
33
+ <binomial>Malus pumila Mill.</binomial>
34
+ <synonims>
35
+ <binomial>Malus communis Poir.</binomial>
36
+ <binomial>Malus domestica auct. non Borkh.</binomial>
37
+ <binomial>Malus praecox (Pall.) Borkh.</binomial>
38
+ <binomial>Malus pumila Mill. var. niedzwetzkyana (Dieck) C.K. Schneid.</binomial>
39
+ <binomial>Malus sylvestris Amer. auth., non (L.) Mill.</binomial>
40
+ <binomial>Malus sylvestris (L.) Mill. var. praecox (Pall.) Ponomar.</binomial>
41
+ <binomial>Pyrus pumila (Mill.) K. Koch</binomial>
42
+ </synonims>
43
+ </spec>
44
+ XML
45
+
46
+ plant = Plant.parse(data,:classification)
47
+
48
+ puts plant.family #=> "Rosaceae"
49
+ puts plant.genus #=> "Malus"
50
+ puts plant.binomial #=> "Malus pumila Mill."
51
+ puts plant.conservation #=> nil
52
+ puts plant.synonims #=> []
53
+
54
+ plant = Plant.parse(data,:synonims)
55
+
56
+ puts plant.family #=> nil
57
+ puts plant.genus #=> nil
58
+ puts plant.binomial #=> "Malus pumila Mill."
59
+ puts plant.conservation #=> nil
60
+ puts plant.synonims #=> [ "Malus communis Poir.",
61
+ # "Malus domestica auct. non Borkh.",
62
+ # "Malus praecox (Pall.) Borkh.",
63
+ # "Malus pumila Mill. var. niedzwetzkyana (Dieck) C.K. Schneid.",
64
+ # "Malus sylvestris Amer. auth., non (L.) Mill.",
65
+ # "Malus sylvestris (L.) Mill. var. praecox (Pall.) Ponomar.",
66
+ # "Pyrus pumila (Mill.) K. Koch" ]
67
+
68
+ plant = Plant.parse(data,:conservation)
69
+
70
+ puts plant.family #=> nil
71
+ puts plant.genus #=> nil
72
+ puts plant.binomial #=> "Malus pumila Mill."
73
+ puts plant.conservation.status #=> "Not Evaluated"
74
+ puts plant.conservation.code #=> "NE"
75
+ puts plant.conservation.year #=> "2007"
76
+ puts plant.synonims #=> []
77
+
78
+ plant = Plant.parse(data,:info)
79
+
80
+ puts plant.family #=> nil
81
+ puts plant.genus #=> nil
82
+ puts plant.binomial #=> "Malus pumila Mill."
83
+ puts plant.conservation.status #=> nil
84
+ puts plant.conservation.code #=> "NE"
85
+ puts plant.conservation.status #=> nil
86
+ puts plant.synonims #=> []
87
+
88
+
89
+ # EOF
@@ -0,0 +1,35 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
+
3
+ class Status
4
+ include SMG::Resource
5
+
6
+ root 'status'
7
+
8
+ extract :id , :class => :integer, :as => :status_id
9
+ extract :created_at , :class => :datetime
10
+ extract :text
11
+
12
+ end
13
+
14
+ class User
15
+ include SMG::Resource
16
+
17
+ root 'user'
18
+
19
+ extract :id , :class => :integer, :as => :twitter_id
20
+ extract :location , :class => :string
21
+ extract :status , :class => Status
22
+ extract :created_at , :class => :datetime
23
+ extract :name
24
+ extract :screen_name
25
+
26
+ end
27
+
28
+ data = File.read(ROOT.join('spec/fixtures/twitter/pipopolam.xml'))
29
+ user = User.parse(data)
30
+
31
+ puts "#{user.screen_name} (#{user.name}), since #{user.created_at.strftime('%Y.%m.%d')}"
32
+ puts user.location
33
+ puts user.status.text
34
+
35
+ # EOF
data/lib/smg/document.rb CHANGED
@@ -3,12 +3,13 @@ module SMG #:nodoc:
3
3
 
4
4
  attr_reader :object, :thing
5
5
 
6
- def initialize(object, thing = nil)
6
+ def initialize(object, context = nil, thing = nil)
7
7
  @object = object
8
8
  @mapping = object.class.mapping
9
9
  @stack = []
10
10
  @docs = []
11
11
  @thing = thing
12
+ @context = context
12
13
  @chars = ""
13
14
  end
14
15
 
@@ -18,26 +19,33 @@ module SMG #:nodoc:
18
19
 
19
20
  if doc = @docs.last
20
21
  doc.start_element(name, attrs)
21
- elsif thing = @mapping.nested[@stack]
22
- @docs << doc = Document.new(thing.data_class.new,thing)
22
+ elsif (thing = @mapping.nested[@stack]) && thing.in_context_of?(@context)
23
+ @docs << doc = Document.new(thing.data_class.new,@context,thing)
23
24
  doc.start_element(name, attrs)
24
25
  end
25
26
 
26
27
  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
28
+ attrh = Hash[*attrs]
29
+ maps.values_at(*attrh.keys).compact.each do |m|
30
+ @object.__send__(m.accessor, m.cast(attrh[m.at])) if m.in_context_of?(@context)
30
31
  end
31
32
  end
32
33
 
33
- @element = @mapping.elements[@stack]
34
- @chars = ""
34
+ if (e = @mapping.elements[@stack]) && e.in_context_of?(@context)
35
+ @element = e
36
+ @chars = ""
37
+ end
35
38
 
36
39
  end
37
40
 
38
41
  def end_element(name)
39
- @object.send(@element.accessor, @element.cast(@chars)) if @element && @chars
40
- @chars, @element = nil, nil
42
+
43
+ if @element
44
+ @object.__send__(@element.accessor, @element.cast(@chars))
45
+ @chars = ""
46
+ @element = nil
47
+ end
48
+
41
49
  if doc = @docs.last
42
50
  doc.end_element(name)
43
51
  if doc.thing.path == @stack
@@ -45,13 +53,14 @@ module SMG #:nodoc:
45
53
  @docs.pop
46
54
  end
47
55
  end
56
+
48
57
  @stack.pop
58
+
49
59
  end
50
60
 
51
61
  def characters(string)
52
62
  if doc = @docs.last
53
63
  doc.characters(string)
54
- @chars ||= ""
55
64
  @chars << string
56
65
  elsif @element
57
66
  @chars << string
data/lib/smg/mapping.rb CHANGED
@@ -12,22 +12,25 @@ module SMG #:nodoc:
12
12
 
13
13
  def attach_element(tag,options)
14
14
  chain = handle_path(tag)
15
+ thing = Element.new(chain, options)
15
16
  if options.key?(:at)
16
- thing = Element.new(chain, options)
17
17
  @attributes[chain] ||= {}
18
18
  @attributes[chain][thing.at] = thing
19
19
  else
20
- @elements[chain] = Element.new(chain, options)
20
+ @elements[chain] = thing
21
21
  end
22
+ thing
22
23
  end
23
24
 
24
25
  def attach_nested(tag,options)
25
26
  chain = handle_path(tag)
26
- @nested[chain] = Element.new(chain, options.merge(:nested => true))
27
+ thing = Element.new(chain, options.merge(:nested => true))
28
+ @nested[chain] = thing
29
+ thing
27
30
  end
28
31
 
29
32
  def use_root(path)
30
- @root = normalize_path(path) # just a root tag for further definitions
33
+ @root = normalize_path(path)
31
34
  end
32
35
 
33
36
  private
@@ -2,26 +2,36 @@ module SMG #:nodoc:
2
2
  class Mapping #:nodoc:
3
3
 
4
4
  class Element
5
- attr_reader :path, :name, :accessor, :data_class, :cast_to, :at
5
+ attr_reader :path, :name, :accessor, :data_class, :cast_to, :at, :context
6
6
 
7
7
  def initialize(path, options = {})
8
8
 
9
9
  @name = (options[:as] || options[:at] || path.last).to_sym
10
10
  @path = path
11
11
  @collection = !!options[:collection]
12
- @accessor = @collection ? :"attach_#{@name}" : :"#{@name}="
12
+ @accessor = @collection ? :"append_to_#{@name}" : :"#{@name}="
13
13
  @data_class = nil
14
14
  @cast_to = nil
15
+ @context = nil
15
16
 
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
17
+ if options.key?(:context)
18
+ if Array === options[:context]
19
+ @context = options[:context].compact
20
+ @context.uniq!
21
+ @context = nil if @context.empty?
23
22
  else
24
- raise ArgumentError, ":class should be an SMG::Model or a Symbol"
23
+ raise ArgumentError, ":context should be an Array"
24
+ end
25
+ end
26
+
27
+ if options.key?(:class)
28
+ klass = options[:class]
29
+ if SMG::Model === klass
30
+ @data_class = klass
31
+ elsif TypeCasts.key?(klass)
32
+ @cast_to = klass
33
+ else
34
+ raise ArgumentError, ":class should be an SMG::Model or a valid typecast"
25
35
  end
26
36
  end
27
37
 
@@ -40,6 +50,10 @@ module SMG #:nodoc:
40
50
  @collection
41
51
  end
42
52
 
53
+ def in_context_of?(context)
54
+ @context.nil? || @context.include?(context)
55
+ end
56
+
43
57
  end
44
58
 
45
59
  end
data/lib/smg/model.rb CHANGED
@@ -43,8 +43,12 @@ module SMG #:nodoc:
43
43
  mapping.use_root(tag)
44
44
  end
45
45
 
46
- def parse(xml)
47
- self.new.parse(xml)
46
+ def parse(data, context = nil)
47
+ resource = new
48
+ doc = SMG::Document.new(resource,context)
49
+ ::Nokogiri::XML::SAX::Parser.new(doc).parse(data)
50
+ resource.instance_variable_set(:@_parsed, true)
51
+ resource
48
52
  end
49
53
 
50
54
  def mapping
data/lib/smg/resource.rb CHANGED
@@ -5,10 +5,8 @@ module SMG #:nodoc:
5
5
  base.extend Model
6
6
  end
7
7
 
8
- def parse(data)
9
- doc = SMG::Document.new(self)
10
- ::Nokogiri::XML::SAX::Parser.new(doc).parse(data)
11
- self
8
+ def parsed?
9
+ @_parsed
12
10
  end
13
11
 
14
12
  end
data/lib/smg/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module SMG
2
- VERSION = '0.0.2'
2
+ VERSION = '0.1.0'
3
3
  end
4
4
 
5
5
  # EOF
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
+ - 1
7
8
  - 0
8
- - 2
9
- version: 0.0.2
9
+ version: 0.1.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - SSDany
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-04-17 00:00:00 +04:00
17
+ date: 2010-04-29 00:00:00 +04:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -31,7 +31,8 @@ dependencies:
31
31
  type: :runtime
32
32
  version_requirements: *id001
33
33
  description: |
34
- Object to xml mapping library with simple declarative syntax.
34
+ XML to Object mapping library with simple declarative syntax.
35
+ Supports 'contextual' parsing.
35
36
  Backed by Nokogiri's SAX Parser.
36
37
 
37
38
  email: inadsence@gmail.com
@@ -43,6 +44,10 @@ extra_rdoc_files:
43
44
  - README.rdoc
44
45
  files:
45
46
  - README.rdoc
47
+ - examples/helper.rb
48
+ - examples/discogs.rb
49
+ - examples/twitter.rb
50
+ - examples/plant.rb
46
51
  - lib/smg/document.rb
47
52
  - lib/smg/mapping/element.rb
48
53
  - lib/smg/mapping/typecasts.rb