peanuts 1.0 → 2.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/spec/cat_spec.rb ADDED
@@ -0,0 +1,151 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
2
+
3
+ require 'bigdecimal'
4
+ require 'test/unit'
5
+ require 'rubygems'
6
+ require 'shoulda'
7
+ require 'peanuts'
8
+
9
+
10
+ class Cheezburger
11
+ include Peanuts
12
+
13
+ attribute :weight, :float
14
+ attribute :price, :decimal
15
+
16
+ def initialize(weight = nil, price = nil)
17
+ @weight, @price = weight, price
18
+ end
19
+
20
+ def eql?(other)
21
+ other && weight == other.weight && price == other.price
22
+ end
23
+
24
+ alias == eql?
25
+ end
26
+
27
+ class Paws
28
+ include Peanuts
29
+
30
+ elements :paws, :name => :paw, :ns => 'urn:x-lol'
31
+ end
32
+
33
+ class Cat
34
+ include Peanuts
35
+
36
+ namespaces :lol => 'urn:x-lol', :kthnx => 'urn:x-lol:kthnx'
37
+
38
+ root 'kitteh', :ns => 'urn:x-lol'
39
+
40
+ attribute :has_tail?, :boolean, :name => 'has-tail', :ns => :kthnx
41
+ attribute :ears, :integer
42
+
43
+ element :ration, [:string], :name => :eats, :ns => :kthnx
44
+ element :name, :ns => 'urn:x-lol:kthnx'
45
+
46
+ shallow :paws, Paws
47
+
48
+ shallow :pals, :ns => :kthnx do
49
+ elements :friends, :name => :pal
50
+ end
51
+
52
+ element :cheezburger, Cheezburger
53
+ element :moar_cheezburgers do
54
+ elements :cheezburger, Cheezburger
55
+ end
56
+ end
57
+
58
+ shared_examples_for 'my cat' do
59
+ it 'should be named Silly Tom' do
60
+ @cat.name.should == 'Silly Tom'
61
+ end
62
+
63
+ it 'should eat tigers and lions' do
64
+ @cat.ration.should == %w(tigers lions)
65
+ end
66
+
67
+ it 'should be a friend of Chrissy, Missy & Sissy' do
68
+ @cat.friends.should == ['Chrissy', 'Missy', 'Sissy']
69
+ end
70
+
71
+ it 'should have 2 ears' do
72
+ @cat.ears.should == 2
73
+ end
74
+
75
+ it 'should have a tail' do
76
+ @cat.should have_tail
77
+ end
78
+
79
+ it 'should have four paws' do
80
+ @cat.paws.should == %w(one two three four)
81
+ end
82
+
83
+ it 'should has cheezburger' do
84
+ @cat.cheezburger.should be_kind_of Cheezburger
85
+ end
86
+
87
+ it 'should has 2 moar good cheezburgerz' do
88
+ @cat.moar_cheezburgers.cheezburger.should == [
89
+ Cheezburger.new(685.940, BigDecimal('19')),
90
+ Cheezburger.new(9356.7, BigDecimal('7.40'))]
91
+ end
92
+ end
93
+
94
+ shared_examples_for 'my cheezburger' do
95
+ it 'should weigh 14.5547 pounds' do
96
+ @cheezburger.weight.should == 14.5547
97
+ end
98
+
99
+ it 'should cost $2.05' do
100
+ @cheezburger.price.should == BigDecimal('2.05')
101
+ end
102
+ end
103
+
104
+ shared_examples_for 'sample kitteh' do
105
+ before :all do
106
+ @xml_fragment = <<-EOS
107
+ <kitteh xmlns='urn:x-lol' xmlns:kthnx='urn:x-lol:kthnx' ears=' 2 ' kthnx:has-tail=' yes '>
108
+ <name xmlns='urn:x-lol:kthnx'>
109
+ Silly
110
+ Tom
111
+ </name>
112
+ <kthnx:eats>
113
+ tigers
114
+ lions
115
+ </kthnx:eats>
116
+ <kthnx:pals>
117
+ <pal>Chrissy</pal>
118
+ <pal>Missy</pal>
119
+ <pal>Sissy</pal>
120
+ </kthnx:pals>
121
+ <paws>
122
+ <paw> one</paw>
123
+ <paw> two </paw>
124
+ <paw>three</paw>
125
+ <paw>four</paw>
126
+ </paws>
127
+ <cheezburger price='2.05' weight='14.5547' />
128
+ <moar_cheezburgers>
129
+ <cheezburger price='19' weight='685.940' />
130
+ <cheezburger price='7.40' weight='9356.7' />
131
+ </moar_cheezburgers>
132
+ </kitteh>
133
+ EOS
134
+ @cat = Cat.from_xml(@xml_fragment)
135
+ @cheezburger = @cat.cheezburger
136
+ end
137
+
138
+ it_should_behave_like 'my cat', 'my cheezburger'
139
+ end
140
+
141
+ describe 'My cat' do
142
+ it_should_behave_like 'sample kitteh'
143
+
144
+ context 'saved and restored' do
145
+ it_should_behave_like 'sample kitteh'
146
+
147
+ before :all do
148
+ @cat = Cat.from_xml(@cat.to_xml)
149
+ end
150
+ end
151
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: peanuts
3
3
  version: !ruby/object:Gem::Version
4
- version: "1.0"
4
+ version: 2.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Igor Gunko
@@ -9,18 +9,28 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-06-21 00:00:00 +03:00
12
+ date: 2009-09-30 00:00:00 +03:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
- name: thoughtbot-shoulda
16
+ name: libxml-ruby
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.1.3
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
17
27
  type: :development
18
28
  version_requirement:
19
29
  version_requirements: !ruby/object:Gem::Requirement
20
30
  requirements:
21
31
  - - ">="
22
32
  - !ruby/object:Gem::Version
23
- version: 2.0.6
33
+ version: 1.2.8
24
34
  version:
25
35
  description: " Peanuts is an XML to Ruby and back again mapping library.\n"
26
36
  email: tekmon@gmail.com
@@ -37,11 +47,12 @@ files:
37
47
  - Rakefile
38
48
  - lib/peanuts.rb
39
49
  - lib/omg-peanuts.rb
40
- - lib/peanuts/nuts.rb
50
+ - lib/peanuts/mappable.rb
41
51
  - lib/peanuts/mappings.rb
42
52
  - lib/peanuts/converters.rb
43
- - lib/peanuts/backend.rb
44
- - lib/peanuts/rexml.rb
53
+ - lib/peanuts/mapper.rb
54
+ - lib/peanuts/xml.rb
55
+ - lib/peanuts/xml/libxml.rb
45
56
  has_rdoc: true
46
57
  homepage: http://github.com/omg/peanuts
47
58
  licenses: []
@@ -73,4 +84,4 @@ signing_key:
73
84
  specification_version: 2
74
85
  summary: Making XML <-> Ruby binding easy
75
86
  test_files:
76
- - test/parsing_test.rb
87
+ - spec/cat_spec.rb
@@ -1,38 +0,0 @@
1
- require 'monitor'
2
-
3
- module Peanuts
4
- module XmlBackend
5
- extend MonitorMixin
6
-
7
- autoload :REXMLBackend, 'peanuts/rexml'
8
-
9
- def self.default
10
- synchronize do
11
- unless defined? @@default
12
- @@default = REXMLBackend.new
13
- def self.default #:nodoc:
14
- @@default
15
- end
16
- @@default
17
- end
18
- end
19
- end
20
-
21
- def self.default=(backend)
22
- @@default = backend
23
- end
24
-
25
- def self.current
26
- Thread.current[XmlBackend.name] || default
27
- end
28
-
29
- def self.current=(backend)
30
- Thread.current[XmlBackend.name] = backend
31
- end
32
-
33
- private
34
- def backend #:doc:
35
- XmlBackend.current
36
- end
37
- end
38
- end
data/lib/peanuts/nuts.rb DELETED
@@ -1,219 +0,0 @@
1
- require 'peanuts/mappings'
2
-
3
- module Peanuts #:nodoc:
4
- # See also +ClassMethods+
5
- def self.included(other) #:nodoc:
6
- other.extend(ClassMethods)
7
- end
8
-
9
- # See also +PeaNuts+.
10
- module ClassMethods
11
- include XmlBackend
12
- include Mappings
13
-
14
- # namespaces(hash) -> Hash
15
- # namespaces -> Hash
16
- #
17
- # Updates and returns class-level prefix mappings.
18
- # When given a hash of mappings merges it over current.
19
- # When called withot arguments simply returns current mappings.
20
- #
21
- # === Example:
22
- # class Cat
23
- # include PeaNuts
24
- # namespaces :lol => 'urn:lol', ...
25
- # ...
26
- # end
27
- def namespaces(mappings = nil)
28
- @namespaces ||= {}
29
- mappings ? @namespaces.update(mappings) : @namespaces
30
- end
31
-
32
- # root(xmlname[, :xmlns => ...]) -> Mappings::Root
33
- # root -> Mappings::Root
34
- #
35
- # Defines element name.
36
- # TODO: moar details
37
- #
38
- # === Arguments
39
- # [+xmlname+] Element name
40
- # [+options+] <tt>:xmlns => <tt> Element namespace
41
- #
42
- # === Example:
43
- # class Cat
44
- # include Peanuts
45
- # ...
46
- # root :kitteh, :xmlns => 'urn:lol'
47
- # ...
48
- # end
49
- def root(xmlname = nil, options = {})
50
- @root = Root.new(xmlname, prepare_options(options)) if xmlname
51
- @root ||= Root.new('root')
52
- end
53
-
54
- # element(name, [type[, options]]) -> Mappings::Element or Mappings::ElementValue
55
- # element(name[, options]) { block } -> Mappings::Element
56
- #
57
- # Defines single-element mapping.
58
- #
59
- # === Arguments
60
- # [+name+] Accessor name
61
- # [+type+] Element type. <tt>:string</tt> assumed if omitted (see +Converter+).
62
- # [+options+] <tt>:xmlname</tt>, <tt>:xmlns</tt>, converter options (see +Converter+).
63
- # [+block+] An anonymous class definition.
64
- #
65
- # === Example:
66
- # class Cat
67
- # include Peanuts
68
- # ...
69
- # element :name, :string, :whitespace => :collapse
70
- # element :cheeseburger, Cheeseburger, :xmlname => :cheezburger
71
- # ...
72
- # end
73
- def element(name, type = :string, options = {}, &block)
74
- type, options = prepare_args(type, options, &block)
75
- define_accessor name
76
- (mappings << (type.is_a?(Class) ? Element : ElementValue).new(name, type, prepare_options(options))).last
77
- end
78
-
79
- # elements(name, [type[, options]]) -> Mappings::Element or Mappings::ElementValue
80
- # elements(name[, options]) { block } -> Mappings::Element
81
- #
82
- # Defines multiple elements mapping.
83
- #
84
- # === Arguments
85
- # [+name+] Accessor name
86
- # [+type+] Element type. <tt>:string</tt> assumed if omitted (see +Converter+).
87
- # [+options+] <tt>:xmlname</tt>, <tt>:xmlns</tt>, converter options (see +Converter+).
88
- # [+block+] An anonymous class definition.
89
- #
90
- # === Example:
91
- # class RichCat
92
- # include Peanuts
93
- # ...
94
- # elements :ration, :string, :whitespace => :collapse
95
- # elements :cheeseburgers, Cheeseburger, :xmlname => :cheezburger
96
- # ...
97
- # end
98
- def elements(name, type = :string, options = {}, &block)
99
- type, options = prepare_args(type, options, &block)
100
- define_accessor name
101
- (mappings << (type.is_a?(Class) ? Elements : ElementValues).new(name, type, prepare_options(options))).last
102
- end
103
-
104
- # attribute(name, [type[, options]]) -> Mappings::Attribute or Mappings::AttributeValue
105
- #
106
- # Defines attribute mapping.
107
- #
108
- # === Arguments
109
- # [+name+] Accessor name
110
- # [+type+] Element type. <tt>:string</tt> assumed if omitted (see +Converter+).
111
- # [+options+] <tt>:xmlname</tt>, <tt>:xmlns</tt>, converter options (see +Converter+).
112
- #
113
- # === Example:
114
- # class Cat
115
- # include Peanuts
116
- # ...
117
- # element :name, :string, :whitespace => :collapse
118
- # element :cheeseburger, Cheeseburger, :xmlname => :cheezburger
119
- # ...
120
- # end
121
- def attribute(name, type = :string, options = {})
122
- define_accessor name
123
- mappings << Attribute.new(name, type, prepare_options(options))
124
- end
125
-
126
- # mappings -> Array
127
- #
128
- # Returns all mappings defined on a class.
129
- def mappings
130
- @mappings ||= []
131
- end
132
-
133
- def parse(source, options = {})
134
- backend.parse(source, options) {|node| parse_node(new, node) }
135
- end
136
-
137
- def build(nut, result = :string, options = {})
138
- options, result = result, :string if result.is_a?(Hash)
139
- options[:xmlname] ||= root.xmlname
140
- options[:xmlns_prefix] = namespaces.invert[options[:xmlns] ||= root.xmlns]
141
- backend.build(result, options) {|node| build_node(nut, node) }
142
- end
143
-
144
- def build_node(nut, node) #:nodoc:
145
- backend.add_namespaces(node, namespaces)
146
- callem(:to_xml, nut, node)
147
- node
148
- end
149
-
150
- def parse_node(nut, node) #:nodoc:
151
- callem(:from_xml, nut, node)
152
- nut
153
- end
154
-
155
- private
156
- def prepare_args(type, options, &block)
157
- if block_given?
158
- options = type if type.is_a?(Hash)
159
- type = Class.new
160
- type.class_eval do
161
- include Peanuts
162
- class_eval(&block)
163
- end
164
- end
165
- return type, prepare_options(options)
166
- end
167
-
168
- def prepare_options(options)
169
- ns = options[:xmlns]
170
- if ns.is_a?(Symbol)
171
- raise ArgumentError, "undefined prefix: #{ns}" unless options[:xmlns] = namespaces[ns]
172
- end
173
- options
174
- end
175
-
176
- def define_accessor(name)
177
- if method_defined?(name) || method_defined?("#{name}=")
178
- raise ArgumentError, "#{name}: name already defined or reserved"
179
- end
180
- attr_accessor name
181
- end
182
-
183
- def callem(method, *args)
184
- mappings.each {|m| m.send(method, *args) }
185
- end
186
- end
187
-
188
- def parse(source, options = {})
189
- backend.parse(source, options) {|node| parse_node(node) }
190
- end
191
-
192
- # build([options]) -> root element or string
193
- # build([options]) -> root element or string
194
- # build(destination[, options]) -> destination
195
- #
196
- # Defines attribute mapping.
197
- #
198
- # === Arguments
199
- # [+destination+]
200
- # Can be given a symbol a backend-specific object, an instance of String or IO classes.
201
- # [<tt>:string</tt>] will return an XML string.
202
- # [<tt>:document</tt>] will return a backend specific document object.
203
- # [<tt>:object</tt>] will return a backend specific object. New document will be created.
204
- # [an instance of +String+] the contents of the string will be replaced with the generated XML.
205
- # [an instance of +IO+] the IO will be written to.
206
- # [+options+] Backend-specific options
207
- #
208
- # === Example:
209
- # cat = Cat.new
210
- # cat.name = 'Pussy'
211
- # puts cat.build
212
- # ...
213
- # doc = REXML::Document.new
214
- # cat.build(doc)
215
- # puts doc.to_s
216
- def build(result = :string, options = {})
217
- self.class.build(self, result, options)
218
- end
219
- end
data/lib/peanuts/rexml.rb DELETED
@@ -1,98 +0,0 @@
1
- require 'rexml/document'
2
- require 'peanuts/backend'
3
-
4
- class Peanuts::XmlBackend::REXMLBackend #:nodoc:
5
- def parse(source, options)
6
- case source
7
- when nil
8
- return nil
9
- when REXML::Document
10
- node = source.root
11
- when REXML::Node
12
- node = source
13
- when String, IO
14
- node = REXML::Document.new(source).root
15
- else
16
- raise ArgumentError, 'invalid source'
17
- end
18
- node && yield(node)
19
- end
20
-
21
- def build(result, options)
22
- case result
23
- when :string, :document, :object, String, IO
24
- doc = REXML::Document.new
25
- when REXML::Document
26
- doc = result
27
- when REXML::Node
28
- node, doc = result, result.document
29
- else
30
- raise ArgumentError, 'invalid destination'
31
- end
32
- node ||= doc.root
33
- unless node
34
- name, ns, prefix = options[:xmlname], options[:xmlns], options[:xmlns_prefix]
35
- name, ns = "#{prefix}:#{name}", nil if prefix
36
- node = add_element(doc, name, ns, nil)
37
- end
38
-
39
- yield node
40
-
41
- case result
42
- when :string
43
- doc.to_s
44
- when String
45
- result.replace(doc.to_s)
46
- when IO
47
- doc.write(result)
48
- result
49
- when REXML::Document, :document
50
- doc
51
- when REXML::Node, :object
52
- node
53
- end
54
- end
55
-
56
- def add_namespaces(context, namespaces)
57
- namespaces.each {|prefix, uri| context.add_namespace(prefix.to_s, uri) }
58
- end
59
-
60
- def each_element(context, name, ns, &block)
61
- ns = context.namespace unless ns
62
- REXML::XPath.each(context, "ns:#{name}", 'ns' => ns, &block)
63
- end
64
-
65
- def value(node)
66
- node.text
67
- end
68
-
69
- def attribute(context, name, ns)
70
- name, ns = to_prefixed_name(context, name, ns, true)
71
- context.attributes[name]
72
- end
73
-
74
- def set_attribute(context, name, ns, text)
75
- name, ns = to_prefixed_name(context, name, ns, true)
76
- context.add_attribute(name, text)
77
- end
78
-
79
- def add_element(context, name, ns, text)
80
- name, ns = to_prefixed_name(context, name, ns, false)
81
- elem = context.add_element(name)
82
- elem.add_namespace(ns) if ns
83
- elem.text = text if text
84
- elem
85
- end
86
-
87
- private
88
- def to_prefixed_name(context, name, ns, prefix_required)
89
- if ns
90
- if prefix = context.namespaces.invert[ns]
91
- name, ns = "#{prefix}:#{name}", nil
92
- else
93
- raise ArgumentError, "no prefix defined for #{ns}" if prefix_required
94
- end
95
- end
96
- return name, ns
97
- end
98
- end
data/test/parsing_test.rb DELETED
@@ -1,115 +0,0 @@
1
- #$:.unshift File.join(File.dirname(__FILE__),'..','lib')
2
-
3
- require 'bigdecimal'
4
- require 'test/unit'
5
- require 'rubygems'
6
- require 'shoulda'
7
- require 'lib/peanuts'
8
-
9
-
10
- class Cheezburger
11
- include Peanuts
12
-
13
- attribute :weight, :float
14
- attribute :price, :decimal
15
- end
16
-
17
- class Cat
18
- include Peanuts
19
-
20
- namespaces :lol => 'urn:x-lol', :kthnx => 'urn:x-lol:kthnx'
21
-
22
- root 'kitteh', :xmlns => :lol
23
-
24
- attribute :has_tail, :boolean, :xmlname => 'has-tail', :xmlns => 'urn:x-lol:kthnx'
25
- attribute :ears, :integer
26
-
27
- element :ration, [:string], :xmlname => :eats, :xmlns => :kthnx
28
- element :name, :string, :xmlns => 'urn:x-lol:kthnx'
29
- elements :paws, :string, :xmlname => :paw
30
-
31
- element :friends, :xmlname => :pals do
32
- elements :names, :string, :xmlname => :pal
33
- end
34
-
35
- element :cheezburger, Cheezburger
36
- element :moar_cheezburgers do
37
- elements :cheezburger, Cheezburger
38
- end
39
- end
40
-
41
- class ParsingTest < Test::Unit::TestCase
42
- def setup
43
- @xml_fragment = <<-EOS
44
- <kitteh xmlns='urn:x-lol' xmlns:kthnx='urn:x-lol:kthnx' ears=' 2 ' kthnx:has-tail=' yes '>
45
- <name xmlns='urn:x-lol:kthnx'>
46
- Silly
47
- Tom
48
- </name>
49
- <kthnx:eats>
50
- tigers
51
- lions
52
- </kthnx:eats>
53
- <pals>
54
- <pal>Chrissy</pal>
55
- <pal>Missy</pal>
56
- <pal>Sissy</pal>
57
- </pals>
58
- <paw> one</paw>
59
- <paw> two </paw>
60
- <paw>three</paw>
61
- <paw>four</paw>
62
- <cheezburger price='2.05' weight='14.5547' />
63
- <moar_cheezburgers>
64
- <cheezburger price='19' weight='685.940' />
65
- <cheezburger price='7.40' weight='9356.7' />
66
- </moar_cheezburgers>
67
- </kitteh>
68
- EOS
69
- @cat = Cat.parse(@xml_fragment)
70
- end
71
-
72
- context "A cat" do
73
- should 'be named Silly Tom' do
74
- assert_equal 'Silly Tom', @cat.name
75
- end
76
-
77
- should 'eat tigers and lions' do
78
- assert_equal %w(tigers lions), @cat.ration
79
- end
80
-
81
- should 'be a friend of Chrissy, Missy & Sissy' do
82
- assert_equal ['Chrissy', 'Missy', 'Sissy'], @cat.friends.names
83
- end
84
-
85
- should 'have 2 ears' do
86
- assert_equal 2, @cat.ears
87
- end
88
-
89
- should 'have a tail' do
90
- assert @cat.has_tail
91
- end
92
-
93
- should 'have four paws' do
94
- assert_equal %w(one two three four), @cat.paws
95
- end
96
-
97
- should 'has cheezburger' do
98
- assert_kind_of Cheezburger, @cat.cheezburger
99
- end
100
- end
101
-
102
- context 'A cheezburger' do
103
- setup do
104
- @burger = @cat.cheezburger
105
- end
106
-
107
- should 'weigh 14.5547 pounds' do
108
- assert_equal 14.5547, @burger.weight
109
- end
110
-
111
- should 'cost $2.05' do
112
- assert_equal BigDecimal('2.05'), @burger.price
113
- end
114
- end
115
- end