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 +14 -6
- data/examples/discogs.rb +39 -0
- data/examples/helper.rb +10 -0
- data/examples/plant.rb +89 -0
- data/examples/twitter.rb +35 -0
- data/lib/smg/document.rb +20 -11
- data/lib/smg/mapping.rb +7 -4
- data/lib/smg/mapping/element.rb +24 -10
- data/lib/smg/model.rb +6 -2
- data/lib/smg/resource.rb +2 -4
- data/lib/smg/version.rb +1 -1
- metadata +9 -4
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
|
-
|
53
|
-
extract
|
54
|
-
extract
|
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)
|
data/examples/discogs.rb
ADDED
@@ -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
|
data/examples/helper.rb
ADDED
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
|
data/examples/twitter.rb
ADDED
@@ -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
|
-
|
28
|
-
|
29
|
-
@object.__send__(m.accessor, m.cast(
|
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
|
-
|
34
|
-
|
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
|
-
|
40
|
-
|
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] =
|
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
|
-
|
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)
|
33
|
+
@root = normalize_path(path)
|
31
34
|
end
|
32
35
|
|
33
36
|
private
|
data/lib/smg/mapping/element.rb
CHANGED
@@ -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 ? :"
|
12
|
+
@accessor = @collection ? :"append_to_#{@name}" : :"#{@name}="
|
13
13
|
@data_class = nil
|
14
14
|
@cast_to = nil
|
15
|
+
@context = nil
|
15
16
|
|
16
|
-
if
|
17
|
-
if
|
18
|
-
|
19
|
-
@
|
20
|
-
|
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, ":
|
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(
|
47
|
-
|
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
data/lib/smg/version.rb
CHANGED
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
|
-
|
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
|
+
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
|
-
|
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
|