omg-peanuts 2.0.0 → 2.0.1
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 +12 -8
- data/Rakefile +6 -3
- data/lib/peanuts/converters.rb +2 -2
- data/lib/peanuts/mapper.rb +10 -26
- data/lib/peanuts/mappings.rb +65 -54
- data/lib/peanuts/nuts.rb +91 -106
- data/lib/peanuts/xml.rb +78 -1
- data/lib/peanuts/xml/libxml.rb +22 -33
- data/spec/cat_spec.rb +142 -0
- metadata +5 -6
- data/lib/peanuts/xml/reader.rb +0 -78
- data/test/parsing_test.rb +0 -115
data/README.rdoc
CHANGED
@@ -47,18 +47,21 @@ Please report via Github issue tracking.
|
|
47
47
|
|
48
48
|
root 'kitteh', :xmlns => :lol
|
49
49
|
|
50
|
-
attribute :has_tail, :boolean, :xmlname => 'has-tail', :xmlns =>
|
50
|
+
attribute :has_tail, :boolean, :xmlname => 'has-tail', :xmlns => :kthnx
|
51
51
|
attribute :ears, :integer
|
52
52
|
|
53
53
|
element :ration, [:string], :xmlname => :eats, :xmlns => :kthnx
|
54
|
-
element :name, :
|
55
|
-
elements :paws, :
|
54
|
+
element :name, :xmlns => 'urn:x-lol:kthnx'
|
55
|
+
elements :paws, :xmlname => :paw
|
56
56
|
|
57
|
-
element :friends, :xmlname => :pals do
|
58
|
-
elements :names, :
|
57
|
+
element :friends, :xmlname => :pals do
|
58
|
+
elements :names, :xmlname => :pal
|
59
59
|
end
|
60
60
|
|
61
61
|
element :cheezburger, Cheezburger
|
62
|
+
element :moar_cheezburgers do
|
63
|
+
elements :cheezburger, Cheezburger
|
64
|
+
end
|
62
65
|
end
|
63
66
|
|
64
67
|
xml_fragment = <<-EOS
|
@@ -83,7 +86,7 @@ Please report via Github issue tracking.
|
|
83
86
|
<cheezburger weight='2' />
|
84
87
|
</kitteh>
|
85
88
|
EOS
|
86
|
-
cat = Cat.
|
89
|
+
cat = Cat.restore_from(xml_fragment)
|
87
90
|
|
88
91
|
assert_equal 'Silly Tom', cat.name
|
89
92
|
assert_equal %w(tigers lions), cat.ration
|
@@ -94,12 +97,13 @@ Please report via Github issue tracking.
|
|
94
97
|
assert_kind_of Cheezburger, cat.cheezburger
|
95
98
|
assert_equal 2, cat.cheezburger.weight
|
96
99
|
...
|
97
|
-
puts cat.
|
100
|
+
puts cat.save_to(:string)
|
98
101
|
|
99
102
|
|
100
103
|
=== See also
|
101
|
-
* http://github.com/omg/
|
104
|
+
* http://github.com/omg/quack -- ... here is my handle, here is my spout
|
102
105
|
* http://github.com/omg/statelogic -- A simple state machine for ActiveRecord
|
106
|
+
* http://github.com/omg/threadpool -- Thread pool implementation
|
103
107
|
|
104
108
|
|
105
109
|
Copyright (c) 2009 Igor Gunko, released under the MIT license
|
data/Rakefile
CHANGED
@@ -5,7 +5,7 @@ require 'rake'
|
|
5
5
|
require 'rake/clean'
|
6
6
|
require 'rake/gempackagetask'
|
7
7
|
require 'rake/rdoctask'
|
8
|
-
require 'rake/
|
8
|
+
require 'spec/rake/spectask'
|
9
9
|
|
10
10
|
Rake::GemPackageTask.new(Gem::Specification.load('peanuts.gemspec')) do |p|
|
11
11
|
p.need_tar = true
|
@@ -21,6 +21,9 @@ Rake::RDocTask.new do |rdoc|
|
|
21
21
|
rdoc.options << '--line-numbers' << '--inline-source'
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
|
-
|
24
|
+
desc 'Run specs'
|
25
|
+
task :test => :spec
|
26
|
+
|
27
|
+
Spec::Rake::SpecTask.new do |t|
|
28
|
+
t.spec_files = FileList['spec/**/*.rb']
|
26
29
|
end
|
data/lib/peanuts/converters.rb
CHANGED
@@ -83,8 +83,8 @@ module Peanuts
|
|
83
83
|
def to_xml(flag)
|
84
84
|
return nil if flag.nil?
|
85
85
|
string = case @format
|
86
|
-
when :true_false then flag ? 'true' : 'false'
|
87
|
-
when :yes_no then flag ? 'yes' : 'no'
|
86
|
+
when :true_false, :truefalse then flag ? 'true' : 'false'
|
87
|
+
when :yes_no, :yesno then flag ? 'yes' : 'no'
|
88
88
|
when :numeric then flag ? '0' : '1'
|
89
89
|
end
|
90
90
|
super(string)
|
data/lib/peanuts/mapper.rb
CHANGED
@@ -4,30 +4,21 @@ module Peanuts
|
|
4
4
|
class Mapper
|
5
5
|
include Enumerable
|
6
6
|
|
7
|
-
attr_reader :root, :namespaces, :
|
7
|
+
attr_reader :root, :namespaces, :ns_context, :default_ns
|
8
8
|
attr_accessor :schema
|
9
9
|
|
10
|
-
def initialize
|
10
|
+
def initialize(ns_context = nil, default_ns = nil)
|
11
|
+
@ns_context, @default_ns = ns_context, default_ns
|
11
12
|
@mappings, @footprints = [], {}
|
12
|
-
@namespaces = Hash.new
|
13
|
-
nscontext && nscontext[k] || raise(IndexError)
|
14
|
-
end
|
13
|
+
@namespaces = ns_context ? Hash.new {|h, k| ns_context[k] || raise(IndexError) } : {}
|
15
14
|
end
|
16
15
|
|
17
16
|
def root=(root)
|
18
17
|
raise 'root already defined' if @root
|
19
|
-
raise 'root in nested scopes not supported' if nested?
|
18
|
+
# TODO raise 'root in nested scopes not supported' if nested?
|
20
19
|
@root = root
|
21
20
|
end
|
22
21
|
|
23
|
-
def set_context(container, nscontext)
|
24
|
-
@container, @nscontext = container, nscontext
|
25
|
-
end
|
26
|
-
|
27
|
-
def nested?
|
28
|
-
!!container
|
29
|
-
end
|
30
|
-
|
31
22
|
def each(&block)
|
32
23
|
@mappings.each(&block)
|
33
24
|
end
|
@@ -38,24 +29,17 @@ module Peanuts
|
|
38
29
|
@mappings << (@footprints[fp] = mapping)
|
39
30
|
end
|
40
31
|
|
41
|
-
def
|
32
|
+
def restore(nut, reader)
|
42
33
|
rdfp = ReaderFootprint.new(reader)
|
43
34
|
reader.each do
|
44
35
|
m = @footprints[rdfp]
|
45
|
-
m.
|
36
|
+
m.restore(nut, reader) if m
|
46
37
|
end
|
47
38
|
nut
|
48
39
|
end
|
49
40
|
|
50
|
-
def
|
51
|
-
|
52
|
-
@root.to_xml(writer) do
|
53
|
-
_save(nut, writer)
|
54
|
-
end
|
55
|
-
else
|
56
|
-
_save(nut, writer)
|
57
|
-
end
|
58
|
-
writer.result
|
41
|
+
def save(nut, writer)
|
42
|
+
@root ? @root.save(writer) { _save(nut, writer) } : _save(nut, writer)
|
59
43
|
end
|
60
44
|
|
61
45
|
def clear(nut)
|
@@ -64,7 +48,7 @@ module Peanuts
|
|
64
48
|
|
65
49
|
private
|
66
50
|
def _save(nut, writer)
|
67
|
-
@mappings.each {|m| m.
|
51
|
+
@mappings.each {|m| m.save(nut, writer) } if nut
|
68
52
|
end
|
69
53
|
|
70
54
|
class Footprint
|
data/lib/peanuts/mappings.rb
CHANGED
@@ -25,7 +25,7 @@ module Peanuts
|
|
25
25
|
class Root < Mapping
|
26
26
|
node_type :element
|
27
27
|
|
28
|
-
def
|
28
|
+
def save(writer, &block)
|
29
29
|
writer.write(node_type, xmlname, xmlns, prefix, &block)
|
30
30
|
end
|
31
31
|
end
|
@@ -37,7 +37,7 @@ module Peanuts
|
|
37
37
|
super(options.delete(:xmlname) || name, options)
|
38
38
|
case type
|
39
39
|
when Array
|
40
|
-
raise ArgumentError, "invalid value for type: #{type}" if type.length != 1
|
40
|
+
raise ArgumentError, "invalid value for type: #{type.inspect}" if type.length != 1
|
41
41
|
options[:item_type] = type.first
|
42
42
|
@converter = Converter.create!(:list, options)
|
43
43
|
when Class
|
@@ -48,12 +48,12 @@ module Peanuts
|
|
48
48
|
@name, @setter, @type = name.to_sym, :"#{name}=", type
|
49
49
|
end
|
50
50
|
|
51
|
-
def
|
52
|
-
|
51
|
+
def save(nut, writer)
|
52
|
+
save_value(writer, get(nut))
|
53
53
|
end
|
54
54
|
|
55
|
-
def
|
56
|
-
set(nut,
|
55
|
+
def restore(nut, reader)
|
56
|
+
set(nut, restore_value(reader, get(nut)))
|
57
57
|
end
|
58
58
|
|
59
59
|
def clear(nut)
|
@@ -69,99 +69,110 @@ module Peanuts
|
|
69
69
|
nut.send(@setter, value)
|
70
70
|
end
|
71
71
|
|
72
|
-
def
|
72
|
+
def to_xml(value)
|
73
73
|
@converter ? @converter.to_xml(value) : value
|
74
74
|
end
|
75
75
|
|
76
|
-
def
|
76
|
+
def from_xml(text)
|
77
77
|
@converter ? @converter.from_xml(text) : text
|
78
78
|
end
|
79
79
|
|
80
|
-
def
|
81
|
-
|
80
|
+
def write(writer, &block)
|
81
|
+
writer.write(node_type, xmlname, xmlns, prefix, &block)
|
82
82
|
end
|
83
83
|
|
84
|
-
def
|
84
|
+
def restore_object(events)
|
85
85
|
type.send(:_restore, events)
|
86
86
|
end
|
87
87
|
|
88
|
-
def
|
88
|
+
def save_object(nut, writer)
|
89
89
|
type.send(:_save, nut, writer)
|
90
90
|
end
|
91
91
|
end
|
92
92
|
|
93
|
-
|
94
|
-
node_type :element
|
95
|
-
|
93
|
+
module SingleMapping
|
96
94
|
private
|
97
|
-
def
|
98
|
-
|
95
|
+
def restore_value(reader, acc)
|
96
|
+
read_value(reader)
|
99
97
|
end
|
100
98
|
|
101
|
-
def
|
102
|
-
|
103
|
-
w.value = toxml(value)
|
104
|
-
end
|
99
|
+
def save_value(writer, value)
|
100
|
+
write(writer) {|w| write_value(w, value) }
|
105
101
|
end
|
106
102
|
end
|
107
103
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
private
|
112
|
-
def getxml(node)
|
113
|
-
parse(node)
|
104
|
+
module MultiMapping
|
105
|
+
def restore_value(reader, acc)
|
106
|
+
(acc || []) << read_value(reader)
|
114
107
|
end
|
115
108
|
|
116
|
-
def
|
117
|
-
|
118
|
-
|
109
|
+
def save_value(writer, values)
|
110
|
+
for value in values
|
111
|
+
write(writer) {|w| write_value(w, value) }
|
119
112
|
end
|
120
113
|
end
|
121
114
|
end
|
122
115
|
|
123
|
-
|
124
|
-
|
116
|
+
module ValueMapping
|
117
|
+
private
|
118
|
+
def read_value(reader)
|
119
|
+
from_xml(reader.value)
|
120
|
+
end
|
125
121
|
|
126
|
-
def
|
127
|
-
|
128
|
-
raise ArgumentError, 'a namespaced attribute must have namespace prefix' if xmlns && !prefix
|
122
|
+
def write_value(writer, value)
|
123
|
+
writer.value = to_xml(value)
|
129
124
|
end
|
125
|
+
end
|
130
126
|
|
127
|
+
module ObjectMapping
|
131
128
|
private
|
132
|
-
def
|
133
|
-
|
129
|
+
def read_value(reader)
|
130
|
+
restore_object(reader)
|
134
131
|
end
|
135
132
|
|
136
|
-
def
|
137
|
-
|
133
|
+
def write_value(writer, value)
|
134
|
+
save_object(value, writer)
|
138
135
|
end
|
139
136
|
end
|
140
137
|
|
141
|
-
class
|
138
|
+
class ElementValue < MemberMapping
|
139
|
+
include SingleMapping
|
140
|
+
include ValueMapping
|
141
|
+
|
142
142
|
node_type :element
|
143
|
+
end
|
143
144
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
145
|
+
class Element < MemberMapping
|
146
|
+
include SingleMapping
|
147
|
+
include ObjectMapping
|
148
|
+
|
149
|
+
node_type :element
|
150
|
+
end
|
148
151
|
|
149
|
-
|
150
|
-
|
152
|
+
class Attribute < MemberMapping
|
153
|
+
include SingleMapping
|
154
|
+
include ValueMapping
|
155
|
+
|
156
|
+
node_type :attribute
|
157
|
+
|
158
|
+
def initialize(name, type, options)
|
159
|
+
super
|
160
|
+
raise ArgumentError, 'a namespaced attribute must have namespace prefix' if xmlns && !prefix
|
151
161
|
end
|
152
162
|
end
|
153
163
|
|
154
|
-
class
|
164
|
+
class ElementValues < MemberMapping
|
165
|
+
include MultiMapping
|
166
|
+
include ValueMapping
|
167
|
+
|
155
168
|
node_type :element
|
169
|
+
end
|
156
170
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
end
|
171
|
+
class Elements < MemberMapping
|
172
|
+
include MultiMapping
|
173
|
+
include ObjectMapping
|
161
174
|
|
162
|
-
|
163
|
-
elements.each {|e| build(node, e, add_element(node)) } if elements
|
164
|
-
end
|
175
|
+
node_type :element
|
165
176
|
end
|
166
177
|
end
|
167
178
|
end
|
data/lib/peanuts/nuts.rb
CHANGED
@@ -1,27 +1,28 @@
|
|
1
1
|
require 'peanuts/mappings'
|
2
2
|
require 'peanuts/mapper'
|
3
|
-
require 'peanuts/xml/reader'
|
4
3
|
|
5
4
|
module Peanuts #:nodoc:
|
6
|
-
# See also +ClassMethods+
|
7
5
|
def self.included(other) #:nodoc:
|
8
|
-
other
|
6
|
+
init(other)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.init(cls, ns_context = nil, default_ns = nil, &block) #:nodoc:
|
10
|
+
cls.instance_eval do
|
11
|
+
include InstanceMethods
|
12
|
+
extend ClassMethods
|
13
|
+
@mapper = Mapper.new(ns_context, default_ns)
|
14
|
+
instance_eval(&block) if block_given?
|
15
|
+
end
|
9
16
|
end
|
10
17
|
|
11
|
-
# See also +
|
18
|
+
# See also +InstanceMethods+.
|
12
19
|
module ClassMethods
|
13
20
|
include Mappings
|
14
21
|
|
15
|
-
|
16
|
-
other.instance_eval do
|
17
|
-
@mappings = Mapper.new
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
# mappings -> Mapper
|
22
|
+
# mapper -> Mapper
|
22
23
|
#
|
23
|
-
# Returns
|
24
|
-
attr_reader :
|
24
|
+
# Returns the mapper for the class.
|
25
|
+
attr_reader :mapper
|
25
26
|
|
26
27
|
# namespaces(hash) -> Hash
|
27
28
|
# namespaces -> Hash
|
@@ -36,8 +37,8 @@ module Peanuts #:nodoc:
|
|
36
37
|
# namespaces :lol => 'urn:lol', ...
|
37
38
|
# ...
|
38
39
|
# end
|
39
|
-
def namespaces(
|
40
|
-
|
40
|
+
def namespaces(map = nil)
|
41
|
+
map ? mapper.namespaces.update(map) : mapper.namespaces
|
41
42
|
end
|
42
43
|
|
43
44
|
# root(xmlname[, :xmlns => ...]) -> Mappings::Root
|
@@ -58,8 +59,8 @@ module Peanuts #:nodoc:
|
|
58
59
|
# ...
|
59
60
|
# end
|
60
61
|
def root(xmlname = nil, options = {})
|
61
|
-
|
62
|
-
|
62
|
+
mapper.root = Root.new(xmlname, prepare_options(options)) if xmlname
|
63
|
+
mapper.root
|
63
64
|
end
|
64
65
|
|
65
66
|
# element(name, [type[, options]]) -> Mappings::Element or Mappings::ElementValue
|
@@ -81,10 +82,10 @@ module Peanuts #:nodoc:
|
|
81
82
|
# element :cheeseburger, Cheeseburger, :xmlname => :cheezburger
|
82
83
|
# ...
|
83
84
|
# end
|
84
|
-
def element(name,
|
85
|
-
prepare_args(
|
85
|
+
def element(name, *args, &block)
|
86
|
+
prepare_args(args, block) do |type, options|
|
86
87
|
define_accessor name
|
87
|
-
(
|
88
|
+
(mapper << (type.is_a?(Class) ? Element : ElementValue).new(name, type, options)).last
|
88
89
|
end
|
89
90
|
end
|
90
91
|
|
@@ -107,10 +108,10 @@ module Peanuts #:nodoc:
|
|
107
108
|
# elements :cheeseburgers, Cheeseburger, :xmlname => :cheezburger
|
108
109
|
# ...
|
109
110
|
# end
|
110
|
-
def elements(name,
|
111
|
-
prepare_args(
|
111
|
+
def elements(name, *args, &block)
|
112
|
+
prepare_args(args, block) do |type, options|
|
112
113
|
define_accessor name
|
113
|
-
(
|
114
|
+
(mapper << (type.is_a?(Class) ? Elements : ElementValues).new(name, type, options)).last
|
114
115
|
end
|
115
116
|
end
|
116
117
|
|
@@ -131,14 +132,16 @@ module Peanuts #:nodoc:
|
|
131
132
|
# element :cheeseburger, Cheeseburger, :xmlname => :cheezburger
|
132
133
|
# ...
|
133
134
|
# end
|
134
|
-
def attribute(name,
|
135
|
-
|
136
|
-
|
135
|
+
def attribute(name, *args)
|
136
|
+
prepare_args(args, nil, :xmlns => nil) do |type, options|
|
137
|
+
define_accessor name
|
138
|
+
mapper << Attribute.new(name, type, options)
|
139
|
+
end
|
137
140
|
end
|
138
141
|
|
139
142
|
def schema(schema = nil)
|
140
|
-
|
141
|
-
|
143
|
+
mapper.schema = schema if schema
|
144
|
+
mapper.schema
|
142
145
|
end
|
143
146
|
|
144
147
|
def restore(reader)
|
@@ -146,70 +149,49 @@ module Peanuts #:nodoc:
|
|
146
149
|
e && _restore(e)
|
147
150
|
end
|
148
151
|
|
149
|
-
def restore_from(
|
150
|
-
_source_or_dest(
|
152
|
+
def restore_from(*args)
|
153
|
+
_source_or_dest(*args) do |source_type, source, options|
|
151
154
|
restore(XML::Reader.new(source, source_type, options))
|
152
155
|
end
|
153
156
|
end
|
154
157
|
|
155
158
|
def save(nut, writer)
|
156
159
|
_save(nut, writer)
|
160
|
+
writer.result
|
157
161
|
end
|
158
162
|
|
159
|
-
def
|
160
|
-
options
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
end
|
165
|
-
|
166
|
-
def build_node(nut, node) #:nodoc:
|
167
|
-
backend.add_namespaces(node, namespaces)
|
168
|
-
callem(:to_xml, nut, node)
|
169
|
-
node
|
170
|
-
end
|
171
|
-
|
172
|
-
def parse_events(nut, events) #:nodoc:
|
173
|
-
@mappings.parse(nut, events)
|
174
|
-
end
|
175
|
-
|
176
|
-
def _source_or_dest(a, b)
|
177
|
-
a, b = :string, a unless a.is_a?(Symbol)
|
178
|
-
yield a, b
|
163
|
+
def _source_or_dest(*args)
|
164
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
165
|
+
type, source = *args
|
166
|
+
type, source = :string, type unless type.is_a?(Symbol)
|
167
|
+
yield type, source, options
|
179
168
|
end
|
180
169
|
|
181
170
|
private
|
182
|
-
def _restore(
|
183
|
-
nut = new
|
184
|
-
@mappings.parse(nut, events)
|
171
|
+
def _restore(reader)
|
172
|
+
mapper.restore(nut = new, reader)
|
185
173
|
nut
|
186
174
|
end
|
187
175
|
|
188
|
-
def _save(nut,
|
189
|
-
|
176
|
+
def _save(nut, writer)
|
177
|
+
mapper.save(nut, writer)
|
190
178
|
end
|
191
179
|
|
192
|
-
def prepare_args(
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
end
|
202
|
-
end
|
203
|
-
else
|
204
|
-
options = prepare_options(options)
|
205
|
-
yield type, options
|
206
|
-
end
|
180
|
+
def prepare_args(args, blk, defopt = nil)
|
181
|
+
type, options = *args
|
182
|
+
type, options = (blk ? Class.new : :string), type if type.nil? || type.is_a?(Hash)
|
183
|
+
options ||= {}
|
184
|
+
options = defopt.merge(options) if defopt
|
185
|
+
options = prepare_options(options)
|
186
|
+
m = yield(type, options)
|
187
|
+
Peanuts.init(type, mapper.namespaces, m.xmlns, &blk) if blk
|
188
|
+
m
|
207
189
|
end
|
208
190
|
|
209
191
|
def prepare_options(options)
|
210
|
-
ns = options.fetch(:xmlns) {|k| options[k] = root && root.xmlns ||
|
192
|
+
ns = options.fetch(:xmlns) {|k| options[k] = root && root.xmlns || mapper.default_ns }
|
211
193
|
if ns.is_a?(Symbol)
|
212
|
-
raise ArgumentError, "undefined prefix: #{ns}" unless options[:xmlns] = namespaces[ns]
|
194
|
+
raise ArgumentError, "undefined prefix: #{ns}" unless options[:xmlns] = mapper.namespaces[ns]
|
213
195
|
options[:prefix] = ns
|
214
196
|
end
|
215
197
|
options
|
@@ -217,47 +199,50 @@ module Peanuts #:nodoc:
|
|
217
199
|
|
218
200
|
def define_accessor(name)
|
219
201
|
if method_defined?(name) || method_defined?("#{name}=")
|
220
|
-
raise ArgumentError, "#{name}: name already defined or reserved"
|
202
|
+
raise ArgumentError, "#{name.inspect}: name already defined or reserved"
|
221
203
|
end
|
222
204
|
attr_accessor name
|
223
205
|
end
|
224
206
|
end
|
225
207
|
|
226
|
-
|
227
|
-
|
228
|
-
|
208
|
+
# See also +ClassMethods+
|
209
|
+
module InstanceMethods
|
210
|
+
def parse(source, options = {})
|
211
|
+
backend.parse(source, options) {|node| parse_node(node) }
|
212
|
+
end
|
229
213
|
|
230
|
-
|
231
|
-
|
232
|
-
|
214
|
+
def save(writer)
|
215
|
+
self.class.save(self, writer)
|
216
|
+
end
|
233
217
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
218
|
+
# build([options]) -> root element or string
|
219
|
+
# build([options]) -> root element or string
|
220
|
+
# build(destination[, options]) -> destination
|
221
|
+
#
|
222
|
+
# Defines attribute mapping.
|
223
|
+
#
|
224
|
+
# === Arguments
|
225
|
+
# [+destination+]
|
226
|
+
# Can be given a symbol a backend-specific object, an instance of String or IO classes.
|
227
|
+
# [<tt>:string</tt>] will return an XML string.
|
228
|
+
# [<tt>:document</tt>] will return a backend specific document object.
|
229
|
+
# [<tt>:object</tt>] will return a backend specific object. New document will be created.
|
230
|
+
# [an instance of +String+] the contents of the string will be replaced with the generated XML.
|
231
|
+
# [an instance of +IO+] the IO will be written to.
|
232
|
+
# [+options+] Backend-specific options
|
233
|
+
#
|
234
|
+
# === Example:
|
235
|
+
# cat = Cat.new
|
236
|
+
# cat.name = 'Pussy'
|
237
|
+
# puts cat.save_to(:string)
|
238
|
+
# ...
|
239
|
+
# doc = LibXML::XML::Document.new
|
240
|
+
# cat.save_to(doc)
|
241
|
+
# puts doc.to_s
|
242
|
+
def save_to(*args)
|
243
|
+
self.class._source_or_dest(*args) do |dest_type, dest, options|
|
244
|
+
save(XML::Writer.new(dest, dest_type, options))
|
245
|
+
end
|
261
246
|
end
|
262
247
|
end
|
263
248
|
end
|
data/lib/peanuts/xml.rb
CHANGED
@@ -1 +1,78 @@
|
|
1
|
-
require '
|
1
|
+
require 'enumerator'
|
2
|
+
|
3
|
+
class Hash
|
4
|
+
def from_namespace(ns)
|
5
|
+
rx = /^#{Regexp.quote(ns.to_s)}_(.*)$/
|
6
|
+
inject({}) do |a, p|
|
7
|
+
a[$1.to_sym] = p[1] if p[0].to_s =~ rx
|
8
|
+
a
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def from_namespace!(ns)
|
13
|
+
h = from_namespace(ns)
|
14
|
+
h.each_key {|k| delete(:"#{ns}_#{k}") }
|
15
|
+
h
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module Peanuts
|
20
|
+
module XML
|
21
|
+
autoload :LibXML, 'peanuts/xml/libxml'
|
22
|
+
|
23
|
+
def self.default
|
24
|
+
@@default ||= LibXML
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.schema(schema_type, source, source_type = :string)
|
28
|
+
default.schema(schema_type, source, source_type)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.method_missing(method, *args, &block)
|
32
|
+
case method.to_s
|
33
|
+
when /^(.*)_schema_from_(.*)$/
|
34
|
+
XML.schema($1.to_sym, args.first, $2.to_sym)
|
35
|
+
else
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class Reader
|
41
|
+
include Enumerable
|
42
|
+
|
43
|
+
def self.new(*args, &block)
|
44
|
+
cls = self == Reader ? XML.default::Reader : self
|
45
|
+
obj = cls.allocate
|
46
|
+
obj.send(:initialize, *args, &block)
|
47
|
+
obj
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.method_missing(method, *args, &block)
|
51
|
+
case method.to_s
|
52
|
+
when /^from_(.*)$/
|
53
|
+
new(args.first, $1.to_sym, &block)
|
54
|
+
else
|
55
|
+
super
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class Writer
|
61
|
+
def self.new(*args, &block)
|
62
|
+
cls = self == Writer ? XML.default::Writer : self
|
63
|
+
obj = cls.allocate
|
64
|
+
obj.send(:initialize, *args, &block)
|
65
|
+
obj
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.method_missing(method, *args, &block)
|
69
|
+
case method.to_s
|
70
|
+
when /^from_(.*)$/
|
71
|
+
new(args.first, $1.to_sym, &block)
|
72
|
+
else
|
73
|
+
super
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/peanuts/xml/libxml.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'libxml'
|
2
2
|
require 'forwardable'
|
3
|
-
require 'peanuts/xml
|
3
|
+
require 'peanuts/xml'
|
4
4
|
|
5
5
|
module Peanuts
|
6
6
|
module XML
|
@@ -14,7 +14,7 @@ module Peanuts
|
|
14
14
|
when :io
|
15
15
|
dest
|
16
16
|
when :document
|
17
|
-
dest || LibXML::XML::Document.new
|
17
|
+
dest || ::LibXML::XML::Document.new
|
18
18
|
else
|
19
19
|
raise ArgumentError, "unrecognized destination type #{dest_type.inspect}"
|
20
20
|
end
|
@@ -24,31 +24,31 @@ module Peanuts
|
|
24
24
|
@dest
|
25
25
|
end
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
def value=(value)
|
28
|
+
case @node
|
29
|
+
when ::LibXML::XML::Attr
|
30
|
+
@node.value = value || ''
|
31
|
+
else
|
32
|
+
@node.content = value || ''
|
33
|
+
end
|
31
34
|
end
|
32
35
|
|
33
36
|
def write(node_type, local_name = nil, namespace_uri = nil, prefix = nil)
|
34
|
-
|
35
|
-
|
37
|
+
case node_type
|
38
|
+
when :element
|
39
|
+
@node = ::LibXML::XML::Node.new(local_name)
|
40
|
+
@parent << @node if @parent
|
41
|
+
@node.namespaces.namespace = mkns(@node, namespace_uri, prefix) if namespace_uri
|
42
|
+
when :attribute
|
43
|
+
@node = ::LibXML::XML::Attr.new(@parent, local_name, '', namespace_uri && mkns(@parent, namespace_uri, prefix))
|
36
44
|
else
|
37
|
-
|
45
|
+
raise "unsupported node type #{node_type.inspect}"
|
38
46
|
end
|
39
47
|
|
40
|
-
@node_type = node_type
|
41
|
-
@local_name, @namespace_uri, @prefix = local_name, namespace_uri, prefix && prefix.to_s
|
42
|
-
|
43
48
|
exparent, @parent = @parent, @node
|
44
49
|
|
45
50
|
yield self
|
46
51
|
|
47
|
-
if @node_type
|
48
|
-
mknode
|
49
|
-
end
|
50
|
-
@node = nil
|
51
|
-
|
52
52
|
if exparent.nil?
|
53
53
|
case @dest_type
|
54
54
|
when :string, :io
|
@@ -62,22 +62,11 @@ module Peanuts
|
|
62
62
|
end
|
63
63
|
|
64
64
|
private
|
65
|
-
def
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
ns = @parent && @parent.namespaces.find_by_href(@namespace_uri)
|
71
|
-
n.namespaces.namespace = ns && ns.prefix == @prefix ? ns : ::LibXML::XML::Namespace.new(n, @prefix, @namespace_uri)
|
72
|
-
end
|
73
|
-
@parent << n if @parent
|
74
|
-
n
|
75
|
-
when :attribute
|
76
|
-
::LibXML::XML::Attr.new(@parent, @local_name, @value, @namespace_uri)
|
77
|
-
else
|
78
|
-
raise "unsupported node type #{@node_type.inspect}"
|
79
|
-
end
|
80
|
-
clear
|
65
|
+
def mkns(node, namespace_uri, prefix)
|
66
|
+
prefix = prefix && prefix.to_s
|
67
|
+
ns = node && node.namespaces.find_by_href(namespace_uri)
|
68
|
+
ns = ::LibXML::XML::Namespace.new(node, prefix, namespace_uri) unless ns && ns.prefix == prefix
|
69
|
+
ns
|
81
70
|
end
|
82
71
|
end
|
83
72
|
|
data/spec/cat_spec.rb
ADDED
@@ -0,0 +1,142 @@
|
|
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 Cat
|
28
|
+
include Peanuts
|
29
|
+
|
30
|
+
namespaces :lol => 'urn:x-lol', :kthnx => 'urn:x-lol:kthnx'
|
31
|
+
|
32
|
+
root 'kitteh', :xmlns => :lol
|
33
|
+
|
34
|
+
attribute :has_tail, :boolean, :xmlname => 'has-tail', :xmlns => :kthnx
|
35
|
+
attribute :ears, :integer
|
36
|
+
|
37
|
+
element :ration, [:string], :xmlname => :eats, :xmlns => :kthnx
|
38
|
+
element :name, :xmlns => 'urn:x-lol:kthnx'
|
39
|
+
elements :paws, :xmlname => :paw
|
40
|
+
|
41
|
+
element :friends, :xmlname => :pals do
|
42
|
+
elements :names, :xmlname => :pal
|
43
|
+
end
|
44
|
+
|
45
|
+
element :cheezburger, Cheezburger
|
46
|
+
element :moar_cheezburgers do
|
47
|
+
elements :cheezburger, Cheezburger
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
shared_examples_for 'my cat' do
|
52
|
+
it 'should be named Silly Tom' do
|
53
|
+
@cat.name.should eql 'Silly Tom'
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should eat tigers and lions' do
|
57
|
+
@cat.ration.should eql %w(tigers lions)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should be a friend of Chrissy, Missy & Sissy' do
|
61
|
+
@cat.friends.names.should eql ['Chrissy', 'Missy', 'Sissy']
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should have 2 ears' do
|
65
|
+
@cat.ears.should eql 2
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should have a tail' do
|
69
|
+
@cat.has_tail.should be_true
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should have four paws' do
|
73
|
+
@cat.paws.should eql %w(one two three four)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should has cheezburger' do
|
77
|
+
@cat.cheezburger.should be_kind_of Cheezburger
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should has 2 moar good cheezburgerz' do
|
81
|
+
@cat.moar_cheezburgers.cheezburger.should eql [
|
82
|
+
Cheezburger.new(685.940, BigDecimal('19')),
|
83
|
+
Cheezburger.new(9356.7, BigDecimal('7.40'))]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
shared_examples_for 'my cheezburger' do
|
88
|
+
it 'should weigh 14.5547 pounds' do
|
89
|
+
@cheezburger.weight.should eql 14.5547
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should cost $2.05' do
|
93
|
+
@cheezburger.price.should eql BigDecimal('2.05')
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
shared_examples_for 'sample kitteh' do
|
98
|
+
before :all do
|
99
|
+
@xml_fragment = <<-EOS
|
100
|
+
<kitteh xmlns='urn:x-lol' xmlns:kthnx='urn:x-lol:kthnx' ears=' 2 ' kthnx:has-tail=' yes '>
|
101
|
+
<name xmlns='urn:x-lol:kthnx'>
|
102
|
+
Silly
|
103
|
+
Tom
|
104
|
+
</name>
|
105
|
+
<kthnx:eats>
|
106
|
+
tigers
|
107
|
+
lions
|
108
|
+
</kthnx:eats>
|
109
|
+
<pals>
|
110
|
+
<pal>Chrissy</pal>
|
111
|
+
<pal>Missy</pal>
|
112
|
+
<pal>Sissy</pal>
|
113
|
+
</pals>
|
114
|
+
<paw> one</paw>
|
115
|
+
<paw> two </paw>
|
116
|
+
<paw>three</paw>
|
117
|
+
<paw>four</paw>
|
118
|
+
<cheezburger price='2.05' weight='14.5547' />
|
119
|
+
<moar_cheezburgers>
|
120
|
+
<cheezburger price='19' weight='685.940' />
|
121
|
+
<cheezburger price='7.40' weight='9356.7' />
|
122
|
+
</moar_cheezburgers>
|
123
|
+
</kitteh>
|
124
|
+
EOS
|
125
|
+
@cat = Cat.restore_from(@xml_fragment)
|
126
|
+
@cheezburger = @cat.cheezburger
|
127
|
+
end
|
128
|
+
|
129
|
+
it_should_behave_like 'my cat', 'my cheezburger'
|
130
|
+
end
|
131
|
+
|
132
|
+
describe 'My cat' do
|
133
|
+
it_should_behave_like 'sample kitteh'
|
134
|
+
end
|
135
|
+
|
136
|
+
describe 'My cat saved and restored' do
|
137
|
+
it_should_behave_like 'sample kitteh'
|
138
|
+
|
139
|
+
before :all do
|
140
|
+
@cat = Cat.restore_from(@cat.save_to(:string))
|
141
|
+
end
|
142
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: omg-peanuts
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Igor Gunko
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-09-
|
12
|
+
date: 2009-09-25 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -23,14 +23,14 @@ dependencies:
|
|
23
23
|
version: 1.1.3
|
24
24
|
version:
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
|
-
name:
|
26
|
+
name: rspec
|
27
27
|
type: :development
|
28
28
|
version_requirement:
|
29
29
|
version_requirements: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 2.
|
33
|
+
version: 1.2.8
|
34
34
|
version:
|
35
35
|
description: Peanuts is an XML to Ruby and back again mapping library.
|
36
36
|
email: tekmon@gmail.com
|
@@ -52,7 +52,6 @@ files:
|
|
52
52
|
- lib/peanuts/converters.rb
|
53
53
|
- lib/peanuts/mapper.rb
|
54
54
|
- lib/peanuts/xml.rb
|
55
|
-
- lib/peanuts/xml/reader.rb
|
56
55
|
- lib/peanuts/xml/libxml.rb
|
57
56
|
has_rdoc: true
|
58
57
|
homepage: http://github.com/omg/peanuts
|
@@ -84,4 +83,4 @@ signing_key:
|
|
84
83
|
specification_version: 2
|
85
84
|
summary: Making XML <-> Ruby binding easy
|
86
85
|
test_files:
|
87
|
-
-
|
86
|
+
- spec/cat_spec.rb
|
data/lib/peanuts/xml/reader.rb
DELETED
@@ -1,78 +0,0 @@
|
|
1
|
-
require 'enumerator'
|
2
|
-
|
3
|
-
class Hash
|
4
|
-
def from_namespace(ns)
|
5
|
-
rx = /^#{Regexp.quote(ns.to_s)}_(.*)$/
|
6
|
-
inject({}) do |a, p|
|
7
|
-
a[$1.to_sym] = p[1] if p[0].to_s =~ rx
|
8
|
-
a
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
def from_namespace!(ns)
|
13
|
-
h = from_namespace(ns)
|
14
|
-
h.each_key {|k| delete(:"#{ns}_#{k}") }
|
15
|
-
h
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
module Peanuts
|
20
|
-
module XML
|
21
|
-
autoload :LibXML, 'peanuts/xml/libxml'
|
22
|
-
|
23
|
-
def self.default
|
24
|
-
@@default ||= LibXML
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.schema(schema_type, source, source_type = :string)
|
28
|
-
default.schema(schema_type, source, source_type)
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.method_missing(method, *args, &block)
|
32
|
-
case method.to_s
|
33
|
-
when /^(.*)_schema_from_(.*)$/
|
34
|
-
XML.schema($1.to_sym, args.first, $2.to_sym)
|
35
|
-
else
|
36
|
-
super
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
class Reader
|
41
|
-
include Enumerable
|
42
|
-
|
43
|
-
def self.new(*args, &block)
|
44
|
-
cls = self == Reader ? XML.default::Reader : self
|
45
|
-
obj = cls.allocate
|
46
|
-
obj.send(:initialize, *args, &block)
|
47
|
-
obj
|
48
|
-
end
|
49
|
-
|
50
|
-
def self.method_missing(method, *args, &block)
|
51
|
-
case method.to_s
|
52
|
-
when /^from_(.*)$/
|
53
|
-
new(args.first, $1.to_sym, &block)
|
54
|
-
else
|
55
|
-
super
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
class Writer
|
61
|
-
def self.new(*args, &block)
|
62
|
-
cls = self == Writer ? XML.default::Writer : self
|
63
|
-
obj = cls.allocate
|
64
|
-
obj.send(:initialize, *args, &block)
|
65
|
-
obj
|
66
|
-
end
|
67
|
-
|
68
|
-
def self.method_missing(method, *args, &block)
|
69
|
-
case method.to_s
|
70
|
-
when /^from_(.*)$/
|
71
|
-
new(args.first, $1.to_sym, &block)
|
72
|
-
else
|
73
|
-
super
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
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 => :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.restore_from(@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
|