smg 0.0.2 → 0.1.0
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 +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
|