ardm-serializer 1.2.2

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.
@@ -0,0 +1,5 @@
1
+ module DataMapper
2
+ module Serializer
3
+ VERSION = '1.2.2'
4
+ end
5
+ end
@@ -0,0 +1,56 @@
1
+ module DataMapper
2
+ module Serializer
3
+ module XML
4
+ # The supported XML Serializers
5
+ SERIALIZERS = {
6
+ :libxml => 'LibXML',
7
+ :nokogiri => 'Nokogiri',
8
+ :rexml => 'REXML'
9
+ }
10
+
11
+ #
12
+ # The current XML Serializer.
13
+ #
14
+ # @return [Module]
15
+ # The module within {DataMapper::Serialize::XML}.
16
+ #
17
+ # @since 1.1.0
18
+ #
19
+ def self.serializer
20
+ @serializer
21
+ end
22
+
23
+ #
24
+ # Sets the XML Serializer to use.
25
+ #
26
+ # @param [Symbol] name
27
+ # The name of the serializer to use. Must be either `:libxml`,
28
+ # `:nokogiri` or `:rexml`.
29
+ #
30
+ # @return [Module]
31
+ # The module within {DataMapper::Serialize::XML}.
32
+ #
33
+ # @since 1.1.0
34
+ #
35
+ def self.serializer=(name)
36
+ serializer_const = SERIALIZERS[name]
37
+
38
+ unless serializer_const
39
+ raise(ArgumentError,"unsupported XML Serializer #{name}")
40
+ end
41
+
42
+ require "dm-serializer/xml/#{name}"
43
+ @serializer = const_get(serializer_const)
44
+ end
45
+
46
+ [:nokogiri, :libxml, :rexml].each do |name|
47
+ # attempt to load the first available XML Serializer
48
+ begin
49
+ self.serializer = name
50
+ break
51
+ rescue LoadError
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,44 @@
1
+ require 'libxml'
2
+
3
+ module DataMapper
4
+ module Serializer
5
+ module XML
6
+ module LibXML
7
+ def self.new_document
8
+ ::LibXML::XML::Document.new
9
+ end
10
+
11
+ def self.root_node(doc, name, attrs = {})
12
+ root = ::LibXML::XML::Node.new(name)
13
+
14
+ attrs.each do |attr_name, attr_val|
15
+ root[attr_name] = attr_val
16
+ end
17
+
18
+ doc.root.nil? ? doc.root = root : doc.root << root
19
+ root
20
+ end
21
+
22
+ def self.add_node(parent, name, value, attrs = {})
23
+ value_str = value.to_s unless value.nil?
24
+ node = ::LibXML::XML::Node.new(name, value_str)
25
+
26
+ attrs.each do |attr_name, attr_val|
27
+ node[attr_name] = attr_val
28
+ end
29
+
30
+ parent << node
31
+ node
32
+ end
33
+
34
+ def self.add_xml(parent, xml)
35
+ parent << xml.root.copy(true)
36
+ end
37
+
38
+ def self.output(doc)
39
+ doc.root.to_s
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,44 @@
1
+ require 'nokogiri'
2
+
3
+ module DataMapper
4
+ module Serializer
5
+ module XML
6
+ module Nokogiri
7
+ def self.new_document
8
+ ::Nokogiri::XML::Document.new
9
+ end
10
+
11
+ def self.root_node(doc, name, attrs = {})
12
+ root = ::Nokogiri::XML::Node.new(name, doc)
13
+
14
+ attrs.each do |attr_name, attr_val|
15
+ root[attr_name] = attr_val
16
+ end
17
+
18
+ doc.root.nil? ? doc.root = root : doc.root << root
19
+ root
20
+ end
21
+
22
+ def self.add_node(parent, name, value, attrs = {})
23
+ node = ::Nokogiri::XML::Node.new(name, parent.document)
24
+ node << ::Nokogiri::XML::Text.new(value.to_s, parent.document) unless value.nil?
25
+
26
+ attrs.each do |attr_name, attr_val|
27
+ node[attr_name] = attr_val
28
+ end
29
+
30
+ parent << node
31
+ node
32
+ end
33
+
34
+ def self.add_xml(parent, xml)
35
+ parent << xml.root
36
+ end
37
+
38
+ def self.output(doc)
39
+ doc.root.to_s
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,36 @@
1
+ require 'rexml/document'
2
+
3
+ module DataMapper
4
+ module Serializer
5
+ module XML
6
+ module REXML
7
+ def self.new_document
8
+ ::REXML::Document.new
9
+ end
10
+
11
+ def self.root_node(document, name, attrs = {})
12
+ add_node(document.root || document, name, nil, attrs)
13
+ end
14
+
15
+ def self.add_node(parent, name, value, attrs = {})
16
+ node = parent.add_element(name)
17
+
18
+ attrs.each do |attr_name, attr_val|
19
+ node.attributes[attr_name] = attr_val
20
+ end
21
+
22
+ node << ::REXML::Text.new(value.to_s) unless value.nil?
23
+ node
24
+ end
25
+
26
+ def self.add_xml(parent, xml)
27
+ parent.add(xml)
28
+ end
29
+
30
+ def self.output(doc)
31
+ doc.to_s
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,11 @@
1
+ class Cow
2
+ include DataMapper::Resource
3
+
4
+ property :id, Integer, :key => true
5
+ property :composite, Integer, :key => true
6
+ property :name, String
7
+ property :breed, String
8
+
9
+ belongs_to :mother_cow, :model => self, :required => false
10
+ has n, :baby_cows, :model => self, :child_key => [ :mother_cow_id, :mother_cow_composite ]
11
+ end
@@ -0,0 +1,46 @@
1
+ class Planet
2
+ include DataMapper::Resource
3
+
4
+ property :name, String, :key => true
5
+ property :aphelion, Float
6
+
7
+ validates_length_of :name, :min => 2
8
+
9
+ # Sorry these associations don't make any sense
10
+ # I just needed a many-to-many association to test against
11
+ has n, :friended_planets
12
+ has n, :friend_planets, :through => :friended_planets, :model => 'Planet'
13
+
14
+ belongs_to :solar_system
15
+
16
+ def category
17
+ case self.name.downcase
18
+ when "mercury", "venus", "earth", "mars" then "terrestrial"
19
+ when "jupiter", "saturn", "uranus", "neptune" then "gas giants"
20
+ when "pluto" then "dwarf planets"
21
+ end
22
+ end
23
+
24
+ def has_known_form_of_life?
25
+ self.name.downcase == "earth"
26
+ end
27
+ end
28
+
29
+ class FriendedPlanet
30
+ include DataMapper::Resource
31
+
32
+ property :planet_name, String, :key => true
33
+ property :friend_planet_name, String, :key => true
34
+
35
+ belongs_to :planet, :child_key => [ :planet_name ]
36
+ belongs_to :friend_planet, :model => 'Planet', :child_key => [ :friend_planet_name ]
37
+ end
38
+
39
+ class SolarSystem
40
+ include DataMapper::Resource
41
+
42
+ property :id, Serial
43
+
44
+ property :name, String
45
+
46
+ end
@@ -0,0 +1,15 @@
1
+ # Yes, this crazy capitalization is intentional,
2
+ # to test xml root element name generation
3
+ module QuanTum
4
+ class Cat
5
+ include DataMapper::Resource
6
+
7
+ property :id, Serial
8
+ property :name, String
9
+ property :location, String
10
+
11
+ repository(:alternate) do
12
+ property :is_dead, Boolean
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ class Vehicle
2
+ include DataMapper::Resource
3
+ property :id, Serial
4
+ property :name, String
5
+ property :type, Discriminator
6
+ end
7
+
8
+ class Car < Vehicle
9
+
10
+ end
11
+
12
+ class Motorcycle < Vehicle
13
+
14
+ end
@@ -0,0 +1,290 @@
1
+ share_examples_for 'A serialization method that also serializes core classes' do
2
+ # This spec ensures that we don't break any serialization methods attached
3
+ # to core classes, such as Array
4
+ before(:all) do
5
+ %w[ @harness ].each do |ivar|
6
+ raise "+#{ivar}+ should be defined in before block" unless instance_variable_get(ivar)
7
+ end
8
+
9
+ DataMapper.auto_migrate!
10
+ end
11
+
12
+ before(:each) do
13
+ DataMapper::Model.descendants.each { |model| model.destroy! }
14
+ end
15
+
16
+ it 'serializes an array of extended objects' do
17
+ Cow.create(
18
+ :id => 89,
19
+ :composite => 34,
20
+ :name => 'Berta',
21
+ :breed => 'Guernsey'
22
+ )
23
+ result = @harness.test(Cow.all.to_a)
24
+ result[0].values_at("id", "composite", "name", "breed").should ==
25
+ [89, 34, "Berta", "Guernsey"]
26
+ end
27
+
28
+ it 'serializes an array of collections' do
29
+ query = DataMapper::Query.new(DataMapper::repository(:default), Cow)
30
+
31
+ keys = %w[ id composite name breed ]
32
+
33
+ resources = [
34
+ Hash[ keys.zip([ 1, 2, 'Betsy', 'Jersey' ]) ],
35
+ Hash[ keys.zip([ 89, 34, 'Berta', 'Guernsey' ]) ],
36
+ ]
37
+
38
+ collection = DataMapper::Collection.new(query, query.model.load(resources, query))
39
+
40
+ result = @harness.test(collection)
41
+ result[0].values_at(*keys).should == resources[0].values_at(*keys)
42
+ result[1].values_at(*keys).should == resources[1].values_at(*keys)
43
+ end
44
+ end
45
+
46
+ share_examples_for 'A serialization method' do
47
+ before(:all) do
48
+ %w[ @harness ].each do |ivar|
49
+ raise "+#{ivar}+ should be defined in before block" unless instance_variable_get(ivar)
50
+ end
51
+
52
+ DataMapper.auto_migrate!
53
+ end
54
+
55
+ before(:each) do
56
+ DataMapper::Model.descendants.each { |model| model.destroy! }
57
+ end
58
+
59
+ describe '(serializing single resources)' do
60
+ it 'should serialize Model.first' do
61
+ # At the moment this is implied by serializing a resource, but this
62
+ # test ensures the contract even if dm-core changes
63
+ Cow.create(
64
+ :id => 89,
65
+ :composite => 34,
66
+ :name => 'Berta',
67
+ :breed => 'Guernsey'
68
+ )
69
+ result = @harness.test(Cow.first)
70
+ result.values_at("name", "breed").should == ["Berta", "Guernsey"]
71
+ end
72
+
73
+ it 'should serialize a resource' do
74
+ cow = Cow.new(
75
+ :id => 89,
76
+ :composite => 34,
77
+ :name => 'Berta',
78
+ :breed => 'Guernsey'
79
+ )
80
+
81
+ result = @harness.test(cow)
82
+ result.values_at("id", "composite", "name", "breed").should == [89, 34, 'Berta', 'Guernsey']
83
+ end
84
+
85
+ it 'should exclude nil properties' do
86
+ cow = Cow.new(
87
+ :id => 89,
88
+ :name => nil
89
+ )
90
+
91
+ result = @harness.test(cow)
92
+ result.values_at("id", "composite").should == [89, nil]
93
+ end
94
+
95
+ it "should only includes properties given to :only option" do
96
+ pending_if 'Psych provides no way to pass in parameters', @ruby_192 && @to_yaml do
97
+ planet = Planet.new(
98
+ :name => "Mars",
99
+ :aphelion => 249_209_300.4
100
+ )
101
+
102
+ result = @harness.test(planet, :only => [:name])
103
+ result.values_at("name", "aphelion").should == ["Mars", nil]
104
+ end
105
+ end
106
+
107
+ it "should serialize values returned by an array of methods given to :methods option" do
108
+ pending_if 'Psych provides no way to pass in parameters', @ruby_192 && @to_yaml do
109
+ planet = Planet.new(
110
+ :name => "Mars",
111
+ :aphelion => 249_209_300.4
112
+ )
113
+
114
+ result = @harness.test(planet, :methods => [:category, :has_known_form_of_life?])
115
+ # XML currently can't serialize ? at the end of method names
116
+ boolean_method_name = @harness.method_name == :to_xml ? "has_known_form_of_life" : "has_known_form_of_life?"
117
+ result.values_at("category", boolean_method_name).should == ["terrestrial", false]
118
+ end
119
+ end
120
+
121
+ it "should serialize values returned by a single method given to :methods option" do
122
+ pending_if 'Psych provides no way to pass in parameters', @ruby_192 && @to_yaml do
123
+ planet = Planet.new(
124
+ :name => "Mars",
125
+ :aphelion => 249_209_300.4
126
+ )
127
+
128
+ result = @harness.test(planet, :methods => :category)
129
+ result.values_at("category").should == ["terrestrial"]
130
+ end
131
+ end
132
+
133
+ it "should only include properties given to :only option" do
134
+ pending_if 'Psych provides no way to pass in parameters', @ruby_192 && @to_yaml do
135
+ planet = Planet.new(
136
+ :name => "Mars",
137
+ :aphelion => 249_209_300.4
138
+ )
139
+
140
+ result = @harness.test(planet, :only => [:name])
141
+ result.values_at("name", "aphelion").should == ["Mars", nil]
142
+ end
143
+ end
144
+
145
+ it "should exclude properties given to :exclude option" do
146
+ pending_if 'Psych provides no way to pass in parameters', @ruby_192 && @to_yaml do
147
+ planet = Planet.new(
148
+ :name => "Mars",
149
+ :aphelion => 249_209_300.4
150
+ )
151
+
152
+ result = @harness.test(planet, :exclude => [:aphelion])
153
+ result.values_at("name", "aphelion").should == ["Mars", nil]
154
+ end
155
+ end
156
+
157
+ it "should give higher precendence to :only option over :exclude" do
158
+ pending_if 'Psych provides no way to pass in parameters', @ruby_192 && @to_yaml do
159
+ planet = Planet.new(
160
+ :name => "Mars",
161
+ :aphelion => 249_209_300.4
162
+ )
163
+
164
+ result = @harness.test(planet, :only => [:name], :exclude => [:name])
165
+ result.values_at("name", "aphelion").should == ["Mars", nil]
166
+ end
167
+ end
168
+
169
+ it 'should support child associations included via the :methods parameter' do
170
+ pending_if 'Psych provides no way to pass in parameters', @ruby_192 && @to_yaml do
171
+ solar_system = SolarSystem.create(:name => "one")
172
+ planet = Planet.new(:name => "earth")
173
+ planet.solar_system = solar_system
174
+ result = @harness.test(planet, :methods => [:solar_system])
175
+ result['solar_system'].values_at('name', 'id').should == ['one', 1]
176
+ end
177
+ end
178
+ end
179
+
180
+ describe "(collections and proxies)" do
181
+ it 'should serialize Model.all' do
182
+ # At the moment this is implied by serializing a collection, but this
183
+ # test ensures the contract even if dm-core changes
184
+ Cow.create(
185
+ :id => 89,
186
+ :composite => 34,
187
+ :name => 'Berta',
188
+ :breed => 'Guernsey'
189
+ )
190
+ result = @harness.test(Cow.all)
191
+ result[0].values_at("name", "breed").should == ["Berta", "Guernsey"]
192
+ end
193
+
194
+ it 'should serialize a collection' do
195
+ query = DataMapper::Query.new(DataMapper::repository(:default), Cow)
196
+
197
+ keys = %w[ id composite name breed ]
198
+
199
+ resources = [
200
+ Hash[ keys.zip([ 1, 2, 'Betsy', 'Jersey' ]) ],
201
+ Hash[ keys.zip([ 10, 20, 'Berta', 'Guernsey' ]) ],
202
+ ]
203
+
204
+ collection = DataMapper::Collection.new(query, query.model.load(resources, query))
205
+
206
+ result = @harness.test(collection)
207
+ result[0].values_at(*keys).should == resources[0].values_at(*keys)
208
+ result[1].values_at(*keys).should == resources[1].values_at(*keys)
209
+ end
210
+
211
+ it 'should serialize an empty collection' do
212
+ query = DataMapper::Query.new(DataMapper::repository(:default), Cow)
213
+ collection = DataMapper::Collection.new(query)
214
+
215
+ result = @harness.test(collection)
216
+ result.should be_empty
217
+ end
218
+
219
+ it "serializes a one to many relationship" do
220
+ parent = Cow.new(:id => 1, :composite => 322, :name => "Harry", :breed => "Angus")
221
+ baby = Cow.new(:mother_cow => parent, :id => 2, :composite => 321, :name => "Felix", :breed => "Angus")
222
+
223
+ parent.save
224
+ baby.save
225
+
226
+ result = @harness.test(parent.baby_cows)
227
+ result.should be_kind_of(Array)
228
+
229
+ result[0].values_at(*%w{id composite name breed}).should == [2, 321, "Felix", "Angus"]
230
+ end
231
+
232
+ it "serializes a many to one relationship" do
233
+ parent = Cow.new(:id => 1, :composite => 322, :name => "Harry", :breed => "Angus")
234
+ baby = Cow.new(:mother_cow => parent, :id => 2, :composite => 321, :name => "Felix", :breed => "Angus")
235
+
236
+ parent.save
237
+ baby.save
238
+
239
+ result = @harness.test(baby.mother_cow)
240
+ result.should be_kind_of(Hash)
241
+ result.values_at(*%w{id composite name breed}).should == [1, 322, "Harry", "Angus"]
242
+ end
243
+
244
+ it "serializes a many to many relationship" do
245
+ pending 'TODO: fix many to many in dm-core' do
246
+ p1 = Planet.create(:name => 'earth')
247
+ p2 = Planet.create(:name => 'mars')
248
+
249
+ FriendedPlanet.create(:planet => p1, :friend_planet => p2)
250
+
251
+ result = @harness.test(p1.reload.friend_planets)
252
+ result.should be_kind_of(Array)
253
+
254
+ result[0]["name"].should == "mars"
255
+ end
256
+ end
257
+ end
258
+
259
+ with_alternate_adapter do
260
+
261
+ describe "(multiple repositories)" do
262
+ before(:all) do
263
+ [ :default, :alternate ].each do |repository_name|
264
+ DataMapper.repository(repository_name) do
265
+ QuanTum::Cat.auto_migrate!
266
+ QuanTum::Cat.destroy!
267
+ end
268
+ end
269
+ end
270
+
271
+ it "should use the repsoitory for the model" do
272
+ alternate_repo = DataMapper::Spec.spec_adapters[:alternate].name
273
+ gerry = QuanTum::Cat.create(:name => "gerry")
274
+ george = DataMapper.repository(alternate_repo){ QuanTum::Cat.create(:name => "george", :is_dead => false) }
275
+ @harness.test(gerry )['is_dead'].should be(nil)
276
+ @harness.test(george)['is_dead'].should be(false)
277
+ end
278
+ end
279
+
280
+ end
281
+
282
+ it 'should integrate with dm-validations' do
283
+ planet = Planet.create(:name => 'a')
284
+ results = @harness.test(planet.errors)
285
+ results.should == {
286
+ "name" => planet.errors[:name].map { |e| e.to_s },
287
+ "solar_system_id" => planet.errors[:solar_system_id].map { |e| e.to_s }
288
+ }
289
+ end
290
+ end