happymapper 0.3.2 → 0.4.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 +1 -1
- data/Rakefile +2 -8
- data/examples/current_weather.rb +1 -1
- data/examples/multi_street_address.rb +17 -2
- data/lib/happymapper.rb +188 -5
- data/lib/happymapper/version.rb +1 -1
- data/spec/fixtures/multi_street_address.xml +1 -1
- data/spec/happymapper_attribute_spec.rb +3 -3
- data/spec/happymapper_element_spec.rb +3 -3
- data/spec/happymapper_item_spec.rb +20 -20
- data/spec/happymapper_spec.rb +9 -2
- data/spec/happymapper_to_xml_namespaces_spec.rb +149 -0
- data/spec/happymapper_to_xml_spec.rb +138 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/support/models.rb +12 -0
- metadata +20 -28
data/README.rdoc
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
== DESCRIPTION:
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
XML to object mapping library. I have included examples to help get you going. The specs should also point you in the right direction.
|
|
6
6
|
|
|
7
7
|
== FEATURES:
|
|
8
8
|
|
data/Rakefile
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require 'rubygems'
|
|
2
|
+
require 'bundler/setup'
|
|
3
|
+
|
|
2
4
|
require 'rake'
|
|
3
|
-
require 'rake/rdoctask'
|
|
4
5
|
require 'spec/rake/spectask'
|
|
5
6
|
require File.expand_path('../lib/happymapper/version', __FILE__)
|
|
6
7
|
|
|
@@ -32,10 +33,3 @@ desc 'Upload website files to rubyforge'
|
|
|
32
33
|
task :website do
|
|
33
34
|
sh %{rsync -av website/ jnunemaker@rubyforge.org:/var/www/gforge-projects/happymapper}
|
|
34
35
|
end
|
|
35
|
-
|
|
36
|
-
Rake::RDocTask.new do |r|
|
|
37
|
-
r.title = 'HappyMapper Docs'
|
|
38
|
-
r.main = 'README.rdoc'
|
|
39
|
-
r.rdoc_dir = 'doc'
|
|
40
|
-
r.rdoc_files.include("README.rdoc", "lib/**/*.rb")
|
|
41
|
-
end
|
data/examples/current_weather.rb
CHANGED
|
@@ -7,7 +7,7 @@ class CurrentWeather
|
|
|
7
7
|
include HappyMapper
|
|
8
8
|
|
|
9
9
|
tag 'ob'
|
|
10
|
-
namespace 'aws'
|
|
10
|
+
namespace 'http://www.aws.com/aws'
|
|
11
11
|
element :temperature, Integer, :tag => 'temp'
|
|
12
12
|
element :feels_like, Integer, :tag => 'feels-like'
|
|
13
13
|
element :current_condition, String, :tag => 'current-condition', :attributes => {:icon => String}
|
|
@@ -6,10 +6,25 @@ file_contents = File.read(dir + '/../spec/fixtures/multi_street_address.xml')
|
|
|
6
6
|
class MultiStreetAddress
|
|
7
7
|
include HappyMapper
|
|
8
8
|
|
|
9
|
+
tag 'address'
|
|
10
|
+
|
|
9
11
|
# allow primitive type to be collection
|
|
10
12
|
has_many :street_address, String, :tag => "streetaddress"
|
|
11
13
|
element :city, String
|
|
12
|
-
element :
|
|
14
|
+
element :state_or_province, String, :tag => "stateOrProvince"
|
|
13
15
|
element :zip, String
|
|
14
16
|
element :country, String
|
|
15
|
-
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
multi = MultiStreetAddress.parse(file_contents)
|
|
20
|
+
|
|
21
|
+
puts "Street Address:"
|
|
22
|
+
|
|
23
|
+
multi.street_address.each do |street|
|
|
24
|
+
puts street
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
puts "City: #{multi.city}"
|
|
28
|
+
puts "State/Province: #{multi.state_or_province}"
|
|
29
|
+
puts "Zip: #{multi.zip}"
|
|
30
|
+
puts "Country: #{multi.country}"
|
data/lib/happymapper.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require 'rubygems'
|
|
1
2
|
require 'date'
|
|
2
3
|
require 'time'
|
|
3
4
|
require 'xml'
|
|
@@ -11,6 +12,8 @@ module HappyMapper
|
|
|
11
12
|
def self.included(base)
|
|
12
13
|
base.instance_variable_set("@attributes", {})
|
|
13
14
|
base.instance_variable_set("@elements", {})
|
|
15
|
+
base.instance_variable_set("@registered_namespaces", {})
|
|
16
|
+
|
|
14
17
|
base.extend ClassMethods
|
|
15
18
|
end
|
|
16
19
|
|
|
@@ -65,6 +68,10 @@ module HappyMapper
|
|
|
65
68
|
@namespace = namespace if namespace
|
|
66
69
|
@namespace
|
|
67
70
|
end
|
|
71
|
+
|
|
72
|
+
def register_namespace(namespace, ns)
|
|
73
|
+
@registered_namespaces.merge!(namespace => ns)
|
|
74
|
+
end
|
|
68
75
|
|
|
69
76
|
def tag(new_tag_name)
|
|
70
77
|
@tag_name = new_tag_name.to_s
|
|
@@ -100,12 +107,12 @@ module HappyMapper
|
|
|
100
107
|
|
|
101
108
|
attributes.each do |attr|
|
|
102
109
|
obj.send("#{attr.method_name}=",
|
|
103
|
-
|
|
110
|
+
attr.from_xml_node(n, namespace))
|
|
104
111
|
end
|
|
105
112
|
|
|
106
113
|
elements.each do |elem|
|
|
107
114
|
obj.send("#{elem.method_name}=",
|
|
108
|
-
|
|
115
|
+
elem.from_xml_node(n, namespace))
|
|
109
116
|
end
|
|
110
117
|
|
|
111
118
|
obj.send("#{@content}=", n.content) if @content
|
|
@@ -124,9 +131,185 @@ module HappyMapper
|
|
|
124
131
|
collection
|
|
125
132
|
end
|
|
126
133
|
end
|
|
134
|
+
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
#
|
|
138
|
+
# Create an xml representation of the specified class based on defined
|
|
139
|
+
# HappyMapper elements and attributes. The method is defined in a way
|
|
140
|
+
# that it can be called recursively by classes that are also HappyMapper
|
|
141
|
+
# classes, allowg for the composition of classes.
|
|
142
|
+
#
|
|
143
|
+
def to_xml(parent_node = nil, default_namespace = nil)
|
|
144
|
+
|
|
145
|
+
#
|
|
146
|
+
# Create a tag that uses the tag name of the class that has no contents
|
|
147
|
+
# but has the specified namespace or uses the default namespace
|
|
148
|
+
#
|
|
149
|
+
current_node = XML::Node.new(self.class.tag_name)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
if parent_node
|
|
153
|
+
#
|
|
154
|
+
# if #to_xml has been called with a parent_node that means this method
|
|
155
|
+
# is being called recursively (or a special case) and we want to return
|
|
156
|
+
# the parent_node with the new node as a child
|
|
157
|
+
#
|
|
158
|
+
parent_node << current_node
|
|
159
|
+
else
|
|
160
|
+
#
|
|
161
|
+
# If #to_xml has been called without a Node (and namespace) that
|
|
162
|
+
# means we want to return an xml document
|
|
163
|
+
#
|
|
164
|
+
write_out_to_xml = true
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
#
|
|
168
|
+
# Add all the registered namespaces to the current node and the current node's
|
|
169
|
+
# root element. Without adding it to the root element it is not possible to
|
|
170
|
+
# parse or use xpath to find elements.
|
|
171
|
+
#
|
|
172
|
+
if self.class.instance_variable_get('@registered_namespaces')
|
|
173
|
+
|
|
174
|
+
# Given a node, continue moving up to parents until there are no more parents
|
|
175
|
+
find_root_node = lambda {|node| while node.parent? ; node = node.parent ; end ; node }
|
|
176
|
+
root_node = find_root_node.call(current_node)
|
|
177
|
+
|
|
178
|
+
# Add the registered namespace to the found root node only if it does not already have one defined
|
|
179
|
+
self.class.instance_variable_get('@registered_namespaces').each_pair do |prefix,href|
|
|
180
|
+
XML::Namespace.new(current_node,prefix,href)
|
|
181
|
+
XML::Namespace.new(root_node,prefix,href) unless root_node.namespaces.find_by_prefix(prefix)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
#
|
|
186
|
+
# Determine the tag namespace if one has been specified. This value takes
|
|
187
|
+
# precendence over one that is handed down to composed sub-classes.
|
|
188
|
+
#
|
|
189
|
+
tag_namespace = current_node.namespaces.find_by_prefix(self.class.namespace) || default_namespace
|
|
190
|
+
|
|
191
|
+
# Set the namespace of the current node to the specified namespace
|
|
192
|
+
current_node.namespaces.namespace = tag_namespace if tag_namespace
|
|
193
|
+
|
|
194
|
+
#
|
|
195
|
+
# Add all the attribute tags to the current node with their namespace, if one
|
|
196
|
+
# is defined, or the namespace handed down to the node.
|
|
197
|
+
#
|
|
198
|
+
self.class.attributes.each do |attribute|
|
|
199
|
+
attribute_namespace = current_node.namespaces.find_by_prefix(attribute.options[:namespace]) || default_namespace
|
|
200
|
+
|
|
201
|
+
value = send(attribute.method_name)
|
|
202
|
+
|
|
203
|
+
#
|
|
204
|
+
# If the attribute has a :on_save attribute defined that is a proc or
|
|
205
|
+
# a defined method, then call those with the current value.
|
|
206
|
+
#
|
|
207
|
+
if on_save_operation = attribute.options[:on_save]
|
|
208
|
+
if on_save_operation.is_a?(Proc)
|
|
209
|
+
value = on_save_operation.call(value)
|
|
210
|
+
elsif respond_to?(on_save_operation)
|
|
211
|
+
value = send(on_save_operation,value)
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
current_node[ "#{attribute_namespace ? "#{attribute_namespace.prefix}:" : ""}#{attribute.tag}" ] = value
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
#
|
|
219
|
+
# All all the elements defined (e.g. has_one, has_many, element) ...
|
|
220
|
+
#
|
|
221
|
+
self.class.elements.each do |element|
|
|
222
|
+
|
|
223
|
+
tag = element.tag || element.name
|
|
224
|
+
|
|
225
|
+
element_namespace = current_node.namespaces.find_by_prefix(element.options[:namespace]) || tag_namespace
|
|
226
|
+
|
|
227
|
+
value = send(element.name)
|
|
228
|
+
|
|
229
|
+
#
|
|
230
|
+
# If the element defines an :on_save lambda/proc then we will call that
|
|
231
|
+
# operation on the specified value. This allows for operations to be
|
|
232
|
+
# performed to convert the value to a specific value to be saved to the xml.
|
|
233
|
+
#
|
|
234
|
+
if on_save_operation = element.options[:on_save]
|
|
235
|
+
if on_save_operation.is_a?(Proc)
|
|
236
|
+
value = on_save_operation.call(value)
|
|
237
|
+
elsif respond_to?(on_save_operation)
|
|
238
|
+
value = send(on_save_operation,value)
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
#
|
|
243
|
+
# Normally a nil value would be ignored, however if specified then
|
|
244
|
+
# an empty element will be written to the xml
|
|
245
|
+
#
|
|
246
|
+
if value.nil? && element.options[:state_when_nil]
|
|
247
|
+
current_node << XML::Node.new(tag,nil,element_namespace)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
#
|
|
251
|
+
# To allow for us to treat both groups of items and singular items
|
|
252
|
+
# equally we wrap the value and treat it as an array.
|
|
253
|
+
#
|
|
254
|
+
if value.nil?
|
|
255
|
+
values = []
|
|
256
|
+
elsif value.respond_to?(:to_ary) && !element.options[:single]
|
|
257
|
+
values = value.to_ary
|
|
258
|
+
else
|
|
259
|
+
values = [value]
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
values.each do |item|
|
|
264
|
+
|
|
265
|
+
if item.is_a?(HappyMapper)
|
|
266
|
+
|
|
267
|
+
#
|
|
268
|
+
# Other HappyMapper items that are convertable should not be called
|
|
269
|
+
# with the current node and the namespace defined for the element.
|
|
270
|
+
#
|
|
271
|
+
item.to_xml(current_node,element_namespace)
|
|
272
|
+
|
|
273
|
+
elsif item
|
|
274
|
+
|
|
275
|
+
#
|
|
276
|
+
# When a value exists we should append the value for the tag
|
|
277
|
+
#
|
|
278
|
+
current_node << XML::Node.new(tag,item.to_s,element_namespace)
|
|
279
|
+
|
|
280
|
+
else
|
|
281
|
+
|
|
282
|
+
#
|
|
283
|
+
# Normally a nil value would be ignored, however if specified then
|
|
284
|
+
# an empty element will be written to the xml
|
|
285
|
+
#
|
|
286
|
+
current_node << XML.Node.new(tag,nil,element_namespace) if element.options[:state_when_nil]
|
|
287
|
+
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
#
|
|
296
|
+
# Generate xml from a document if no node was passed as a parameter. Otherwise
|
|
297
|
+
# this method is being called recursively (or special case) and we should
|
|
298
|
+
# return the node with this node attached as a child.
|
|
299
|
+
#
|
|
300
|
+
if write_out_to_xml
|
|
301
|
+
document = XML::Document.new
|
|
302
|
+
document.root = current_node
|
|
303
|
+
document.to_s
|
|
304
|
+
else
|
|
305
|
+
parent_node
|
|
306
|
+
end
|
|
307
|
+
|
|
127
308
|
end
|
|
309
|
+
|
|
310
|
+
|
|
128
311
|
end
|
|
129
312
|
|
|
130
|
-
require 'happymapper/item'
|
|
131
|
-
require 'happymapper/attribute'
|
|
132
|
-
require 'happymapper/element'
|
|
313
|
+
require File.dirname(__FILE__) + '/happymapper/item'
|
|
314
|
+
require File.dirname(__FILE__) + '/happymapper/attribute'
|
|
315
|
+
require File.dirname(__FILE__) + '/happymapper/element'
|
data/lib/happymapper/version.rb
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
require
|
|
1
|
+
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe HappyMapper::Attribute do
|
|
4
4
|
describe "initialization" do
|
|
5
5
|
before do
|
|
6
6
|
@attr = HappyMapper::Attribute.new(:foo, String)
|
|
7
7
|
end
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
it 'should know that it is an attribute' do
|
|
10
10
|
@attr.attribute?.should be_true
|
|
11
11
|
end
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
it 'should know that it is NOT an element' do
|
|
14
14
|
@attr.element?.should be_false
|
|
15
15
|
end
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
require
|
|
1
|
+
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe HappyMapper::Element do
|
|
4
4
|
describe "initialization" do
|
|
5
5
|
before do
|
|
6
6
|
@attr = HappyMapper::Element.new(:foo, String)
|
|
7
7
|
end
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
it 'should know that it is an element' do
|
|
10
10
|
@attr.element?.should be_true
|
|
11
11
|
end
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
it 'should know that it is NOT an attribute' do
|
|
14
14
|
@attr.attribute?.should be_false
|
|
15
15
|
end
|
|
@@ -1,75 +1,75 @@
|
|
|
1
|
-
require
|
|
1
|
+
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
module Foo
|
|
4
4
|
class Bar; end
|
|
5
5
|
end
|
|
6
6
|
|
|
7
7
|
describe HappyMapper::Item do
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
describe "new instance" do
|
|
10
10
|
before do
|
|
11
11
|
@item = HappyMapper::Item.new(:foo, String, :tag => 'foobar')
|
|
12
12
|
end
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
it "should accept a name" do
|
|
15
15
|
@item.name.should == 'foo'
|
|
16
16
|
end
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
it 'should accept a type' do
|
|
19
19
|
@item.type.should == String
|
|
20
20
|
end
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
it 'should accept :tag as an option' do
|
|
23
23
|
@item.tag.should == 'foobar'
|
|
24
24
|
end
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
it "should have a method_name" do
|
|
27
27
|
@item.method_name.should == 'foo'
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
describe "#constant" do
|
|
32
32
|
it "should just use type if constant" do
|
|
33
33
|
item = HappyMapper::Item.new(:foo, String)
|
|
34
34
|
item.constant.should == String
|
|
35
35
|
end
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
it "should convert string type to constant" do
|
|
38
38
|
item = HappyMapper::Item.new(:foo, 'String')
|
|
39
39
|
item.constant.should == String
|
|
40
40
|
end
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
it "should convert string with :: to constant" do
|
|
43
43
|
item = HappyMapper::Item.new(:foo, 'Foo::Bar')
|
|
44
44
|
item.constant.should == Foo::Bar
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
describe "#method_name" do
|
|
49
49
|
it "should convert dashes to underscores" do
|
|
50
50
|
item = HappyMapper::Item.new(:'foo-bar', String, :tag => 'foobar')
|
|
51
51
|
item.method_name.should == 'foo_bar'
|
|
52
52
|
end
|
|
53
53
|
end
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
describe "#xpath" do
|
|
56
56
|
it "should default to tag" do
|
|
57
57
|
item = HappyMapper::Item.new(:foo, String, :tag => 'foobar')
|
|
58
58
|
item.xpath.should == 'foobar'
|
|
59
59
|
end
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
it "should prepend with .// if options[:deep] true" do
|
|
62
62
|
item = HappyMapper::Item.new(:foo, String, :tag => 'foobar', :deep => true)
|
|
63
63
|
item.xpath.should == './/foobar'
|
|
64
64
|
end
|
|
65
|
-
|
|
65
|
+
|
|
66
66
|
it "should prepend namespace if namespace exists" do
|
|
67
67
|
item = HappyMapper::Item.new(:foo, String, :tag => 'foobar')
|
|
68
68
|
item.namespace = 'http://example.com'
|
|
69
69
|
item.xpath.should == 'happymapper:foobar'
|
|
70
70
|
end
|
|
71
71
|
end
|
|
72
|
-
|
|
72
|
+
|
|
73
73
|
describe "typecasting" do
|
|
74
74
|
it "should work with Strings" do
|
|
75
75
|
item = HappyMapper::Item.new(:foo, String)
|
|
@@ -77,36 +77,36 @@ describe HappyMapper::Item do
|
|
|
77
77
|
item.typecast(a).should == '21'
|
|
78
78
|
end
|
|
79
79
|
end
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
it "should work with Integers" do
|
|
82
82
|
item = HappyMapper::Item.new(:foo, Integer)
|
|
83
83
|
[21, 21.0, '21'].each do |a|
|
|
84
84
|
item.typecast(a).should == 21
|
|
85
85
|
end
|
|
86
86
|
end
|
|
87
|
-
|
|
87
|
+
|
|
88
88
|
it "should work with Floats" do
|
|
89
89
|
item = HappyMapper::Item.new(:foo, Float)
|
|
90
90
|
[21, 21.0, '21'].each do |a|
|
|
91
91
|
item.typecast(a).should == 21.0
|
|
92
92
|
end
|
|
93
93
|
end
|
|
94
|
-
|
|
94
|
+
|
|
95
95
|
it "should work with Times" do
|
|
96
96
|
item = HappyMapper::Item.new(:foo, Time)
|
|
97
97
|
item.typecast('2000-01-01 01:01:01.123456').should == Time.local(2000, 1, 1, 1, 1, 1, 123456)
|
|
98
98
|
end
|
|
99
|
-
|
|
99
|
+
|
|
100
100
|
it "should work with Dates" do
|
|
101
101
|
item = HappyMapper::Item.new(:foo, Date)
|
|
102
102
|
item.typecast('2000-01-01').should == Date.new(2000, 1, 1)
|
|
103
103
|
end
|
|
104
|
-
|
|
104
|
+
|
|
105
105
|
it "should work with DateTimes" do
|
|
106
106
|
item = HappyMapper::Item.new(:foo, DateTime)
|
|
107
107
|
item.typecast('2000-01-01 00:00:00').should == DateTime.new(2000, 1, 1, 0, 0, 0)
|
|
108
108
|
end
|
|
109
|
-
|
|
109
|
+
|
|
110
110
|
it "should work with Boolean" do
|
|
111
111
|
item = HappyMapper::Item.new(:foo, Boolean)
|
|
112
112
|
item.typecast('false').should == false
|
data/spec/happymapper_spec.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require
|
|
1
|
+
require 'spec_helper'
|
|
2
2
|
require 'pp'
|
|
3
3
|
require 'uri'
|
|
4
4
|
require 'support/models'
|
|
@@ -142,7 +142,7 @@ describe HappyMapper do
|
|
|
142
142
|
first.extended.should == 'ROXML is a Ruby library designed to make it easier for Ruby developers to work with XML. Using simple annotations, it enables Ruby classes to be custom-mapped to XML. ROXML takes care of the marshalling and unmarshalling of mapped attributes so that developers can focus on building first-class Ruby classes.'
|
|
143
143
|
end
|
|
144
144
|
|
|
145
|
-
it "should parse xml elements to ruby
|
|
145
|
+
it "should parse xml elements to ruby objects" do
|
|
146
146
|
statuses = Status.parse(fixture_file('statuses.xml'))
|
|
147
147
|
statuses.size.should == 20
|
|
148
148
|
first = statuses.first
|
|
@@ -173,6 +173,13 @@ describe HappyMapper do
|
|
|
173
173
|
address.country.should == 'Germany'
|
|
174
174
|
end
|
|
175
175
|
|
|
176
|
+
it "should parse xml containing a has many relationship with primitive types" do
|
|
177
|
+
address = MultiStreetAddress.parse(fixture_file('multi_street_address.xml'), :single => true)
|
|
178
|
+
address.should_not be_nil
|
|
179
|
+
address.street_address.first.should == "123 Smith Dr"
|
|
180
|
+
address.street_address.last.should == "Apt 31"
|
|
181
|
+
end
|
|
182
|
+
|
|
176
183
|
it "should parse xml with default namespace (amazon)" do
|
|
177
184
|
file_contents = fixture_file('pita.xml')
|
|
178
185
|
items = PITA::Items.parse(file_contents, :single => true)
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module ToXMLWithNamespaces
|
|
4
|
+
|
|
5
|
+
#
|
|
6
|
+
# Similar example as the to_xml but this time with namespacing
|
|
7
|
+
#
|
|
8
|
+
class Address
|
|
9
|
+
include HappyMapper
|
|
10
|
+
|
|
11
|
+
register_namespace 'address', 'http://www.company.com/address'
|
|
12
|
+
register_namespace 'country', 'http://www.company.com/country'
|
|
13
|
+
|
|
14
|
+
tag 'Address'
|
|
15
|
+
namespace 'address'
|
|
16
|
+
|
|
17
|
+
element :country, 'Country', :tag => 'country', :namespace => 'country'
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
attribute :location, String
|
|
21
|
+
|
|
22
|
+
element :street, String
|
|
23
|
+
element :postcode, String
|
|
24
|
+
element :city, String
|
|
25
|
+
|
|
26
|
+
element :housenumber, String
|
|
27
|
+
|
|
28
|
+
#
|
|
29
|
+
# to_xml will default to the attr_accessor method and not the attribute,
|
|
30
|
+
# allowing for that to be overwritten
|
|
31
|
+
#
|
|
32
|
+
def housenumber
|
|
33
|
+
"[#{@housenumber}]"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
#
|
|
37
|
+
# Write a empty element even if this is not specified
|
|
38
|
+
#
|
|
39
|
+
element :description, String, :state_when_nil => true
|
|
40
|
+
|
|
41
|
+
#
|
|
42
|
+
# Perform the on_save operation when saving
|
|
43
|
+
#
|
|
44
|
+
has_one :date_created, Time, :on_save => lambda {|time| DateTime.parse(time).strftime("%T %D") if time }
|
|
45
|
+
|
|
46
|
+
#
|
|
47
|
+
# Write multiple elements and call on_save when saving
|
|
48
|
+
#
|
|
49
|
+
has_many :dates_updated, Time, :on_save => lambda {|times|
|
|
50
|
+
times.compact.map {|time| DateTime.parse(time).strftime("%T %D") } if times }
|
|
51
|
+
|
|
52
|
+
#
|
|
53
|
+
# Class composition
|
|
54
|
+
#
|
|
55
|
+
|
|
56
|
+
def initialize(parameters)
|
|
57
|
+
parameters.each_pair do |property,value|
|
|
58
|
+
send("#{property}=",value) if respond_to?("#{property}=")
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
#
|
|
65
|
+
# Country is composed above the in Address class. Here is a demonstration
|
|
66
|
+
# of how to_xml will handle class composition as well as utilizing the tag
|
|
67
|
+
# value.
|
|
68
|
+
#
|
|
69
|
+
class Country
|
|
70
|
+
include HappyMapper
|
|
71
|
+
|
|
72
|
+
register_namespace 'countryName', 'http://www.company.com/countryName'
|
|
73
|
+
|
|
74
|
+
attribute :code, String, :tag => 'countryCode'
|
|
75
|
+
has_one :name, String, :tag => 'countryName', :namespace => 'countryName'
|
|
76
|
+
|
|
77
|
+
def initialize(parameters)
|
|
78
|
+
parameters.each_pair do |property,value|
|
|
79
|
+
send("#{property}=",value) if respond_to?("#{property}=")
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
describe "#to_xml" do
|
|
86
|
+
|
|
87
|
+
context "Address" do
|
|
88
|
+
|
|
89
|
+
before(:all) do
|
|
90
|
+
address = Address.new('street' => 'Mockingbird Lane',
|
|
91
|
+
'location' => 'Home',
|
|
92
|
+
'housenumber' => '1313',
|
|
93
|
+
'postcode' => '98103',
|
|
94
|
+
'city' => 'Seattle',
|
|
95
|
+
'country' => Country.new(:name => 'USA', :code => 'us'),
|
|
96
|
+
'date_created' => '2011-01-01 15:00:00')
|
|
97
|
+
|
|
98
|
+
address.dates_updated = ["2011-01-01 16:01:00","2011-01-02 11:30:01"]
|
|
99
|
+
|
|
100
|
+
@address_xml = XML::Parser.string(address.to_xml).parse.root
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
{ 'street' => 'Mockingbird Lane',
|
|
104
|
+
'postcode' => '98103',
|
|
105
|
+
'city' => 'Seattle' }.each_pair do |property,value|
|
|
106
|
+
|
|
107
|
+
it "should have the element '#{property}' with the value '#{value}'" do
|
|
108
|
+
@address_xml.find("address:#{property}").first.child.to_s.should == value
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it "should use the result of #housenumber method (not the @housenumber)" do
|
|
114
|
+
@address_xml.find("address:housenumber").first.child.to_s.should == "[1313]"
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it "should have the attribute 'location' with the value 'Home'" do
|
|
118
|
+
@address_xml.find('@location').first.child.to_s.should == "Home"
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it "should add an empty description element" do
|
|
122
|
+
@address_xml.find('address:description').first.child.to_s.should == ""
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it "should call #on_save when saving the time to convert the time" do
|
|
126
|
+
@address_xml.find('address:date_created').first.child.to_s.should == "15:00:00 01/01/11"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it "should handle multiple elements for 'has_many'" do
|
|
130
|
+
dates_updated = @address_xml.find('address:dates_updated')
|
|
131
|
+
dates_updated.length.should == 2
|
|
132
|
+
dates_updated.first.child.to_s.should == "16:01:00 01/01/11"
|
|
133
|
+
dates_updated.last.child.to_s.should == "11:30:01 01/02/11"
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it "should write the country code" do
|
|
137
|
+
@address_xml.find('country:country/@country:countryCode').first.child.to_s.should == "us"
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
it "should write the country name" do
|
|
141
|
+
@address_xml.find('country:country/countryName:countryName').first.child.to_s.should == "USA"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module ToXML
|
|
4
|
+
|
|
5
|
+
class Address
|
|
6
|
+
include HappyMapper
|
|
7
|
+
|
|
8
|
+
tag 'address'
|
|
9
|
+
|
|
10
|
+
attribute :location, String
|
|
11
|
+
|
|
12
|
+
element :street, String
|
|
13
|
+
element :postcode, String
|
|
14
|
+
element :city, String
|
|
15
|
+
|
|
16
|
+
element :housenumber, String
|
|
17
|
+
|
|
18
|
+
#
|
|
19
|
+
# to_xml will default to the attr_accessor method and not the attribute,
|
|
20
|
+
# allowing for that to be overwritten
|
|
21
|
+
#
|
|
22
|
+
def housenumber
|
|
23
|
+
"[#{@housenumber}]"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
#
|
|
27
|
+
# Write a empty element even if this is not specified
|
|
28
|
+
#
|
|
29
|
+
element :description, String, :state_when_nil => true
|
|
30
|
+
|
|
31
|
+
#
|
|
32
|
+
# Perform the on_save operation when saving
|
|
33
|
+
#
|
|
34
|
+
has_one :date_created, Time, :on_save => lambda {|time| DateTime.parse(time).strftime("%T %D") if time }
|
|
35
|
+
|
|
36
|
+
#
|
|
37
|
+
# Write multiple elements and call on_save when saving
|
|
38
|
+
#
|
|
39
|
+
has_many :dates_updated, Time, :on_save => lambda {|times|
|
|
40
|
+
times.compact.map {|time| DateTime.parse(time).strftime("%T %D") } if times }
|
|
41
|
+
|
|
42
|
+
#
|
|
43
|
+
# Class composition
|
|
44
|
+
#
|
|
45
|
+
element :country, 'Country', :tag => 'country'
|
|
46
|
+
|
|
47
|
+
def initialize(parameters)
|
|
48
|
+
parameters.each_pair do |property,value|
|
|
49
|
+
send("#{property}=",value) if respond_to?("#{property}=")
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
#
|
|
56
|
+
# Country is composed above the in Address class. Here is a demonstration
|
|
57
|
+
# of how to_xml will handle class composition as well as utilizing the tag
|
|
58
|
+
# value.
|
|
59
|
+
#
|
|
60
|
+
class Country
|
|
61
|
+
include HappyMapper
|
|
62
|
+
|
|
63
|
+
attribute :code, String, :tag => 'countryCode'
|
|
64
|
+
has_one :name, String, :tag => 'countryName'
|
|
65
|
+
|
|
66
|
+
def initialize(parameters)
|
|
67
|
+
parameters.each_pair do |property,value|
|
|
68
|
+
send("#{property}=",value) if respond_to?("#{property}=")
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
describe "#to_xml" do
|
|
75
|
+
|
|
76
|
+
context "Address" do
|
|
77
|
+
|
|
78
|
+
before(:all) do
|
|
79
|
+
address = Address.new('street' => 'Mockingbird Lane',
|
|
80
|
+
'location' => 'Home',
|
|
81
|
+
'housenumber' => '1313',
|
|
82
|
+
'postcode' => '98103',
|
|
83
|
+
'city' => 'Seattle',
|
|
84
|
+
'country' => Country.new(:name => 'USA', :code => 'us'),
|
|
85
|
+
'date_created' => '2011-01-01 15:00:00')
|
|
86
|
+
|
|
87
|
+
address.dates_updated = ["2011-01-01 16:01:00","2011-01-02 11:30:01"]
|
|
88
|
+
|
|
89
|
+
@address_xml = XML::Parser.string(address.to_xml).parse.root
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
{ 'street' => 'Mockingbird Lane',
|
|
93
|
+
'postcode' => '98103',
|
|
94
|
+
'city' => 'Seattle' }.each_pair do |property,value|
|
|
95
|
+
|
|
96
|
+
it "should have the element '#{property}' with the value '#{value}'" do
|
|
97
|
+
@address_xml.find("#{property}").first.child.to_s.should == value
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "should use the result of #housenumber method (not the @housenumber)" do
|
|
103
|
+
@address_xml.find("housenumber").first.child.to_s.should == "[1313]"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it "should have the attribute 'location' with the value 'Home'" do
|
|
107
|
+
@address_xml.find('@location').first.child.to_s.should == "Home"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "should add an empty description element" do
|
|
111
|
+
@address_xml.find('description').first.child.to_s.should == ""
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it "should call #on_save when saving the time to convert the time" do
|
|
115
|
+
@address_xml.find('date_created').first.child.to_s.should == "15:00:00 01/01/11"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it "should handle multiple elements for 'has_many'" do
|
|
119
|
+
dates_updated = @address_xml.find('dates_updated')
|
|
120
|
+
dates_updated.length.should == 2
|
|
121
|
+
dates_updated.first.child.to_s.should == "16:01:00 01/01/11"
|
|
122
|
+
dates_updated.last.child.to_s.should == "11:30:01 01/02/11"
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
it "should write the country code" do
|
|
126
|
+
@address_xml.find('country/@countryCode').first.child.to_s.should == "us"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it "should write the country name" do
|
|
130
|
+
@address_xml.find('country/countryName').first.child.to_s.should == "USA"
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/support/models.rb
CHANGED
|
@@ -239,6 +239,18 @@ class Address
|
|
|
239
239
|
element :country, String
|
|
240
240
|
end
|
|
241
241
|
|
|
242
|
+
class MultiStreetAddress
|
|
243
|
+
include HappyMapper
|
|
244
|
+
|
|
245
|
+
tag 'address'
|
|
246
|
+
# allow primitive type to be collection
|
|
247
|
+
has_many :street_address, String, :tag => "streetaddress"
|
|
248
|
+
element :city, String
|
|
249
|
+
element :state_or_providence, String, :tag => "stateOfProvidence"
|
|
250
|
+
element :zip, String
|
|
251
|
+
element :country, String
|
|
252
|
+
end
|
|
253
|
+
|
|
242
254
|
# for type coercion
|
|
243
255
|
class ProductGroup < String; end
|
|
244
256
|
|
metadata
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: happymapper
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
|
|
4
|
+
hash: 15
|
|
5
|
+
prerelease:
|
|
5
6
|
segments:
|
|
6
7
|
- 0
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
version: 0.
|
|
8
|
+
- 4
|
|
9
|
+
- 0
|
|
10
|
+
version: 0.4.0
|
|
10
11
|
platform: ruby
|
|
11
12
|
authors:
|
|
12
13
|
- John Nunemaker
|
|
@@ -14,37 +15,23 @@ autorequire:
|
|
|
14
15
|
bindir: bin
|
|
15
16
|
cert_chain: []
|
|
16
17
|
|
|
17
|
-
date:
|
|
18
|
-
default_executable:
|
|
18
|
+
date: 2011-09-26 00:00:00 Z
|
|
19
19
|
dependencies:
|
|
20
20
|
- !ruby/object:Gem::Dependency
|
|
21
|
-
name: libxml-ruby
|
|
22
|
-
prerelease: false
|
|
23
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
|
24
|
-
requirements:
|
|
25
|
-
- - ~>
|
|
26
|
-
- !ruby/object:Gem::Version
|
|
27
|
-
segments:
|
|
28
|
-
- 1
|
|
29
|
-
- 1
|
|
30
|
-
- 3
|
|
31
|
-
version: 1.1.3
|
|
32
21
|
type: :runtime
|
|
33
|
-
version_requirements: *id001
|
|
34
|
-
- !ruby/object:Gem::Dependency
|
|
35
|
-
name: rspec
|
|
36
22
|
prerelease: false
|
|
37
|
-
requirement: &
|
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
|
24
|
+
none: false
|
|
38
25
|
requirements:
|
|
39
26
|
- - ~>
|
|
40
27
|
- !ruby/object:Gem::Version
|
|
28
|
+
hash: 3
|
|
41
29
|
segments:
|
|
42
|
-
-
|
|
43
|
-
- 3
|
|
30
|
+
- 2
|
|
44
31
|
- 0
|
|
45
|
-
version:
|
|
46
|
-
|
|
47
|
-
|
|
32
|
+
version: "2.0"
|
|
33
|
+
version_requirements: *id001
|
|
34
|
+
name: libxml-ruby
|
|
48
35
|
description:
|
|
49
36
|
email:
|
|
50
37
|
- nunemaker@gmail.com
|
|
@@ -86,13 +73,14 @@ files:
|
|
|
86
73
|
- spec/happymapper_element_spec.rb
|
|
87
74
|
- spec/happymapper_item_spec.rb
|
|
88
75
|
- spec/happymapper_spec.rb
|
|
76
|
+
- spec/happymapper_to_xml_namespaces_spec.rb
|
|
77
|
+
- spec/happymapper_to_xml_spec.rb
|
|
89
78
|
- spec/spec.opts
|
|
90
79
|
- spec/spec_helper.rb
|
|
91
80
|
- spec/support/models.rb
|
|
92
81
|
- License
|
|
93
82
|
- Rakefile
|
|
94
83
|
- README.rdoc
|
|
95
|
-
has_rdoc: true
|
|
96
84
|
homepage: http://happymapper.rubyforge.org
|
|
97
85
|
licenses: []
|
|
98
86
|
|
|
@@ -102,23 +90,27 @@ rdoc_options: []
|
|
|
102
90
|
require_paths:
|
|
103
91
|
- lib
|
|
104
92
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
93
|
+
none: false
|
|
105
94
|
requirements:
|
|
106
95
|
- - ">="
|
|
107
96
|
- !ruby/object:Gem::Version
|
|
97
|
+
hash: 3
|
|
108
98
|
segments:
|
|
109
99
|
- 0
|
|
110
100
|
version: "0"
|
|
111
101
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
|
+
none: false
|
|
112
103
|
requirements:
|
|
113
104
|
- - ">="
|
|
114
105
|
- !ruby/object:Gem::Version
|
|
106
|
+
hash: 3
|
|
115
107
|
segments:
|
|
116
108
|
- 0
|
|
117
109
|
version: "0"
|
|
118
110
|
requirements: []
|
|
119
111
|
|
|
120
112
|
rubyforge_project: happymapper
|
|
121
|
-
rubygems_version: 1.
|
|
113
|
+
rubygems_version: 1.8.9
|
|
122
114
|
signing_key:
|
|
123
115
|
specification_version: 3
|
|
124
116
|
summary: object to xml mapping library
|