fcoury-mongomapper 0.2.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/.gitignore +7 -0
- data/History +30 -0
- data/LICENSE +20 -0
- data/README.rdoc +39 -0
- data/Rakefile +71 -0
- data/VERSION +1 -0
- data/lib/mongomapper.rb +70 -0
- data/lib/mongomapper/associations.rb +69 -0
- data/lib/mongomapper/associations/array_proxy.rb +6 -0
- data/lib/mongomapper/associations/base.rb +54 -0
- data/lib/mongomapper/associations/belongs_to_proxy.rb +26 -0
- data/lib/mongomapper/associations/has_many_embedded_proxy.rb +19 -0
- data/lib/mongomapper/associations/has_many_proxy.rb +29 -0
- data/lib/mongomapper/associations/polymorphic_belongs_to_proxy.rb +31 -0
- data/lib/mongomapper/associations/proxy.rb +66 -0
- data/lib/mongomapper/callbacks.rb +106 -0
- data/lib/mongomapper/document.rb +276 -0
- data/lib/mongomapper/document_rails_compatibility.rb +13 -0
- data/lib/mongomapper/embedded_document.rb +248 -0
- data/lib/mongomapper/embedded_document_rails_compatibility.rb +22 -0
- data/lib/mongomapper/finder_options.rb +81 -0
- data/lib/mongomapper/key.rb +82 -0
- data/lib/mongomapper/observing.rb +50 -0
- data/lib/mongomapper/save_with_validation.rb +19 -0
- data/lib/mongomapper/serialization.rb +55 -0
- data/lib/mongomapper/serializers/json_serializer.rb +77 -0
- data/lib/mongomapper/validations.rb +47 -0
- data/mongomapper.gemspec +105 -0
- data/test/serializers/test_json_serializer.rb +104 -0
- data/test/test_associations.rb +444 -0
- data/test/test_callbacks.rb +84 -0
- data/test/test_document.rb +1002 -0
- data/test/test_embedded_document.rb +253 -0
- data/test/test_finder_options.rb +148 -0
- data/test/test_helper.rb +62 -0
- data/test/test_key.rb +200 -0
- data/test/test_mongomapper.rb +28 -0
- data/test/test_observing.rb +101 -0
- data/test/test_rails_compatibility.rb +73 -0
- data/test/test_serializations.rb +54 -0
- data/test/test_validations.rb +409 -0
- metadata +155 -0
@@ -0,0 +1,253 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class EmbeddedDocumentTest < Test::Unit::TestCase
|
4
|
+
context "Including MongoMapper::EmbeddedDocument" do
|
5
|
+
setup do
|
6
|
+
@klass = Class.new do
|
7
|
+
include MongoMapper::EmbeddedDocument
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
should "clear out document default keys" do
|
12
|
+
@klass.keys.size.should == 0
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context "An instance of an embedded document" do
|
17
|
+
setup do
|
18
|
+
@document = Class.new do
|
19
|
+
include MongoMapper::EmbeddedDocument
|
20
|
+
|
21
|
+
key :name, String
|
22
|
+
key :age, Integer
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "when initialized" do
|
27
|
+
should "accept a hash that sets keys and values" do
|
28
|
+
doc = @document.new(:name => 'John', :age => 23)
|
29
|
+
doc.attributes.should == {'name' => 'John', 'age' => 23}
|
30
|
+
end
|
31
|
+
|
32
|
+
should "not throw error if initialized with nil" do
|
33
|
+
doc = @document.new(nil)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "mass assigning keys" do
|
38
|
+
should "update values for keys provided" do
|
39
|
+
doc = @document.new(:name => 'foobar', :age => 10)
|
40
|
+
doc.attributes = {:name => 'new value', :age => 5}
|
41
|
+
doc.attributes[:name].should == 'new value'
|
42
|
+
doc.attributes[:age].should == 5
|
43
|
+
end
|
44
|
+
|
45
|
+
should "not update values for keys that were not provided" do
|
46
|
+
doc = @document.new(:name => 'foobar', :age => 10)
|
47
|
+
doc.attributes = {:name => 'new value'}
|
48
|
+
doc.attributes[:name].should == 'new value'
|
49
|
+
doc.attributes[:age].should == 10
|
50
|
+
end
|
51
|
+
|
52
|
+
should "ignore keys that do not exist" do
|
53
|
+
doc = @document.new(:name => 'foobar', :age => 10)
|
54
|
+
doc.attributes = {:name => 'new value', :foobar => 'baz'}
|
55
|
+
doc.attributes[:name].should == 'new value'
|
56
|
+
doc.attributes[:foobar].should be(nil)
|
57
|
+
end
|
58
|
+
|
59
|
+
should "not ignore keys that have methods defined" do
|
60
|
+
@document.class_eval do
|
61
|
+
attr_writer :password
|
62
|
+
|
63
|
+
def passwd
|
64
|
+
@password
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
doc = @document.new(:name => 'foobar', :password => 'secret')
|
69
|
+
doc.passwd.should == 'secret'
|
70
|
+
end
|
71
|
+
|
72
|
+
should "typecast key values" do
|
73
|
+
doc = @document.new(:name => 1234, :age => '21')
|
74
|
+
doc.name.should == '1234'
|
75
|
+
doc.age.should == 21
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "requesting keys" do
|
80
|
+
should "default to empty hash" do
|
81
|
+
doc = @document.new
|
82
|
+
doc.attributes.should == {}
|
83
|
+
end
|
84
|
+
|
85
|
+
should "return all keys that aren't nil" do
|
86
|
+
doc = @document.new(:name => 'string', :age => nil)
|
87
|
+
doc.attributes.should == {'name' => 'string'}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "key shorcuts" do
|
92
|
+
should "be able to read key with []" do
|
93
|
+
doc = @document.new(:name => 'string')
|
94
|
+
doc[:name].should == 'string'
|
95
|
+
end
|
96
|
+
|
97
|
+
should "be able to write key value with []=" do
|
98
|
+
doc = @document.new
|
99
|
+
doc[:name] = 'string'
|
100
|
+
doc[:name].should == 'string'
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context "indifferent access" do
|
105
|
+
should "be enabled for keys" do
|
106
|
+
doc = @document.new(:name => 'string')
|
107
|
+
doc.attributes[:name].should == 'string'
|
108
|
+
doc.attributes['name'].should == 'string'
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context "reading an attribute" do
|
113
|
+
should "work for defined keys" do
|
114
|
+
doc = @document.new(:name => 'string')
|
115
|
+
doc.name.should == 'string'
|
116
|
+
end
|
117
|
+
|
118
|
+
should "raise no method error for undefined keys" do
|
119
|
+
doc = @document.new
|
120
|
+
lambda { doc.fart }.should raise_error(NoMethodError)
|
121
|
+
end
|
122
|
+
|
123
|
+
should "know if reader defined" do
|
124
|
+
doc = @document.new
|
125
|
+
doc.reader?('name').should be(true)
|
126
|
+
doc.reader?(:name).should be(true)
|
127
|
+
doc.reader?('age').should be(true)
|
128
|
+
doc.reader?(:age).should be(true)
|
129
|
+
doc.reader?('foobar').should be(false)
|
130
|
+
doc.reader?(:foobar).should be(false)
|
131
|
+
end
|
132
|
+
|
133
|
+
should "be accessible for use in the model" do
|
134
|
+
@document.class_eval do
|
135
|
+
def name_and_age
|
136
|
+
"#{read_attribute(:name)} (#{read_attribute(:age)})"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
doc = @document.new(:name => 'John', :age => 27)
|
141
|
+
doc.name_and_age.should == 'John (27)'
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context "reading an attribute before typcasting" do
|
146
|
+
should "work for defined keys" do
|
147
|
+
doc = @document.new(:name => 12)
|
148
|
+
doc.name_before_typecast.should == 12
|
149
|
+
end
|
150
|
+
|
151
|
+
should "raise no method error for undefined keys" do
|
152
|
+
doc = @document.new
|
153
|
+
lambda { doc.foo_before_typecast }.should raise_error(NoMethodError)
|
154
|
+
end
|
155
|
+
|
156
|
+
should "be accessible for use in a document" do
|
157
|
+
@document.class_eval do
|
158
|
+
def untypcasted_name
|
159
|
+
read_attribute_before_typecast(:name)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
doc = @document.new(:name => 12)
|
164
|
+
doc.name.should == '12'
|
165
|
+
doc.untypcasted_name.should == 12
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context "writing an attribute" do
|
170
|
+
should "work for defined keys" do
|
171
|
+
doc = @document.new
|
172
|
+
doc.name = 'John'
|
173
|
+
doc.name.should == 'John'
|
174
|
+
end
|
175
|
+
|
176
|
+
should "raise no method error for undefined keys" do
|
177
|
+
doc = @document.new
|
178
|
+
lambda { doc.fart = 'poof!' }.should raise_error(NoMethodError)
|
179
|
+
end
|
180
|
+
|
181
|
+
should "typecast value" do
|
182
|
+
doc = @document.new
|
183
|
+
doc.name = 1234
|
184
|
+
doc.name.should == '1234'
|
185
|
+
doc.age = '21'
|
186
|
+
doc.age.should == 21
|
187
|
+
end
|
188
|
+
|
189
|
+
should "know if writer defined" do
|
190
|
+
doc = @document.new
|
191
|
+
doc.writer?('name').should be(true)
|
192
|
+
doc.writer?('name=').should be(true)
|
193
|
+
doc.writer?(:name).should be(true)
|
194
|
+
doc.writer?('age').should be(true)
|
195
|
+
doc.writer?('age=').should be(true)
|
196
|
+
doc.writer?(:age).should be(true)
|
197
|
+
doc.writer?('foobar').should be(false)
|
198
|
+
doc.writer?('foobar=').should be(false)
|
199
|
+
doc.writer?(:foobar).should be(false)
|
200
|
+
end
|
201
|
+
|
202
|
+
should "be accessible for use in the model" do
|
203
|
+
@document.class_eval do
|
204
|
+
def name_and_age=(new_value)
|
205
|
+
new_value.match(/([^\(\s]+) \((.*)\)/)
|
206
|
+
write_attribute :name, $1
|
207
|
+
write_attribute :age, $2
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
doc = @document.new
|
212
|
+
doc.name_and_age = 'Frank (62)'
|
213
|
+
doc.name.should == 'Frank'
|
214
|
+
doc.age.should == 62
|
215
|
+
end
|
216
|
+
end # writing an attribute
|
217
|
+
|
218
|
+
context "respond_to?" do
|
219
|
+
setup do
|
220
|
+
@doc = @document.new
|
221
|
+
end
|
222
|
+
|
223
|
+
should "work for readers" do
|
224
|
+
@doc.respond_to?(:name).should be_true
|
225
|
+
@doc.respond_to?('name').should be_true
|
226
|
+
end
|
227
|
+
|
228
|
+
should "work for writers" do
|
229
|
+
@doc.respond_to?(:name=).should be_true
|
230
|
+
@doc.respond_to?('name=').should be_true
|
231
|
+
end
|
232
|
+
|
233
|
+
should "work for readers before typecast" do
|
234
|
+
@doc.respond_to?(:name_before_typecast).should be_true
|
235
|
+
@doc.respond_to?('name_before_typecast').should be_true
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
context "equality" do
|
240
|
+
should "be true if all keys and values are equal" do
|
241
|
+
doc1 = @document.new(:name => 'John', :age => 27)
|
242
|
+
doc2 = @document.new(:name => 'John', :age => 27)
|
243
|
+
doc1.should == doc2
|
244
|
+
end
|
245
|
+
|
246
|
+
should "be false if not all the keys and values are equal" do
|
247
|
+
doc1 = @document.new(:name => 'Steve', :age => 27)
|
248
|
+
doc2 = @document.new(:name => 'John', :age => 27)
|
249
|
+
doc1.should_not == doc2
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end # instance of a embedded document
|
253
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class FinderOptionsTest < Test::Unit::TestCase
|
4
|
+
include MongoMapper
|
5
|
+
|
6
|
+
should "raise error if provided something other than a hash" do
|
7
|
+
lambda { FinderOptions.new }.should raise_error(ArgumentError)
|
8
|
+
lambda { FinderOptions.new(1) }.should raise_error(ArgumentError)
|
9
|
+
end
|
10
|
+
|
11
|
+
should "have symbolize the keys of the hash provided" do
|
12
|
+
FinderOptions.new('offset' => 1).options.keys.map do |key|
|
13
|
+
key.should be_instance_of(Symbol)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "#criteria" do
|
18
|
+
should "convert conditions to criteria" do
|
19
|
+
FinderOptions.expects(:to_mongo_criteria).with(:foo => 1).returns({})
|
20
|
+
FinderOptions.new(:conditions => {:foo => 1}).criteria
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "#options" do
|
25
|
+
should "convert options to mongo options" do
|
26
|
+
FinderOptions.expects(:to_mongo_options).with(:order => 'foo asc', :select => 'foo,bar').returns({})
|
27
|
+
FinderOptions.new(:order => 'foo asc', :select => 'foo,bar').options
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
context "Converting conditions to criteria" do
|
33
|
+
should "work with simple criteria" do
|
34
|
+
FinderOptions.to_mongo_criteria(:foo => 'bar').should == {
|
35
|
+
:foo => 'bar'
|
36
|
+
}
|
37
|
+
|
38
|
+
FinderOptions.to_mongo_criteria(:foo => 'bar', :baz => 'wick').should == {
|
39
|
+
:foo => 'bar',
|
40
|
+
:baz => 'wick'
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
should "use $in for arrays" do
|
45
|
+
FinderOptions.to_mongo_criteria(:foo => [1,2,3]).should == {
|
46
|
+
:foo => {'$in' => [1,2,3]}
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
should "work arbitrarily deep" do
|
51
|
+
FinderOptions.to_mongo_criteria(:foo => {:bar => [1,2,3]}).should == {
|
52
|
+
:foo => {:bar => {'$in' => [1,2,3]}}
|
53
|
+
}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "ordering" do
|
58
|
+
should "single field with ascending direction" do
|
59
|
+
hash = OrderedHash.new
|
60
|
+
hash[:foo] = 1
|
61
|
+
FinderOptions.to_mongo_options(:order => 'foo asc')[:sort].should == hash
|
62
|
+
FinderOptions.to_mongo_options(:order => 'foo ASC')[:sort].should == hash
|
63
|
+
end
|
64
|
+
|
65
|
+
should "single field with descending direction" do
|
66
|
+
hash = OrderedHash.new
|
67
|
+
hash[:foo] = -1
|
68
|
+
FinderOptions.to_mongo_options(:order => 'foo desc')[:sort].should == hash
|
69
|
+
FinderOptions.to_mongo_options(:order => 'foo DESC')[:sort].should == hash
|
70
|
+
end
|
71
|
+
|
72
|
+
should "convert field without direction to ascending" do
|
73
|
+
hash = OrderedHash.new
|
74
|
+
hash[:foo] = 1
|
75
|
+
FinderOptions.to_mongo_options(:order => 'foo')[:sort].should == hash
|
76
|
+
end
|
77
|
+
|
78
|
+
should "convert multiple fields with directions" do
|
79
|
+
hash = OrderedHash.new
|
80
|
+
hash[:foo] = -1
|
81
|
+
hash[:bar] = 1
|
82
|
+
hash[:baz] = -1
|
83
|
+
FinderOptions.to_mongo_options(:order => 'foo desc, bar asc, baz desc')[:sort].should == hash
|
84
|
+
end
|
85
|
+
|
86
|
+
should "convert multiple fields with some missing directions" do
|
87
|
+
hash = OrderedHash.new
|
88
|
+
hash[:foo] = -1
|
89
|
+
hash[:bar] = 1
|
90
|
+
hash[:baz] = 1
|
91
|
+
FinderOptions.to_mongo_options(:order => 'foo desc, bar, baz')[:sort].should == hash
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context "offset" do
|
96
|
+
should "default to 0" do
|
97
|
+
FinderOptions.to_mongo_options({})[:offset].should == 0
|
98
|
+
end
|
99
|
+
|
100
|
+
should "use offset provided" do
|
101
|
+
FinderOptions.to_mongo_options(:offset => 2)[:offset].should == 2
|
102
|
+
end
|
103
|
+
|
104
|
+
should "covert string to integer" do
|
105
|
+
FinderOptions.to_mongo_options(:offset => '2')[:offset].should == 2
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context "limit" do
|
110
|
+
should "default to 0" do
|
111
|
+
FinderOptions.to_mongo_options({})[:limit].should == 0
|
112
|
+
end
|
113
|
+
|
114
|
+
should "use offset provided" do
|
115
|
+
FinderOptions.to_mongo_options(:limit => 2)[:limit].should == 2
|
116
|
+
end
|
117
|
+
|
118
|
+
should "covert string to integer" do
|
119
|
+
FinderOptions.to_mongo_options(:limit => '2')[:limit].should == 2
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context "fields" do
|
124
|
+
should "default to nil" do
|
125
|
+
FinderOptions.to_mongo_options({})[:fields].should be(nil)
|
126
|
+
end
|
127
|
+
|
128
|
+
should "be converted to nil if empty string" do
|
129
|
+
FinderOptions.to_mongo_options(:fields => '')[:fields].should be(nil)
|
130
|
+
end
|
131
|
+
|
132
|
+
should "be converted to nil if []" do
|
133
|
+
FinderOptions.to_mongo_options(:fields => [])[:fields].should be(nil)
|
134
|
+
end
|
135
|
+
|
136
|
+
should "should work with array" do
|
137
|
+
FinderOptions.to_mongo_options({:fields => %w(a b)})[:fields].should == %w(a b)
|
138
|
+
end
|
139
|
+
|
140
|
+
should "convert comma separated list to array" do
|
141
|
+
FinderOptions.to_mongo_options({:fields => 'a, b'})[:fields].should == %w(a b)
|
142
|
+
end
|
143
|
+
|
144
|
+
should "also work as select" do
|
145
|
+
FinderOptions.new(:select => %w(a b)).options[:fields].should == %w(a b)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end # FinderOptionsTest
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'test/unit'
|
4
|
+
require 'shoulda'
|
5
|
+
|
6
|
+
gem 'mocha', '~> 0.9.4'
|
7
|
+
gem 'jnunemaker-matchy', '0.4.0'
|
8
|
+
|
9
|
+
require 'matchy'
|
10
|
+
require 'mocha'
|
11
|
+
|
12
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
13
|
+
dir = (Pathname(__FILE__).dirname + '..' + 'lib').expand_path
|
14
|
+
require dir + 'mongomapper'
|
15
|
+
|
16
|
+
class Test::Unit::TestCase
|
17
|
+
custom_matcher :be_nil do |receiver, matcher, args|
|
18
|
+
matcher.positive_failure_message = "Expected #{receiver} to be nil but it wasn't"
|
19
|
+
matcher.negative_failure_message = "Expected #{receiver} not to be nil but it was"
|
20
|
+
receiver.nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
custom_matcher :be_true do |receiver, matcher, args|
|
24
|
+
matcher.positive_failure_message = "Expected #{receiver} to be true but it wasn't"
|
25
|
+
matcher.negative_failure_message = "Expected #{receiver} not to be true but it was"
|
26
|
+
receiver.eql?(true)
|
27
|
+
end
|
28
|
+
|
29
|
+
custom_matcher :be_false do |receiver, matcher, args|
|
30
|
+
matcher.positive_failure_message = "Expected #{receiver} to be false but it wasn't"
|
31
|
+
matcher.negative_failure_message = "Expected #{receiver} not to be false but it was"
|
32
|
+
receiver.eql?(false)
|
33
|
+
end
|
34
|
+
|
35
|
+
custom_matcher :be_valid do |receiver, matcher, args|
|
36
|
+
matcher.positive_failure_message = "Expected to be valid but it was invalid #{receiver.errors.inspect}"
|
37
|
+
matcher.negative_failure_message = "Expected to be invalid but it was valid #{receiver.errors.inspect}"
|
38
|
+
receiver.valid?
|
39
|
+
end
|
40
|
+
|
41
|
+
custom_matcher :have_error_on do |receiver, matcher, args|
|
42
|
+
receiver.valid?
|
43
|
+
attribute = args[0]
|
44
|
+
expected_message = args[1]
|
45
|
+
|
46
|
+
if expected_message.nil?
|
47
|
+
matcher.positive_failure_message = "#{receiver} had no errors on #{attribute}"
|
48
|
+
matcher.negative_failure_message = "#{receiver} had errors on #{attribute} #{receiver.errors.inspect}"
|
49
|
+
!receiver.errors.on(attribute).blank?
|
50
|
+
else
|
51
|
+
actual = receiver.errors.on(attribute)
|
52
|
+
matcher.positive_failure_message = %Q(Expected error on #{attribute} to be "#{expected_message}" but was "#{actual}")
|
53
|
+
matcher.negative_failure_message = %Q(Expected error on #{attribute} not to be "#{expected_message}" but was "#{actual}")
|
54
|
+
actual == expected_message
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
DefaultDatabase = 'test' unless defined?(DefaultDatabase)
|
60
|
+
AlternateDatabase = 'test2' unless defined?(AlternateDatabase)
|
61
|
+
|
62
|
+
MongoMapper.database = DefaultDatabase
|