mongo_mapper 0.13.1 → 0.15.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.
Files changed (133) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE +1 -1
  3. data/README.md +61 -0
  4. data/examples/keys.rb +1 -1
  5. data/examples/modifiers/set.rb +1 -1
  6. data/examples/querying.rb +1 -1
  7. data/examples/safe.rb +2 -2
  8. data/examples/scopes.rb +1 -1
  9. data/lib/mongo_mapper.rb +4 -0
  10. data/lib/mongo_mapper/connection.rb +16 -38
  11. data/lib/mongo_mapper/document.rb +2 -0
  12. data/lib/mongo_mapper/extensions/array.rb +14 -6
  13. data/lib/mongo_mapper/extensions/hash.rb +15 -3
  14. data/lib/mongo_mapper/extensions/object.rb +4 -0
  15. data/lib/mongo_mapper/extensions/object_id.rb +5 -1
  16. data/lib/mongo_mapper/extensions/string.rb +13 -5
  17. data/lib/mongo_mapper/plugins/accessible.rb +13 -12
  18. data/lib/mongo_mapper/plugins/associations.rb +7 -6
  19. data/lib/mongo_mapper/plugins/associations/base.rb +23 -14
  20. data/lib/mongo_mapper/plugins/associations/belongs_to_association.rb +1 -1
  21. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +9 -8
  22. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +18 -11
  23. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +4 -4
  24. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +60 -29
  25. data/lib/mongo_mapper/plugins/associations/in_foreign_array_proxy.rb +136 -0
  26. data/lib/mongo_mapper/plugins/associations/many_association.rb +4 -2
  27. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +18 -16
  28. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +55 -48
  29. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +14 -13
  30. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +7 -6
  31. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +7 -5
  32. data/lib/mongo_mapper/plugins/associations/one_as_proxy.rb +14 -11
  33. data/lib/mongo_mapper/plugins/associations/one_embedded_polymorphic_proxy.rb +14 -13
  34. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +12 -10
  35. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +27 -26
  36. data/lib/mongo_mapper/plugins/associations/proxy.rb +38 -27
  37. data/lib/mongo_mapper/plugins/associations/single_association.rb +5 -4
  38. data/lib/mongo_mapper/plugins/callbacks.rb +13 -0
  39. data/lib/mongo_mapper/plugins/counter_cache.rb +23 -4
  40. data/lib/mongo_mapper/plugins/dirty.rb +29 -37
  41. data/lib/mongo_mapper/plugins/document.rb +1 -1
  42. data/lib/mongo_mapper/plugins/dynamic_querying.rb +10 -9
  43. data/lib/mongo_mapper/plugins/dynamic_querying/dynamic_finder.rb +18 -17
  44. data/lib/mongo_mapper/plugins/embedded_callbacks.rb +2 -1
  45. data/lib/mongo_mapper/plugins/embedded_document.rb +2 -2
  46. data/lib/mongo_mapper/plugins/identity_map.rb +4 -2
  47. data/lib/mongo_mapper/plugins/indexes.rb +14 -7
  48. data/lib/mongo_mapper/plugins/keys.rb +164 -159
  49. data/lib/mongo_mapper/plugins/keys/key.rb +27 -16
  50. data/lib/mongo_mapper/plugins/keys/static.rb +45 -0
  51. data/lib/mongo_mapper/plugins/modifiers.rb +64 -38
  52. data/lib/mongo_mapper/plugins/partial_updates.rb +86 -0
  53. data/lib/mongo_mapper/plugins/persistence.rb +13 -8
  54. data/lib/mongo_mapper/plugins/protected.rb +6 -5
  55. data/lib/mongo_mapper/plugins/querying.rb +85 -42
  56. data/lib/mongo_mapper/plugins/querying/decorated_plucky_query.rb +20 -15
  57. data/lib/mongo_mapper/plugins/safe.rb +10 -4
  58. data/lib/mongo_mapper/plugins/scopes.rb +94 -7
  59. data/lib/mongo_mapper/plugins/stats.rb +1 -3
  60. data/lib/mongo_mapper/plugins/strong_parameters.rb +26 -0
  61. data/lib/mongo_mapper/plugins/timestamps.rb +1 -0
  62. data/lib/mongo_mapper/plugins/validations.rb +0 -0
  63. data/lib/mongo_mapper/railtie.rb +1 -0
  64. data/lib/mongo_mapper/utils.rb +2 -2
  65. data/lib/mongo_mapper/version.rb +1 -1
  66. data/lib/rails/generators/mongo_mapper/config/config_generator.rb +12 -13
  67. data/lib/rails/generators/mongo_mapper/model/model_generator.rb +9 -9
  68. data/spec/examples.txt +1728 -0
  69. data/spec/functional/accessible_spec.rb +19 -13
  70. data/spec/functional/associations/belongs_to_polymorphic_proxy_spec.rb +13 -13
  71. data/spec/functional/associations/belongs_to_proxy_spec.rb +54 -20
  72. data/spec/functional/associations/in_array_proxy_spec.rb +145 -10
  73. data/spec/functional/associations/in_foreign_array_proxy_spec.rb +321 -0
  74. data/spec/functional/associations/many_documents_as_proxy_spec.rb +6 -6
  75. data/spec/functional/associations/many_documents_proxy_spec.rb +85 -14
  76. data/spec/functional/associations/many_embedded_polymorphic_proxy_spec.rb +13 -13
  77. data/spec/functional/associations/many_embedded_proxy_spec.rb +1 -1
  78. data/spec/functional/associations/many_polymorphic_proxy_spec.rb +4 -4
  79. data/spec/functional/associations/one_as_proxy_spec.rb +10 -10
  80. data/spec/functional/associations/one_embedded_polymorphic_proxy_spec.rb +9 -9
  81. data/spec/functional/associations/one_embedded_proxy_spec.rb +31 -3
  82. data/spec/functional/associations/one_proxy_spec.rb +21 -11
  83. data/spec/functional/associations_spec.rb +3 -3
  84. data/spec/functional/binary_spec.rb +2 -2
  85. data/spec/functional/caching_spec.rb +8 -15
  86. data/spec/functional/callbacks_spec.rb +89 -2
  87. data/spec/functional/counter_cache_spec.rb +89 -0
  88. data/spec/functional/dirty_spec.rb +84 -46
  89. data/spec/functional/dirty_with_callbacks_spec.rb +59 -0
  90. data/spec/functional/document_spec.rb +2 -5
  91. data/spec/functional/dumpable_spec.rb +1 -1
  92. data/spec/functional/embedded_document_spec.rb +17 -17
  93. data/spec/functional/identity_map_spec.rb +29 -16
  94. data/spec/functional/indexes_spec.rb +19 -18
  95. data/spec/functional/keys_spec.rb +55 -28
  96. data/spec/functional/logger_spec.rb +3 -3
  97. data/spec/functional/modifiers_spec.rb +81 -19
  98. data/spec/functional/partial_updates_spec.rb +577 -0
  99. data/spec/functional/protected_spec.rb +14 -14
  100. data/spec/functional/querying_spec.rb +77 -28
  101. data/spec/functional/safe_spec.rb +23 -27
  102. data/spec/functional/sci_spec.rb +9 -9
  103. data/spec/functional/scopes_spec.rb +323 -2
  104. data/spec/functional/static_keys_spec.rb +153 -0
  105. data/spec/functional/stats_spec.rb +28 -16
  106. data/spec/functional/strong_parameters_spec.rb +49 -0
  107. data/spec/functional/touch_spec.rb +1 -1
  108. data/spec/functional/validations_spec.rb +51 -57
  109. data/spec/quality_spec.rb +2 -2
  110. data/spec/spec_helper.rb +37 -9
  111. data/spec/support/matchers.rb +5 -14
  112. data/spec/unit/associations/base_spec.rb +12 -12
  113. data/spec/unit/associations/belongs_to_association_spec.rb +2 -2
  114. data/spec/unit/associations/many_association_spec.rb +2 -2
  115. data/spec/unit/associations/one_association_spec.rb +2 -2
  116. data/spec/unit/associations/proxy_spec.rb +26 -20
  117. data/spec/unit/clone_spec.rb +1 -1
  118. data/spec/unit/document_spec.rb +8 -8
  119. data/spec/unit/dynamic_finder_spec.rb +8 -8
  120. data/spec/unit/embedded_document_spec.rb +18 -19
  121. data/spec/unit/extensions_spec.rb +17 -17
  122. data/spec/unit/identity_map_middleware_spec.rb +65 -96
  123. data/spec/unit/key_spec.rb +28 -26
  124. data/spec/unit/keys_spec.rb +20 -11
  125. data/spec/unit/mongo_mapper_spec.rb +38 -85
  126. data/spec/unit/serialization_spec.rb +1 -1
  127. data/spec/unit/time_zones_spec.rb +2 -2
  128. data/spec/unit/validations_spec.rb +46 -33
  129. metadata +56 -32
  130. data/README.rdoc +0 -59
  131. data/lib/mongo_mapper/connections/10gen.rb +0 -0
  132. data/lib/mongo_mapper/connections/moped.rb +0 -0
  133. data/lib/mongo_mapper/extensions/ordered_hash.rb +0 -23
@@ -3,12 +3,12 @@ require 'spec_helper'
3
3
  describe "Single collection inheritance (document)" do
4
4
  context "without a connection", :without_connection => true do
5
5
  it "should attempt to create a connection during inheritance" do
6
- Mongo::MongoClient.should_not_receive(:new)
6
+ Mongo::Client.should_not_receive(:new)
7
7
  doc = Class.new
8
8
  doc.send(:include, MongoMapper::Document)
9
- expect {
9
+ lambda {
10
10
  Class.new(doc)
11
- }.to_not raise_error
11
+ }.should_not raise_error
12
12
  end
13
13
 
14
14
  it "should pick up a connection if one wasn't set" do
@@ -17,7 +17,7 @@ describe "Single collection inheritance (document)" do
17
17
  klass = Class.new(doc)
18
18
  klass.connection.should be_nil
19
19
  MongoMapper.connection
20
- klass.connection.should be_a Mongo::MongoClient
20
+ klass.connection.should be_a Mongo::Client
21
21
  end
22
22
  end
23
23
 
@@ -27,7 +27,7 @@ describe "Single collection inheritance (document)" do
27
27
  include MongoMapper::Document
28
28
  key :name, String
29
29
  end
30
- DocParent.collection.remove
30
+ DocParent.collection.drop
31
31
 
32
32
  class ::DocDaughter < ::DocParent; end
33
33
  class ::DocSon < ::DocParent; end
@@ -49,7 +49,7 @@ describe "Single collection inheritance (document)" do
49
49
  end
50
50
 
51
51
  it "should automatically add _type key to store class" do
52
- DocParent.key?(:_type).should be_true
52
+ DocParent.key?(:_type).should be_truthy
53
53
  end
54
54
 
55
55
  it "should use modifiers properly" do
@@ -61,7 +61,7 @@ describe "Single collection inheritance (document)" do
61
61
  it "should use the same connection in the subclass" do
62
62
  parent_class = Class.new do
63
63
  include MongoMapper::Document
64
- connection Mongo::MongoClient.new
64
+ connection Mongo::Client.new(['127.0.0.1:27017'])
65
65
  end
66
66
 
67
67
  child_class = Class.new(parent_class) do
@@ -323,7 +323,7 @@ describe "Single collection inheritance (document)" do
323
323
  end
324
324
 
325
325
  it "should automatically add _type key" do
326
- Grandparent.key?(:_type).should be_true
326
+ Grandparent.key?(:_type).should be_truthy
327
327
  end
328
328
 
329
329
  context ".single_collection_inherited?" do
@@ -377,4 +377,4 @@ describe "Single collection inheritance (document)" do
377
377
  p.reload.article_parent.sci_polymorphic_posts.all.should include(p)
378
378
  end
379
379
  end
380
- end
380
+ end
@@ -66,7 +66,7 @@ describe "Scopes" do
66
66
  @document.class_eval do
67
67
  scope :age, lambda { |age| {:age => age} }
68
68
  scope :ages, lambda { |low, high| {:age.gte => low, :age.lte => high} }
69
- scope :ordered, lambda { |sort| sort(sort) }
69
+ scope :ordered, lambda { |s| sort(s) }
70
70
  end
71
71
  end
72
72
 
@@ -170,7 +170,7 @@ describe "Scopes" do
170
170
  key :title, String
171
171
  key :published_at, Time
172
172
  end
173
- Item.collection.remove
173
+ Item.collection.drop
174
174
 
175
175
  class ::Page < ::Item; end
176
176
  class ::Blog < ::Item
@@ -200,5 +200,326 @@ describe "Scopes" do
200
200
  Blog.scopes.keys.map(&:to_s).should =~ %w(by_slug by_title published)
201
201
  end
202
202
  end
203
+
204
+ context 'with predefined class method name' do
205
+ it 'should raise ArgumentError' do
206
+ bad_names = %i(private public protected allocate new name parent superclass reload)
207
+ bad_names.each do |bad_name|
208
+ -> {
209
+ Doc() do
210
+ scope bad_name, -> {}
211
+ end
212
+ }.should raise_error(ArgumentError, /You tried to define a scope named "#{bad_name}"/)
213
+ end
214
+ end
215
+ end
216
+ end
217
+
218
+ describe "with_scope" do
219
+ describe "with a base query" do
220
+ before do
221
+ @klass = Doc do
222
+ end
223
+ end
224
+
225
+ it "should scope" do
226
+ u1 = @klass.create!(:first_name => "Scott")
227
+ u2 = @klass.create!(:first_name => "Andrew")
228
+
229
+ run = false
230
+
231
+ @klass.with_scope(:first_name => "Scott") do
232
+ run = true
233
+ @klass.all.should == [u1]
234
+ end
235
+
236
+ run.should == true
237
+ end
238
+
239
+ it "should return the scope at the end of the block" do
240
+ u1 = @klass.create!(:first_name => "Scott")
241
+ u2 = @klass.create!(:first_name => "Andrew")
242
+
243
+ run = false
244
+
245
+ @klass.with_scope(:first_name => "Scott") do
246
+ run = true
247
+ @klass.all.should == [u1]
248
+ end
249
+
250
+ run.should == true
251
+
252
+ @klass.all.should include(u1)
253
+ @klass.all.should include(u2)
254
+ end
255
+
256
+ it "should be able to use an unscoped query" do
257
+ u1 = @klass.create!(:first_name => "Scott")
258
+ u2 = @klass.create!(:first_name => "Andrew")
259
+
260
+ run = false
261
+
262
+ @klass.with_scope(:first_name => "Scott") do
263
+ @klass.unscoped do
264
+ run = true
265
+ @klass.all.should include(u1)
266
+ @klass.all.should include(u2)
267
+ end
268
+
269
+ @klass.all.should == [u1]
270
+ end
271
+
272
+ run.should == true
273
+
274
+ @klass.all.should include(u1)
275
+ @klass.all.should include(u2)
276
+ end
277
+
278
+ it "should return the value of the block" do
279
+ u1 = @klass.create!(:first_name => "Scott")
280
+ u2 = @klass.create!(:first_name => "Andrew")
281
+
282
+ result = @klass.with_scope(:first_name => "Scott") do
283
+ @klass.all
284
+ end
285
+
286
+ result.should == [u1]
287
+ end
288
+
289
+ it "should have an empty list of default scope" do
290
+ @klass.default_scopes.should == []
291
+ end
292
+
293
+ it "should apply the default scope to all queries" do
294
+ @klass.key :active, Boolean, :default => true
295
+
296
+ normal = @klass.create!
297
+ inactive = @klass.create!(:active => false)
298
+
299
+ @klass.default_scope do
300
+ {
301
+ :active => true
302
+ }
303
+ end
304
+
305
+ @klass.all.should == [normal]
306
+ end
307
+
308
+ it "should allow multiple default scopes" do
309
+ @klass.key :active, Boolean, :default => true
310
+ @klass.key :soft_deleted, Boolean, :default => false
311
+
312
+ normal = @klass.create!
313
+ inactive = @klass.create!(:active => false)
314
+ deleted = @klass.create!(:soft_deleted => true)
315
+
316
+ @klass.default_scope do
317
+ {
318
+ :active => true,
319
+ }
320
+ end
321
+
322
+ @klass.default_scope do
323
+ {
324
+ :soft_deleted => false
325
+ }
326
+ end
327
+
328
+ @klass.all.should == [normal]
329
+ end
330
+
331
+ it "should allow a where inside a default_scope" do
332
+ @klass.key :active, Boolean, :default => true
333
+
334
+ normal = @klass.create!
335
+ inactive = @klass.create!(:active => false)
336
+
337
+ @klass.default_scope do
338
+ where(:active => true)
339
+ end
340
+
341
+ @klass.all.should == [normal]
342
+ end
343
+
344
+ it "should allow a hash directly" do
345
+ @klass.key :active, Boolean, :default => true
346
+
347
+ normal = @klass.create!
348
+ inactive = @klass.create!(:active => false)
349
+
350
+ @klass.default_scope :active => true
351
+
352
+ @klass.all.should == [normal]
353
+ end
354
+
355
+ it "should inherit default scopes, but only downwards" do
356
+ @klass.default_scope do
357
+ where(:active => true)
358
+ end
359
+ @subclass = Class.new(@klass)
360
+ @subclass.default_scopes.length.should == 1
361
+
362
+ @subclass.default_scope do
363
+ {
364
+ :foo => :bar
365
+ }
366
+ end
367
+
368
+ @subclass.default_scopes.length.should == 2
369
+ @klass.default_scopes.length.should == 1
370
+ end
371
+ end
372
+ end
373
+
374
+ describe "regression - multiple named scopes" do
375
+ it "should merge the hashes from multiple scopes" do
376
+ klass = Doc do
377
+ key :a, Boolean
378
+ key :b, Boolean
379
+ key :c, Boolean
380
+
381
+ scope :one, where(:a => true, :b => true)
382
+ scope :two, where(:c => true)
383
+ end
384
+
385
+ obj = klass.create!(:a => false, :b => true, :c => true)
386
+ klass.one.two.all.should == []
387
+
388
+ obj2 = klass.create(:a => true, :b => true, :c => true)
389
+ klass.one.two.all.should == [obj2]
390
+ end
391
+ end
392
+
393
+ describe "regression - with multiple scopes that set a key to nil" do
394
+ it "should still have the query as nil" do
395
+ klass = Doc do
396
+ key :a, Boolean
397
+
398
+ scope :one, lambda {
399
+ {
400
+ :a => nil
401
+ }
402
+ }
403
+ scope :two, lambda {
404
+ {
405
+ :a => nil
406
+ }
407
+ }
408
+ end
409
+
410
+ obj = klass.create!(:a => nil)
411
+ klass.one.two.all.should == [obj]
412
+ end
413
+ end
414
+
415
+ describe "regression - multiple scopes leading to empty query (array bug?)" do
416
+ before do
417
+ @klass = Doc do
418
+ key :send_at, Time
419
+
420
+ scope :unsent, lambda {
421
+ now = Time.now
422
+
423
+ {
424
+ send_at: {
425
+ '$lte' => now,
426
+ '$gte' => now - 30.days,
427
+ },
428
+ sent_at: nil,
429
+ }
430
+ }
431
+
432
+ scope :sorted, lambda {
433
+ sort(send_at: 1)
434
+ }
435
+ end
436
+ end
437
+
438
+ it "should filter properly by dates" do
439
+ old_obj = @klass.create(send_at: 3.months.ago)
440
+ one_day_ago = @klass.create(send_at: 1.day.ago)
441
+ two_days_ago = @klass.create(send_at: 2.days.ago)
442
+
443
+ all_unsorted = @klass.unsent.all
444
+
445
+ all_unsorted.should include(one_day_ago)
446
+ all_unsorted.should include(two_days_ago)
447
+ @klass.unsent.sorted.all.should == [two_days_ago, one_day_ago]
448
+ end
449
+ end
450
+
451
+ describe "thread safety" do
452
+ before do
453
+ @klass = Doc do
454
+ key :name, String
455
+ end
456
+
457
+ @threads = []
458
+ end
459
+
460
+ after do
461
+ @threads.each do |thread|
462
+ thread.kill
463
+ end
464
+ end
465
+
466
+ def make_thread(&block)
467
+ Thread.new(&block).tap do |thread|
468
+ @threads << thread
469
+ end
470
+ end
471
+
472
+ def wait_for_threads!
473
+ @threads.each { |t| t.join }
474
+ end
475
+
476
+ it "should not taint another thread's active scopes" do
477
+ count_active_scopes = nil
478
+ second_thread_run = false
479
+
480
+ make_thread do
481
+ @klass.with_scope(name: 'foo') do
482
+ loop do
483
+ sleep Float::EPSILON
484
+ break if second_thread_run
485
+ end
486
+ end
487
+ end
488
+
489
+ make_thread do
490
+ count_active_scopes = @klass.active_scopes.length
491
+ second_thread_run = true
492
+ end
493
+
494
+ wait_for_threads!
495
+
496
+ count_active_scopes.should == 0
497
+ end
498
+
499
+ it "should not taint another thread's count method" do
500
+ @klass.create!(name: 'foo')
501
+ @klass.create!(name: 'bar')
502
+
503
+ klass_count = nil
504
+ second_thread_run = false
505
+
506
+ make_thread do
507
+ @klass.with_scope(name: 'foo') do
508
+ loop do
509
+ sleep Float::EPSILON
510
+ break if second_thread_run
511
+ end
512
+ end
513
+ end
514
+
515
+ make_thread do
516
+ klass_count = @klass.count
517
+ second_thread_run = true
518
+ end
519
+
520
+ wait_for_threads!
521
+
522
+ klass_count.should == 2
523
+ end
203
524
  end
204
525
  end
@@ -0,0 +1,153 @@
1
+ require 'spec_helper'
2
+
3
+ describe MongoMapper::Plugins::Keys::Static do
4
+ before do
5
+ @klass = Class.new do
6
+ include MongoMapper::Document
7
+ end
8
+ end
9
+
10
+ it "is off by default" do
11
+ @klass.static_keys.should == false
12
+ end
13
+
14
+ it "can be turned on" do
15
+ @klass.static_keys = true
16
+ @klass.static_keys.should == true
17
+ end
18
+
19
+ describe "a class with static keys" do
20
+ before do
21
+ @klass = Class.new do
22
+ include MongoMapper::Document
23
+
24
+ self.static_keys = true
25
+
26
+ attr_accessor :an_accessor
27
+
28
+ key "valid_key", String
29
+ end
30
+
31
+ @obj = @klass.new
32
+ end
33
+
34
+ it "should be able to assign to a valid key" do
35
+ @obj.valid_key = "foo"
36
+ @obj.valid_key.should == "foo"
37
+ end
38
+
39
+ it "should raise a MissingKeyError if trying to assign to a field that does not exist" do
40
+ lambda {
41
+ @obj['foo'] = "foo"
42
+ }.should raise_error(MongoMapper::Plugins::Keys::Static::MissingKeyError)
43
+ end
44
+
45
+ it "should raise a MissingKeyError if trying to read from a field that does not exist" do
46
+ lambda {
47
+ @obj['foo']
48
+ }.should raise_error(MongoMapper::Plugins::Keys::Static::MissingKeyError)
49
+ end
50
+
51
+ it "should raise a MissingKeyError if trying to set attributes= to a field that does not exist" do
52
+ lambda {
53
+ @obj.attributes = {:foo => "foo"}
54
+ }.should raise_error(MongoMapper::Plugins::Keys::Static::MissingKeyError)
55
+ end
56
+
57
+ it "should be able to call a method" do
58
+ @obj.an_accessor = :bar
59
+ @obj.an_accessor.should == :bar
60
+ end
61
+
62
+ it "should raise a NoMethodError if trying to call the non-existant key method" do
63
+ lambda {
64
+ @obj.foo
65
+ }.should raise_error(NoMethodError)
66
+ end
67
+
68
+ it "should raise for read_key" do
69
+ lambda {
70
+ @obj.read_key('foo')
71
+ }.should raise_error(MongoMapper::Plugins::Keys::Static::MissingKeyError)
72
+ end
73
+
74
+ it "should raise for attribute" do
75
+ lambda {
76
+ @obj.attribute('foo')
77
+ }.should raise_error(MongoMapper::Plugins::Keys::Static::MissingKeyError)
78
+ end
79
+
80
+ it "should not raise if trying to assign to a field that exists (but with a symbol instead of a string)" do
81
+ lambda {
82
+ @obj.write_key(:valid_key, "foo")
83
+ }.should_not raise_error
84
+
85
+ @obj.valid_key.should == "foo"
86
+ end
87
+
88
+ it "should not raise a MissingKeyError if trying to read from a field that exists (but with a symbol instead of a string)" do
89
+ lambda {
90
+ @obj[:valid_key]
91
+ }.should_not raise_error
92
+ end
93
+
94
+ it "should not blow up when loading if there is a key defined in the db that has not been defined (but it should not load it)" do
95
+ @klass.collection.insert_one({ :foo => "bar", :valid_key => "something" })
96
+ @obj = @klass.first
97
+ @obj.valid_key.should == "something"
98
+
99
+ lambda {
100
+ @obj.foo
101
+ }.should raise_error(NoMethodError)
102
+ end
103
+ end
104
+
105
+ describe "a class without static keys" do
106
+ before do
107
+ @klass = Class.new do
108
+ include MongoMapper::Document
109
+ key :valid_key, String
110
+ end
111
+
112
+ @obj = @klass.new
113
+ end
114
+
115
+ it "should be able to assign to a valid key" do
116
+ @obj.valid_key = "foo"
117
+ @obj.valid_key.should == "foo"
118
+ end
119
+
120
+ it "should be able to read and assign a dynamic key" do
121
+ @obj['foo'].should be_nil
122
+ @obj['foo'] = "bar"
123
+ @obj['foo'].should == "bar"
124
+ end
125
+ end
126
+
127
+ describe "with mixed static + non static models" do
128
+ before do
129
+ @static_model = Class.new do
130
+ include MongoMapper::Document
131
+ self.static_keys = true
132
+ end
133
+
134
+ @non_static_model = Class.new do
135
+ include MongoMapper::Document
136
+ self.static_keys = false
137
+ end
138
+
139
+ @static_key_object = @static_model.new
140
+ @non_static_key_object = @non_static_model.new
141
+ end
142
+
143
+ it "should allow non-static key assignment to one, only static assignment to the other" do
144
+ @non_static_key_object['foo'] = 'bar'
145
+ @non_static_key_object.foo.should == 'bar'
146
+ @non_static_key_object['foo'].should == 'bar'
147
+
148
+ lambda {
149
+ @static_key_object['foo'] = 'bar'
150
+ }.should raise_error(MongoMapper::Plugins::Keys::Static::MissingKeyError)
151
+ end
152
+ end
153
+ end