danielharan-mongo_mapper 0.6.5

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.
Files changed (74) hide show
  1. data/.gitignore +10 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +53 -0
  4. data/Rakefile +55 -0
  5. data/VERSION +1 -0
  6. data/bin/mmconsole +60 -0
  7. data/lib/mongo_mapper.rb +134 -0
  8. data/lib/mongo_mapper/associations.rb +183 -0
  9. data/lib/mongo_mapper/associations/base.rb +110 -0
  10. data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +34 -0
  11. data/lib/mongo_mapper/associations/belongs_to_proxy.rb +22 -0
  12. data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +25 -0
  13. data/lib/mongo_mapper/associations/many_documents_proxy.rb +127 -0
  14. data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +33 -0
  15. data/lib/mongo_mapper/associations/many_embedded_proxy.rb +53 -0
  16. data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +11 -0
  17. data/lib/mongo_mapper/associations/many_proxy.rb +6 -0
  18. data/lib/mongo_mapper/associations/proxy.rb +80 -0
  19. data/lib/mongo_mapper/callbacks.rb +109 -0
  20. data/lib/mongo_mapper/dirty.rb +136 -0
  21. data/lib/mongo_mapper/document.rb +481 -0
  22. data/lib/mongo_mapper/dynamic_finder.rb +35 -0
  23. data/lib/mongo_mapper/embedded_document.rb +386 -0
  24. data/lib/mongo_mapper/finder_options.rb +133 -0
  25. data/lib/mongo_mapper/key.rb +36 -0
  26. data/lib/mongo_mapper/observing.rb +50 -0
  27. data/lib/mongo_mapper/pagination.rb +53 -0
  28. data/lib/mongo_mapper/rails_compatibility/document.rb +15 -0
  29. data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +27 -0
  30. data/lib/mongo_mapper/serialization.rb +54 -0
  31. data/lib/mongo_mapper/serializers/json_serializer.rb +92 -0
  32. data/lib/mongo_mapper/support.rb +193 -0
  33. data/lib/mongo_mapper/validations.rb +41 -0
  34. data/mongo_mapper.gemspec +171 -0
  35. data/specs.watchr +32 -0
  36. data/test/NOTE_ON_TESTING +1 -0
  37. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +55 -0
  38. data/test/functional/associations/test_belongs_to_proxy.rb +48 -0
  39. data/test/functional/associations/test_many_documents_as_proxy.rb +246 -0
  40. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +156 -0
  41. data/test/functional/associations/test_many_embedded_proxy.rb +196 -0
  42. data/test/functional/associations/test_many_polymorphic_proxy.rb +339 -0
  43. data/test/functional/associations/test_many_proxy.rb +384 -0
  44. data/test/functional/test_associations.rb +44 -0
  45. data/test/functional/test_binary.rb +18 -0
  46. data/test/functional/test_callbacks.rb +85 -0
  47. data/test/functional/test_dirty.rb +159 -0
  48. data/test/functional/test_document.rb +1180 -0
  49. data/test/functional/test_embedded_document.rb +125 -0
  50. data/test/functional/test_logger.rb +20 -0
  51. data/test/functional/test_pagination.rb +95 -0
  52. data/test/functional/test_rails_compatibility.rb +25 -0
  53. data/test/functional/test_string_id_compatibility.rb +72 -0
  54. data/test/functional/test_validations.rb +369 -0
  55. data/test/models.rb +271 -0
  56. data/test/support/custom_matchers.rb +55 -0
  57. data/test/support/timing.rb +16 -0
  58. data/test/test_helper.rb +27 -0
  59. data/test/unit/serializers/test_json_serializer.rb +189 -0
  60. data/test/unit/test_association_base.rb +166 -0
  61. data/test/unit/test_document.rb +204 -0
  62. data/test/unit/test_dynamic_finder.rb +125 -0
  63. data/test/unit/test_embedded_document.rb +718 -0
  64. data/test/unit/test_finder_options.rb +296 -0
  65. data/test/unit/test_key.rb +172 -0
  66. data/test/unit/test_mongo_mapper.rb +65 -0
  67. data/test/unit/test_observing.rb +101 -0
  68. data/test/unit/test_pagination.rb +113 -0
  69. data/test/unit/test_rails_compatibility.rb +49 -0
  70. data/test/unit/test_serializations.rb +52 -0
  71. data/test/unit/test_support.rb +342 -0
  72. data/test/unit/test_time_zones.rb +40 -0
  73. data/test/unit/test_validations.rb +503 -0
  74. metadata +233 -0
@@ -0,0 +1,296 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class FinderOptionsTest < Test::Unit::TestCase
5
+ include MongoMapper
6
+
7
+ should "raise error if provided something other than a hash" do
8
+ lambda { FinderOptions.new(Room) }.should raise_error(ArgumentError)
9
+ lambda { FinderOptions.new(Room, 1) }.should raise_error(ArgumentError)
10
+ end
11
+
12
+ should "symbolize the keys of the hash provided" do
13
+ FinderOptions.new(Room, 'offset' => 1).options.keys.map do |key|
14
+ key.should be_instance_of(Symbol)
15
+ end
16
+ end
17
+
18
+ context "Converting conditions to criteria" do
19
+ should "not add _type to query if model does not have superclass that is single collection inherited" do
20
+ FinderOptions.new(Message, :foo => 'bar').criteria.should == {
21
+ :foo => 'bar'
22
+ }
23
+ end
24
+
25
+ should "not add _type to nested conditions" do
26
+ FinderOptions.new(Enter, :foo => 'bar', :age => {'$gt' => 21}).criteria.should == {
27
+ :foo => 'bar',
28
+ :age => {'$gt' => 21},
29
+ :_type => 'Enter'
30
+ }
31
+ end
32
+
33
+ %w{gt lt gte lte ne in nin mod size where exists}.each do |operator|
34
+ should "convert #{operator} conditions" do
35
+ FinderOptions.new(Room, :age.send(operator) => 21).criteria.should == {
36
+ :age => {"$#{operator}" => 21}
37
+ }
38
+ end
39
+ end
40
+
41
+ should "automatically add _type to query if model is single collection inherited" do
42
+ FinderOptions.new(Enter, :foo => 'bar').criteria.should == {
43
+ :foo => 'bar',
44
+ :_type => 'Enter'
45
+ }
46
+ end
47
+
48
+ should "work with simple criteria" do
49
+ FinderOptions.new(Room, :foo => 'bar').criteria.should == {
50
+ :foo => 'bar'
51
+ }
52
+
53
+ FinderOptions.new(Room, :foo => 'bar', :baz => 'wick').criteria.should == {
54
+ :foo => 'bar',
55
+ :baz => 'wick'
56
+ }
57
+ end
58
+
59
+ should "convert id to _id" do
60
+ id = Mongo::ObjectID.new
61
+ FinderOptions.new(Room, :id => id).criteria.should == {:_id => id}
62
+ end
63
+
64
+ should "make sure that _id's are object ids" do
65
+ id = Mongo::ObjectID.new
66
+ FinderOptions.new(Room, :_id => id.to_s).criteria.should == {:_id => id}
67
+ end
68
+
69
+ should "work fine with _id's that are object ids" do
70
+ id = Mongo::ObjectID.new
71
+ FinderOptions.new(Room, :_id => id).criteria.should == {:_id => id}
72
+ end
73
+
74
+ should "make sure other object id typed keys get converted" do
75
+ id = Mongo::ObjectID.new
76
+ FinderOptions.new(Message, :room_id => id.to_s).criteria.should == {:room_id => id}
77
+ end
78
+
79
+ should "work fine with object ids for object id typed keys" do
80
+ id = Mongo::ObjectID.new
81
+ FinderOptions.new(Message, :room_id => id).criteria.should == {:room_id => id}
82
+ end
83
+
84
+ should "use $in for arrays" do
85
+ FinderOptions.new(Room, :foo => [1,2,3]).criteria.should == {
86
+ :foo => {'$in' => [1,2,3]}
87
+ }
88
+ end
89
+
90
+ should "not use $in for arrays if already using array operator" do
91
+ FinderOptions.new(Room, :foo => {'$all' => [1,2,3]}).criteria.should == {
92
+ :foo => {'$all' => [1,2,3]}
93
+ }
94
+
95
+ FinderOptions.new(Room, :foo => {'$any' => [1,2,3]}).criteria.should == {
96
+ :foo => {'$any' => [1,2,3]}
97
+ }
98
+ end
99
+
100
+ should "work arbitrarily deep" do
101
+ FinderOptions.new(Room, :foo => {:bar => [1,2,3]}).criteria.should == {
102
+ :foo => {:bar => {'$in' => [1,2,3]}}
103
+ }
104
+
105
+ FinderOptions.new(Room, :foo => {:bar => {'$any' => [1,2,3]}}).criteria.should == {
106
+ :foo => {:bar => {'$any' => [1,2,3]}}
107
+ }
108
+ end
109
+ end
110
+
111
+ context "ordering" do
112
+ should "single field with ascending direction" do
113
+ sort = [['foo', 1]]
114
+ FinderOptions.new(Room, :order => 'foo asc').options[:sort].should == sort
115
+ FinderOptions.new(Room, :order => 'foo ASC').options[:sort].should == sort
116
+ end
117
+
118
+ should "single field with descending direction" do
119
+ sort = [['foo', -1]]
120
+ FinderOptions.new(Room, :order => 'foo desc').options[:sort].should == sort
121
+ FinderOptions.new(Room, :order => 'foo DESC').options[:sort].should == sort
122
+ end
123
+
124
+ should "convert field without direction to ascending" do
125
+ sort = [['foo', 1]]
126
+ FinderOptions.new(Room, :order => 'foo').options[:sort].should == sort
127
+ end
128
+
129
+ should "convert multiple fields with directions" do
130
+ sort = [['foo', -1], ['bar', 1], ['baz', -1]]
131
+ FinderOptions.new(Room, :order => 'foo desc, bar asc, baz desc').options[:sort].should == sort
132
+ end
133
+
134
+ should "convert multiple fields with some missing directions" do
135
+ sort = [['foo', -1], ['bar', 1], ['baz', 1]]
136
+ FinderOptions.new(Room, :order => 'foo desc, bar, baz').options[:sort].should == sort
137
+ end
138
+
139
+ should "just use sort if sort and order are present" do
140
+ sort = [['$natural', 1]]
141
+ FinderOptions.new(Room, :sort => sort, :order => 'foo asc').options[:sort].should == sort
142
+ end
143
+
144
+ should "convert natural in order to proper" do
145
+ sort = [['$natural', 1]]
146
+ FinderOptions.new(Room, :order => '$natural asc').options[:sort].should == sort
147
+ sort = [['$natural', -1]]
148
+ FinderOptions.new(Room, :order => '$natural desc').options[:sort].should == sort
149
+ end
150
+
151
+ should "work for natural order ascending" do
152
+ FinderOptions.new(Room, :sort => {'$natural' => 1}).options[:sort]['$natural'].should == 1
153
+ end
154
+
155
+ should "work for natural order descending" do
156
+ FinderOptions.new(Room, :sort => {'$natural' => -1}).options[:sort]['$natural'].should == -1
157
+ end
158
+ end
159
+
160
+ context "skip" do
161
+ should "default to 0" do
162
+ FinderOptions.new(Room, {}).options[:skip].should == 0
163
+ end
164
+
165
+ should "use skip provided" do
166
+ FinderOptions.new(Room, :skip => 2).options[:skip].should == 2
167
+ end
168
+
169
+ should "covert string to integer" do
170
+ FinderOptions.new(Room, :skip => '2').options[:skip].should == 2
171
+ end
172
+
173
+ should "convert offset to skip" do
174
+ FinderOptions.new(Room, :offset => 1).options[:skip].should == 1
175
+ end
176
+ end
177
+
178
+ context "limit" do
179
+ should "default to 0" do
180
+ FinderOptions.new(Room, {}).options[:limit].should == 0
181
+ end
182
+
183
+ should "use limit provided" do
184
+ FinderOptions.new(Room, :limit => 2).options[:limit].should == 2
185
+ end
186
+
187
+ should "covert string to integer" do
188
+ FinderOptions.new(Room, :limit => '2').options[:limit].should == 2
189
+ end
190
+ end
191
+
192
+ context "fields" do
193
+ should "default to nil" do
194
+ FinderOptions.new(Room, {}).options[:fields].should be(nil)
195
+ end
196
+
197
+ should "be converted to nil if empty string" do
198
+ FinderOptions.new(Room, :fields => '').options[:fields].should be(nil)
199
+ end
200
+
201
+ should "be converted to nil if []" do
202
+ FinderOptions.new(Room, :fields => []).options[:fields].should be(nil)
203
+ end
204
+
205
+ should "should work with array" do
206
+ FinderOptions.new(Room, {:fields => %w(a b)}).options[:fields].should == %w(a b)
207
+ end
208
+
209
+ should "convert comma separated list to array" do
210
+ FinderOptions.new(Room, {:fields => 'a, b'}).options[:fields].should == %w(a b)
211
+ end
212
+
213
+ should "also work as select" do
214
+ FinderOptions.new(Room, :select => %w(a b)).options[:fields].should == %w(a b)
215
+ end
216
+ end
217
+
218
+ context "Condition auto-detection" do
219
+ should "know :conditions are criteria" do
220
+ finder = FinderOptions.new(Room, :conditions => {:foo => 'bar'})
221
+ finder.criteria.should == {:foo => 'bar'}
222
+ finder.options.keys.should_not include(:conditions)
223
+ end
224
+
225
+ should "know fields is an option" do
226
+ finder = FinderOptions.new(Room, :fields => ['foo'])
227
+ finder.options[:fields].should == ['foo']
228
+ finder.criteria.keys.should_not include(:fields)
229
+ end
230
+
231
+ # select gets converted to fields so just checking keys
232
+ should "know select is an option" do
233
+ finder = FinderOptions.new(Room, :select => 'foo')
234
+ finder.options.keys.should include(:sort)
235
+ finder.criteria.keys.should_not include(:select)
236
+ finder.criteria.keys.should_not include(:fields)
237
+ end
238
+
239
+ should "know skip is an option" do
240
+ finder = FinderOptions.new(Room, :skip => 10)
241
+ finder.options[:skip].should == 10
242
+ finder.criteria.keys.should_not include(:skip)
243
+ end
244
+
245
+ # offset gets converted to skip so just checking keys
246
+ should "know offset is an option" do
247
+ finder = FinderOptions.new(Room, :offset => 10)
248
+ finder.options.keys.should include(:skip)
249
+ finder.criteria.keys.should_not include(:skip)
250
+ finder.criteria.keys.should_not include(:offset)
251
+ end
252
+
253
+ should "know limit is an option" do
254
+ finder = FinderOptions.new(Room, :limit => 10)
255
+ finder.options[:limit].should == 10
256
+ finder.criteria.keys.should_not include(:limit)
257
+ end
258
+
259
+ should "know sort is an option" do
260
+ finder = FinderOptions.new(Room, :sort => [['foo', 1]])
261
+ finder.options[:sort].should == [['foo', 1]]
262
+ finder.criteria.keys.should_not include(:sort)
263
+ end
264
+
265
+ # order gets converted to sort so just checking keys
266
+ should "know order is an option" do
267
+ finder = FinderOptions.new(Room, :order => 'foo')
268
+ finder.options.keys.should include(:sort)
269
+ finder.criteria.keys.should_not include(:sort)
270
+ end
271
+
272
+ should "work with full range of things" do
273
+ finder_options = FinderOptions.new(Room, {
274
+ :foo => 'bar',
275
+ :baz => true,
276
+ :sort => [['foo', 1]],
277
+ :fields => ['foo', 'baz'],
278
+ :limit => 10,
279
+ :skip => 10,
280
+ })
281
+
282
+ finder_options.criteria.should == {
283
+ :foo => 'bar',
284
+ :baz => true,
285
+ }
286
+
287
+ finder_options.options.should == {
288
+ :sort => [['foo', 1]],
289
+ :fields => ['foo', 'baz'],
290
+ :limit => 10,
291
+ :skip => 10,
292
+ }
293
+ end
294
+ end
295
+
296
+ end # FinderOptionsTest
@@ -0,0 +1,172 @@
1
+ require 'test_helper'
2
+
3
+ class Address
4
+ include MongoMapper::EmbeddedDocument
5
+
6
+ key :address, String
7
+ key :city, String
8
+ key :state, String
9
+ key :zip, Integer
10
+ end
11
+
12
+ class FooType < Struct.new(:bar)
13
+ def self.to_mongo(value)
14
+ 'to_mongo'
15
+ end
16
+
17
+ def self.from_mongo(value)
18
+ 'from_mongo'
19
+ end
20
+ end
21
+
22
+ class KeyTest < Test::Unit::TestCase
23
+ include MongoMapper
24
+
25
+ context "Initializing a new key" do
26
+ should "allow setting the name" do
27
+ Key.new(:foo, String).name.should == 'foo'
28
+ end
29
+
30
+ should "allow setting the type" do
31
+ Key.new(:foo, Integer).type.should be(Integer)
32
+ end
33
+
34
+ should "allow setting options" do
35
+ Key.new(:foo, Integer, :required => true).options[:required].should be(true)
36
+ end
37
+
38
+ should "default options to {}" do
39
+ Key.new(:foo, Integer, nil).options.should == {}
40
+ end
41
+
42
+ should "symbolize option keys" do
43
+ Key.new(:foo, Integer, 'required' => true).options[:required].should be(true)
44
+ end
45
+
46
+ should "work with just name" do
47
+ key = Key.new(:foo)
48
+ key.name.should == 'foo'
49
+ end
50
+
51
+ should "work with name and type" do
52
+ key = Key.new(:foo, String)
53
+ key.name.should == 'foo'
54
+ key.type.should == String
55
+ end
56
+
57
+ should "work with name, type, and options" do
58
+ key = Key.new(:foo, String, :required => true)
59
+ key.name.should == 'foo'
60
+ key.type.should == String
61
+ key.options[:required].should be_true
62
+ end
63
+
64
+ should "work with name and options" do
65
+ key = Key.new(:foo, :required => true)
66
+ key.name.should == 'foo'
67
+ key.options[:required].should be_true
68
+ end
69
+ end
70
+
71
+ context "A key" do
72
+ should "be equal to another key with same name and type" do
73
+ Key.new(:name, String).should == Key.new(:name, String)
74
+ end
75
+
76
+ should "not be equal to another key with different name" do
77
+ Key.new(:name, String).should_not == Key.new(:foo, String)
78
+ end
79
+
80
+ should "not be equal to another key with different type" do
81
+ Key.new(:name, String).should_not == Key.new(:name, Integer)
82
+ end
83
+
84
+ should "know if it is a embedded_document" do
85
+ klass = Class.new do
86
+ include MongoMapper::EmbeddedDocument
87
+ end
88
+ Key.new(:name, klass).embeddable?.should be_true
89
+ end
90
+
91
+ should "know if it is not a embedded_document" do
92
+ Key.new(:name, String).embeddable?.should be_false
93
+ end
94
+
95
+ should "know if it is a number" do
96
+ Key.new(:age, Integer).number?.should be_true
97
+ Key.new(:age, Float).number?.should be_true
98
+ end
99
+
100
+ should "know if it is not a number" do
101
+ Key.new(:age, String).number?.should be_false
102
+ end
103
+ end
104
+
105
+ context "setting a value with a custom type" do
106
+ should "correctly typecast" do
107
+ key = Key.new(:foo, FooType)
108
+ key.set("something").should == 'to_mongo'
109
+ end
110
+
111
+ should "correctly typecast if object of that type is given" do
112
+ key = Key.new(:foo, FooType)
113
+ key.set(FooType.new('something')).should == 'to_mongo'
114
+ end
115
+ end
116
+
117
+ context "getting a value with a custom type" do
118
+ should "use #from_mongo to convert back to custom type" do
119
+ key = Key.new(:foo, FooType)
120
+ key.get('something').should == 'from_mongo'
121
+ end
122
+ end
123
+
124
+ context "getting a value" do
125
+ should "work with a type" do
126
+ key = Key.new(:foo, String)
127
+ key.get('bar').should == 'bar'
128
+ end
129
+
130
+ should "work without type" do
131
+ key = Key.new(:foo)
132
+ key.get([1, '2']).should == [1, '2']
133
+ key.get(false).should == false
134
+ key.get({}).should == {}
135
+ end
136
+
137
+ context "for a embedded_document" do
138
+ should "default to nil" do
139
+ key = Key.new(:foo, Address)
140
+ key.get(nil).should be_nil
141
+ end
142
+
143
+ should "return instance if instance" do
144
+ address = Address.new(:city => 'South Bend', :state => 'IN', :zip => 46544)
145
+ key = Key.new(:foo, Address)
146
+ key.get(address).should == address
147
+ end
148
+ end
149
+ end
150
+
151
+ context "getting a value with a default set" do
152
+ setup do
153
+ @key = Key.new(:foo, String, :default => 'baz')
154
+ end
155
+
156
+ should "return default value if value nil" do
157
+ @key.get(nil).should == 'baz'
158
+ end
159
+
160
+ should "return value if not blank" do
161
+ @key.get('foobar').should == 'foobar'
162
+ end
163
+
164
+ should "work with Boolean type and false value" do
165
+ Key.new(:active, Boolean, :default => false).get(nil).should be_false
166
+ end
167
+
168
+ should "work with Boolean type and true value" do
169
+ Key.new(:active, Boolean, :default => true).get(nil).should be_true
170
+ end
171
+ end
172
+ end # KeyTest