mongo_mapper 0.12.0 → 0.13.0.beta1

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 (154) hide show
  1. checksums.yaml +7 -0
  2. data/README.rdoc +35 -13
  3. data/bin/mmconsole +1 -1
  4. data/lib/mongo_mapper.rb +4 -0
  5. data/lib/mongo_mapper/connection.rb +17 -6
  6. data/lib/mongo_mapper/document.rb +1 -0
  7. data/lib/mongo_mapper/exceptions.rb +4 -1
  8. data/lib/mongo_mapper/extensions/binary.rb +1 -1
  9. data/lib/mongo_mapper/extensions/boolean.rb +20 -23
  10. data/lib/mongo_mapper/extensions/date.rb +3 -3
  11. data/lib/mongo_mapper/extensions/integer.rb +5 -1
  12. data/lib/mongo_mapper/extensions/kernel.rb +2 -0
  13. data/lib/mongo_mapper/extensions/ordered_hash.rb +23 -0
  14. data/lib/mongo_mapper/extensions/string.rb +2 -2
  15. data/lib/mongo_mapper/extensions/time.rb +7 -5
  16. data/lib/mongo_mapper/middleware/identity_map.rb +3 -4
  17. data/lib/mongo_mapper/plugins.rb +1 -1
  18. data/lib/mongo_mapper/plugins/associations.rb +11 -5
  19. data/lib/mongo_mapper/plugins/associations/base.rb +5 -3
  20. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +0 -0
  21. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +8 -8
  22. data/lib/mongo_mapper/plugins/associations/collection.rb +2 -0
  23. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +32 -7
  24. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +2 -2
  25. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +12 -12
  26. data/lib/mongo_mapper/plugins/associations/proxy.rb +5 -1
  27. data/lib/mongo_mapper/plugins/associations/single_association.rb +6 -6
  28. data/lib/mongo_mapper/plugins/clone.rb +4 -2
  29. data/lib/mongo_mapper/plugins/dirty.rb +22 -21
  30. data/lib/mongo_mapper/plugins/document.rb +4 -4
  31. data/lib/mongo_mapper/plugins/dumpable.rb +22 -0
  32. data/lib/mongo_mapper/plugins/embedded_callbacks.rb +58 -9
  33. data/lib/mongo_mapper/plugins/identity_map.rb +42 -32
  34. data/lib/mongo_mapper/plugins/keys.rb +133 -54
  35. data/lib/mongo_mapper/plugins/keys/key.rb +68 -22
  36. data/lib/mongo_mapper/plugins/modifiers.rb +26 -19
  37. data/lib/mongo_mapper/plugins/persistence.rb +15 -5
  38. data/lib/mongo_mapper/plugins/querying.rb +15 -40
  39. data/lib/mongo_mapper/plugins/querying/{decorator.rb → decorated_plucky_query.rb} +24 -4
  40. data/lib/mongo_mapper/plugins/rails.rb +22 -2
  41. data/lib/mongo_mapper/plugins/safe.rb +8 -5
  42. data/lib/mongo_mapper/plugins/sci.rb +26 -4
  43. data/lib/mongo_mapper/plugins/scopes.rb +5 -4
  44. data/lib/mongo_mapper/plugins/timestamps.rb +11 -4
  45. data/lib/mongo_mapper/plugins/validations.rb +1 -1
  46. data/lib/mongo_mapper/utils.rb +12 -0
  47. data/lib/mongo_mapper/version.rb +1 -1
  48. data/lib/rails/generators/mongo_mapper/config/config_generator.rb +20 -7
  49. data/lib/rails/generators/mongo_mapper/config/templates/mongo.yml +6 -0
  50. data/lib/rails/generators/mongo_mapper/model/model_generator.rb +18 -1
  51. data/lib/rails/generators/mongo_mapper/model/templates/model.rb +9 -5
  52. data/{test/functional/test_accessible.rb → spec/functional/accessible_spec.rb} +29 -29
  53. data/{test/functional/associations/test_belongs_to_polymorphic_proxy.rb → spec/functional/associations/belongs_to_polymorphic_proxy_spec.rb} +10 -10
  54. data/{test/functional/associations/test_belongs_to_proxy.rb → spec/functional/associations/belongs_to_proxy_spec.rb} +82 -64
  55. data/{test/functional/associations/test_in_array_proxy.rb → spec/functional/associations/in_array_proxy_spec.rb} +68 -68
  56. data/{test/functional/associations/test_many_documents_as_proxy.rb → spec/functional/associations/many_documents_as_proxy_spec.rb} +37 -38
  57. data/{test/functional/associations/test_many_documents_proxy.rb → spec/functional/associations/many_documents_proxy_spec.rb} +233 -146
  58. data/{test/functional/associations/test_many_embedded_polymorphic_proxy.rb → spec/functional/associations/many_embedded_polymorphic_proxy_spec.rb} +19 -20
  59. data/{test/functional/associations/test_many_embedded_proxy.rb → spec/functional/associations/many_embedded_proxy_spec.rb} +23 -24
  60. data/{test/functional/associations/test_many_polymorphic_proxy.rb → spec/functional/associations/many_polymorphic_proxy_spec.rb} +45 -46
  61. data/{test/functional/associations/test_one_as_proxy.rb → spec/functional/associations/one_as_proxy_spec.rb} +75 -77
  62. data/{test/functional/associations/test_one_embedded_polymorphic_proxy.rb → spec/functional/associations/one_embedded_polymorphic_proxy_spec.rb} +31 -32
  63. data/{test/functional/associations/test_one_embedded_proxy.rb → spec/functional/associations/one_embedded_proxy_spec.rb} +10 -10
  64. data/{test/functional/associations/test_one_proxy.rb → spec/functional/associations/one_proxy_spec.rb} +125 -102
  65. data/spec/functional/associations_spec.rb +48 -0
  66. data/{test/functional/test_binary.rb → spec/functional/binary_spec.rb} +6 -6
  67. data/spec/functional/caching_spec.rb +75 -0
  68. data/{test/functional/test_callbacks.rb → spec/functional/callbacks_spec.rb} +84 -26
  69. data/{test/functional/test_dirty.rb → spec/functional/dirty_spec.rb} +57 -42
  70. data/{test/functional/test_document.rb → spec/functional/document_spec.rb} +52 -52
  71. data/spec/functional/dumpable_spec.rb +24 -0
  72. data/{test/functional/test_dynamic_querying.rb → spec/functional/dynamic_querying_spec.rb} +14 -14
  73. data/{test/functional/test_embedded_document.rb → spec/functional/embedded_document_spec.rb} +51 -42
  74. data/{test/functional/test_equality.rb → spec/functional/equality_spec.rb} +4 -4
  75. data/spec/functional/extensions_spec.rb +16 -0
  76. data/{test/functional/test_identity_map.rb → spec/functional/identity_map_spec.rb} +73 -61
  77. data/spec/functional/indexes_spec.rb +48 -0
  78. data/spec/functional/keys_spec.rb +224 -0
  79. data/{test/functional/test_logger.rb → spec/functional/logger_spec.rb} +6 -6
  80. data/spec/functional/modifiers_spec.rb +550 -0
  81. data/spec/functional/pagination_spec.rb +89 -0
  82. data/spec/functional/protected_spec.rb +199 -0
  83. data/spec/functional/querying_spec.rb +1003 -0
  84. data/spec/functional/rails_spec.rb +55 -0
  85. data/spec/functional/safe_spec.rb +163 -0
  86. data/{test/functional/test_sci.rb → spec/functional/sci_spec.rb} +123 -34
  87. data/{test/functional/test_scopes.rb → spec/functional/scopes_spec.rb} +59 -26
  88. data/spec/functional/timestamps_spec.rb +97 -0
  89. data/{test/functional/test_touch.rb → spec/functional/touch_spec.rb} +13 -13
  90. data/spec/functional/userstamps_spec.rb +46 -0
  91. data/{test/functional/test_validations.rb → spec/functional/validations_spec.rb} +64 -64
  92. data/spec/spec_helper.rb +81 -0
  93. data/spec/support/matchers.rb +24 -0
  94. data/{test → spec/support}/models.rb +1 -6
  95. data/spec/unit/associations/base_spec.rb +146 -0
  96. data/spec/unit/associations/belongs_to_association_spec.rb +30 -0
  97. data/spec/unit/associations/many_association_spec.rb +64 -0
  98. data/spec/unit/associations/one_association_spec.rb +48 -0
  99. data/{test/unit/associations/test_proxy.rb → spec/unit/associations/proxy_spec.rb} +21 -21
  100. data/{test/unit/test_clone.rb → spec/unit/clone_spec.rb} +21 -11
  101. data/spec/unit/config_generator_spec.rb +24 -0
  102. data/{test/unit/test_document.rb → spec/unit/document_spec.rb} +42 -42
  103. data/{test/unit/test_dynamic_finder.rb → spec/unit/dynamic_finder_spec.rb} +28 -28
  104. data/{test/unit/test_embedded_document.rb → spec/unit/embedded_document_spec.rb} +102 -108
  105. data/{test/unit/test_equality.rb → spec/unit/equality_spec.rb} +7 -7
  106. data/{test/unit/test_exceptions.rb → spec/unit/exceptions_spec.rb} +3 -3
  107. data/{test/unit/test_extensions.rb → spec/unit/extensions_spec.rb} +85 -71
  108. data/spec/unit/identity_map_middleware_spec.rb +134 -0
  109. data/{test/unit/test_inspect.rb → spec/unit/inspect_spec.rb} +8 -8
  110. data/{test/unit/test_key.rb → spec/unit/key_spec.rb} +82 -52
  111. data/spec/unit/keys_spec.rb +155 -0
  112. data/spec/unit/model_generator_spec.rb +47 -0
  113. data/spec/unit/mongo_mapper_spec.rb +184 -0
  114. data/spec/unit/pagination_spec.rb +11 -0
  115. data/{test/unit/test_plugins.rb → spec/unit/plugins_spec.rb} +14 -14
  116. data/spec/unit/rails_compatibility_spec.rb +40 -0
  117. data/{test/unit/test_rails_reflect_on_association.rb → spec/unit/rails_reflect_on_association_spec.rb} +9 -9
  118. data/{test/unit/test_rails.rb → spec/unit/rails_spec.rb} +31 -31
  119. data/spec/unit/serialization_spec.rb +169 -0
  120. data/spec/unit/serializers/json_serializer_spec.rb +218 -0
  121. data/spec/unit/serializers/xml_serializer_spec.rb +198 -0
  122. data/{test/unit/test_time_zones.rb → spec/unit/time_zones_spec.rb} +8 -8
  123. data/{test/unit/test_translation.rb → spec/unit/translation_spec.rb} +6 -6
  124. data/{test/unit/test_validations.rb → spec/unit/validations_spec.rb} +72 -59
  125. metadata +199 -179
  126. data/test/_NOTE_ON_TESTING +0 -1
  127. data/test/functional/test_associations.rb +0 -46
  128. data/test/functional/test_caching.rb +0 -77
  129. data/test/functional/test_indexes.rb +0 -50
  130. data/test/functional/test_modifiers.rb +0 -537
  131. data/test/functional/test_pagination.rb +0 -91
  132. data/test/functional/test_protected.rb +0 -201
  133. data/test/functional/test_querying.rb +0 -935
  134. data/test/functional/test_safe.rb +0 -76
  135. data/test/functional/test_timestamps.rb +0 -62
  136. data/test/functional/test_userstamps.rb +0 -44
  137. data/test/support/railtie.rb +0 -4
  138. data/test/support/railtie/autoloaded.rb +0 -2
  139. data/test/support/railtie/not_autoloaded.rb +0 -3
  140. data/test/support/railtie/parent.rb +0 -3
  141. data/test/test_active_model_lint.rb +0 -18
  142. data/test/test_helper.rb +0 -93
  143. data/test/unit/associations/test_base.rb +0 -146
  144. data/test/unit/associations/test_belongs_to_association.rb +0 -29
  145. data/test/unit/associations/test_many_association.rb +0 -63
  146. data/test/unit/associations/test_one_association.rb +0 -47
  147. data/test/unit/serializers/test_json_serializer.rb +0 -216
  148. data/test/unit/serializers/test_xml_serializer.rb +0 -196
  149. data/test/unit/test_identity_map_middleware.rb +0 -132
  150. data/test/unit/test_keys.rb +0 -65
  151. data/test/unit/test_mongo_mapper.rb +0 -157
  152. data/test/unit/test_pagination.rb +0 -11
  153. data/test/unit/test_rails_compatibility.rb +0 -38
  154. data/test/unit/test_serialization.rb +0 -166
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Indexing" do
4
+ before do
5
+ @document = Doc do
6
+ key :first_name, String
7
+ key :last_name, String
8
+ key :age, Integer
9
+ key :date, Date
10
+ end
11
+ end
12
+ after { drop_indexes(@document) }
13
+
14
+ [:create_index, :ensure_index, :drop_index, :drop_indexes].each do |method|
15
+ it "should delegate #{method} to collection" do
16
+ @document.stub(:collection).and_return(double(:name => :foo))
17
+ @document.collection.should_receive(method).with(:arg)
18
+ @document.send(method, :arg)
19
+ end
20
+ end
21
+
22
+ it "should allow creating index for a key" do
23
+ @document.ensure_index :first_name
24
+ @document.should have_index('first_name_1')
25
+ end
26
+
27
+ it "should allow creating unique index for a key" do
28
+ @document.ensure_index :first_name, :unique => true
29
+ @document.should have_index('first_name_1')
30
+ end
31
+
32
+ it "should allow creating index on multiple keys" do
33
+ @document.ensure_index [[:first_name, 1], [:last_name, -1]]
34
+
35
+ # order is different for different versions of ruby so instead of
36
+ # just checking have_index('first_name_1_last_name_-1') I'm checking
37
+ # the values of the indexes to make sure the index creation was successful
38
+ @document.collection.index_information.detect do |index|
39
+ keys = index[0]
40
+ keys.include?('first_name_1') && keys.include?('last_name_-1')
41
+ end.should_not be_nil
42
+ end
43
+
44
+ it "should work with :index shortcut when defining key" do
45
+ silence_stderr { @document.key :father, String, :index => true }
46
+ @document.should have_index('father_1')
47
+ end
48
+ end
@@ -0,0 +1,224 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Keys" do
4
+ it "should not have a disparity between the ivar and accessor" do
5
+ doc = Doc do
6
+ key :foo, String
7
+
8
+ def modify_foo
9
+ @foo = :baz
10
+ end
11
+
12
+ def get_foo
13
+ @foo
14
+ end
15
+ end
16
+
17
+ instance = doc.new(:foo => "bar")
18
+ instance.get_foo.should == instance.foo
19
+
20
+ instance.foo = :bing
21
+ instance.get_foo.should == instance.foo
22
+
23
+ instance.modify_foo
24
+ instance.get_foo.should == instance.foo
25
+ end
26
+
27
+ it "should not bomb if a key is written before Keys#initialize gets to get called" do
28
+ doc = Class.new do
29
+ include MongoMapper::Document
30
+
31
+ def initialize
32
+ self.class.key :something, String
33
+ self.something = :other_thing
34
+ super
35
+ end
36
+ end
37
+
38
+ expect { doc.new }.to_not raise_error
39
+ end
40
+
41
+ it "should not bomb if a key is read before Keys#initialize gets to get called" do
42
+ doc = Class.new do
43
+ include MongoMapper::Document
44
+
45
+ def initialize
46
+ self.class.key :something, String
47
+ self.something
48
+ super
49
+ end
50
+ end
51
+
52
+ expect { doc.new }.to_not raise_error
53
+ end
54
+
55
+
56
+ context "key segmenting" do
57
+ let(:doc) {
58
+ Doc {
59
+ key :defined
60
+ }
61
+ }
62
+
63
+ before do
64
+ doc.collection.insert(:dynamic => "foo")
65
+ doc.first
66
+ end
67
+
68
+ describe "#dynamic_keys" do
69
+ it "should find dynamic keys" do
70
+ doc.dynamic_keys.keys.should == ["dynamic"]
71
+ end
72
+ end
73
+
74
+ describe "#defined_keys" do
75
+ it "should find defined keys" do
76
+ doc.defined_keys.keys.should =~ ["_id", "defined"]
77
+ end
78
+ end
79
+ end
80
+
81
+ describe "with invalid names" do
82
+ it "should warn when key names start with an uppercase letter" do
83
+ doc = Doc {}
84
+ Kernel.should_receive(:warn).once.with(/may not start with uppercase letters/)
85
+ doc.class_eval do
86
+ key :NotConstant
87
+ end
88
+ end
89
+
90
+ it "should handle keys that start with uppercase letters by translating their first letter to lowercase" do
91
+ doc = Doc {}
92
+ Kernel.stub(:warn)
93
+ doc.class_eval do
94
+ key :NotConstant
95
+ end
96
+ doc.collection.insert("NotConstant" => "Just data!")
97
+ doc.first.notConstant.should == "Just data!"
98
+ end
99
+
100
+ it "should not create accessors for bad keys" do
101
+ doc = Doc {}
102
+ doc.should_not_receive(:create_accessors_for)
103
+ doc.class_eval do
104
+ key :"bad-name", :__dynamic => true
105
+ end
106
+ expect { doc.new.method(:"bad-name") }.to raise_error(NameError)
107
+ end
108
+
109
+ it "should create accessors for good keys" do
110
+ doc = Doc {
111
+ key :good_name
112
+ }
113
+ doc.new.good_name.should be_nil
114
+ expect { doc.new.method("good_name") }.to_not raise_error
115
+ end
116
+ end
117
+
118
+ it "should handle loading dynamic fields from the database that have bad names" do
119
+ doc = Doc {}
120
+ doc.collection.insert("foo-bar" => "baz-bin")
121
+
122
+ doc.first["foo-bar"].should == "baz-bin"
123
+ end
124
+
125
+ describe "with aliases" do
126
+ AliasedKeyModel = Doc do
127
+ key :foo, :abbr => :f
128
+ key :with_underscores, :alias => "with-hyphens"
129
+ key :field_name, :field_name => "alternate_field_name"
130
+ key :bar
131
+ end
132
+
133
+ before { AliasedKeyModel.collection.drop }
134
+
135
+ context "standard key operations" do
136
+ before do
137
+ AliasedKeyModel.create(:foo => "whee!", :bar => "whoo!")
138
+ end
139
+
140
+ it "should serialize with aliased keys" do
141
+ AliasedKeyModel.collection.find_one.keys.should =~ %w(_id f bar)
142
+
143
+ AliasedKeyModel.first.tap do |d|
144
+ d.foo.should == "whee!"
145
+ d.bar.should == "whoo!"
146
+ end
147
+ end
148
+
149
+ it "should permit querying via aliases" do
150
+ AliasedKeyModel.where(AliasedKeyModel.abbr(:f) => "whee!").first.foo.should == "whee!"
151
+ end
152
+
153
+ it "should serialize to JSON with full keys" do
154
+ AliasedKeyModel.first.as_json.tap do |json|
155
+ json.should have_key "foo"
156
+ json.should_not have_key "f"
157
+ end
158
+ end
159
+ end
160
+
161
+ context "given field which are not valid Ruby method names" do
162
+ before { AliasedKeyModel.create(:with_underscores => "foobar") }
163
+ it "should work" do
164
+ AliasedKeyModel.first.with_underscores.should == "foobar"
165
+ AliasedKeyModel.collection.find_one["with-hyphens"].should == "foobar"
166
+ end
167
+ end
168
+
169
+ context "given a field aliased with :field_name" do
170
+ before { AliasedKeyModel.create(:field_name => "foobar") }
171
+ it "should work" do
172
+ AliasedKeyModel.first.field_name.should == "foobar"
173
+ AliasedKeyModel.collection.find_one["alternate_field_name"].should == "foobar"
174
+ end
175
+ end
176
+
177
+ context "associations" do
178
+ AssociatedKeyWithAlias = Doc do
179
+ set_collection_name "associated_documents"
180
+ key :name, String, :abbr => :n
181
+ key :association_id, ObjectId, :abbr => :aid
182
+ end
183
+
184
+ OwnerDocWithKeyAliases = Doc do
185
+ set_collection_name "owner_documents"
186
+ key :name, String, :abbr => :n
187
+ many :associated_documents, :class_name => "AssociatedKeyWithAlias", :foreign_key => AssociatedKeyWithAlias.abbr(:association_id)
188
+ many :other_documents, :class_name => "EmbeddedDocWithAliases"
189
+ end
190
+
191
+ EmbeddedDocWithAliases = EDoc do
192
+ key :embedded_name, String, :abbr => :en
193
+ end
194
+
195
+ before do
196
+ AssociatedKeyWithAlias.collection.drop
197
+ OwnerDocWithKeyAliases.collection.drop
198
+ end
199
+
200
+ it "should work" do
201
+ owner = OwnerDocWithKeyAliases.create(:name => "Big Boss")
202
+
203
+ associated_documents = 3.times.map {|i| AssociatedKeyWithAlias.new(:name => "Associated Record #{i}") }
204
+ owner.associated_documents = associated_documents
205
+ owner.save
206
+
207
+ owner.reload
208
+ owner.associated_documents.to_a.should =~ associated_documents.to_a
209
+
210
+ AssociatedKeyWithAlias.collection.find_one.keys.should =~ %w(_id n aid)
211
+ end
212
+
213
+ it "should work with embedded documents" do
214
+ owner = OwnerDocWithKeyAliases.create(:name => "Big Boss")
215
+ owner.other_documents << EmbeddedDocWithAliases.new(:embedded_name => "Underling")
216
+ owner.save
217
+
218
+ owner.reload
219
+ owner.other_documents[0].embedded_name.should == "Underling"
220
+ owner.collection.find_one["other_documents"][0]["en"].should == "Underling"
221
+ end
222
+ end
223
+ end
224
+ end
@@ -1,18 +1,18 @@
1
- require 'test_helper'
1
+ require 'spec_helper'
2
2
 
3
- class LoggerTest < Test::Unit::TestCase
3
+ describe "Logger" do
4
4
  context "with connection that has logger" do
5
- setup do
5
+ before do
6
6
  @output = StringIO.new
7
7
  @logger = Logger.new(@output)
8
- MongoMapper.connection = Mongo::Connection.new('127.0.0.1', 27017, :logger => @logger)
8
+ MongoMapper.connection = Mongo::MongoClient.new('127.0.0.1', 27017, :logger => @logger)
9
9
  end
10
10
 
11
- should "be able to get access to that logger" do
11
+ it "should be able to get access to that logger" do
12
12
  MongoMapper.logger.should == @logger
13
13
  end
14
14
 
15
- should "be able to log messages" do
15
+ it "should be able to log messages" do
16
16
  MongoMapper.logger.debug 'testing'
17
17
  @output.string.include?('testing').should be_true
18
18
  end
@@ -0,0 +1,550 @@
1
+ require 'spec_helper'
2
+
3
+ module Modifiers
4
+ describe "Modifiers" do
5
+ let(:page_class_with_compound_key) {
6
+ Doc do
7
+ key :_id, BSON::OrderedHash, :default => lambda { BSON::OrderedHash['n', 42, 'i', BSON::ObjectId.new] }
8
+ key :title, String
9
+ key :day_count, Integer, :default => 0
10
+ key :week_count, Integer, :default => 0
11
+ key :month_count, Integer, :default => 0
12
+ key :tags, Array
13
+ end
14
+ }
15
+
16
+ let(:page_class_with_standard_key) {
17
+ Doc do
18
+ key :title, String
19
+ key :day_count, Integer, :default => 0
20
+ key :week_count, Integer, :default => 0
21
+ key :month_count, Integer, :default => 0
22
+ key :tags, Array
23
+ end
24
+ }
25
+
26
+ def assert_page_counts(page, day_count, week_count, month_count)
27
+ doc = page.collection.find_one({:_id => page.id})
28
+ doc.should be_present, "Could not find document"
29
+ doc.fetch('day_count').should == day_count
30
+ doc.fetch('week_count').should == week_count
31
+ doc.fetch('month_count').should == month_count
32
+ end
33
+
34
+ def assert_keys_removed(page, *keys)
35
+ page.class.collection.find_one({:_id => page.id}).tap do |doc|
36
+ doc.should be_present, "Could not find document"
37
+ (doc.keys & keys).should be_empty, "Expected to not have keys #{keys.inspect}, got #{(keys & doc.keys).inspect}"
38
+ end
39
+ end
40
+
41
+ context "ClassMethods" do
42
+ let!(:page_class) { page_class_with_standard_key }
43
+ let!(:page) { page_class.create(:title => 'Home') }
44
+ let!(:page2) { page_class.create(:title => 'Home') }
45
+
46
+ context "unset" do
47
+ let!(:page) { page_class.create(:title => 'Home', :tags => %w(foo bar)) }
48
+
49
+ it "should work with criteria and keys" do
50
+ page_class.unset({:title => 'Home'}, :title, :tags)
51
+ assert_keys_removed page, :title, :tags
52
+ assert_keys_removed page2, :title, :tags
53
+ end
54
+
55
+ it "should work with ids and keys" do
56
+ page_class.unset(page.id, page2.id, :title, :tags)
57
+ assert_keys_removed page, :title, :tags
58
+ assert_keys_removed page2, :title, :tags
59
+ end
60
+
61
+ context "additional options (upsert & safe)" do
62
+ it "should be able to pass upsert option" do
63
+ new_key_value = DateTime.now.to_s
64
+ page_class.unset({:title => new_key_value, :tags => %w(foo bar)}, :tags, {:upsert => true})
65
+ page_class.count(:title => new_key_value).should == 1
66
+ page_class.first(:title => new_key_value).tags.should == []
67
+ end
68
+
69
+ it "should be able to pass safe option" do
70
+ page_class.create(:title => "Better Be Safe than Sorry")
71
+
72
+ Mongo::Collection.any_instance.should_receive(:update).with(
73
+ {:title => "Better Be Safe than Sorry"},
74
+ {'$unset' => {:tags => 1}},
75
+ {:w => 1, :multi => true}
76
+ )
77
+ page_class.unset({:title => "Better Be Safe than Sorry"}, :tags, {:w => 1})
78
+ end
79
+
80
+ it "should be able to pass both safe and upsert options" do
81
+ new_key_value = DateTime.now.to_s
82
+ page_class.unset({:title => new_key_value, :tags => %w(foo bar)}, :tags, {:upsert => true, :safe => true})
83
+ page_class.count(:title => new_key_value).should == 1
84
+ page_class.first(:title => new_key_value).tags.should == []
85
+ end
86
+ end
87
+ end
88
+
89
+ context "increment" do
90
+ it "should work with criteria and modifier hashes" do
91
+ page_class.increment({:title => 'Home'}, :day_count => 1, :week_count => 2, :month_count => 3)
92
+
93
+ assert_page_counts page, 1, 2, 3
94
+ assert_page_counts page2, 1, 2, 3
95
+ end
96
+
97
+ it "should work with ids and modifier hash" do
98
+ page_class.increment(page.id, page2.id, :day_count => 1, :week_count => 2, :month_count => 3)
99
+
100
+ assert_page_counts page, 1, 2, 3
101
+ assert_page_counts page2, 1, 2, 3
102
+ end
103
+
104
+ it "should work with ids given as strings" do
105
+ page_class.increment(page.id.to_s, page2.id.to_s, :day_count => 1, :week_count => 2, :month_count => 3)
106
+
107
+ assert_page_counts page, 1, 2, 3
108
+ assert_page_counts page2, 1, 2, 3
109
+ end
110
+ end
111
+
112
+ context "decrement" do
113
+ let!(:page) { page_class.create(:title => 'Home', :day_count => 1, :week_count => 2, :month_count => 3) }
114
+ let!(:page2) { page_class.create(:title => 'Home', :day_count => 1, :week_count => 2, :month_count => 3) }
115
+
116
+ it "should work with criteria and modifier hashes" do
117
+ page_class.decrement({:title => 'Home'}, :day_count => 1, :week_count => 2, :month_count => 3)
118
+
119
+ assert_page_counts page, 0, 0, 0
120
+ assert_page_counts page2, 0, 0, 0
121
+ end
122
+
123
+ it "should work with ids and modifier hash" do
124
+ page_class.decrement(page.id, page2.id, :day_count => 1, :week_count => 2, :month_count => 3)
125
+
126
+ assert_page_counts page, 0, 0, 0
127
+ assert_page_counts page2, 0, 0, 0
128
+ end
129
+
130
+ it "should decrement with positive or negative numbers" do
131
+ page_class.decrement(page.id, page2.id, :day_count => -1, :week_count => 2, :month_count => -3)
132
+
133
+ assert_page_counts page, 0, 0, 0
134
+ assert_page_counts page2, 0, 0, 0
135
+ end
136
+
137
+ it "should work with ids given as strings" do
138
+ page_class.decrement(page.id.to_s, page2.id.to_s, :day_count => -1, :week_count => 2, :month_count => -3)
139
+
140
+ assert_page_counts page, 0, 0, 0
141
+ assert_page_counts page2, 0, 0, 0
142
+ end
143
+ end
144
+
145
+ context "set" do
146
+ it "should work with criteria and modifier hashes" do
147
+ page_class.set({:title => 'Home'}, :title => 'Home Revised')
148
+
149
+ page.reload
150
+ page.title.should == 'Home Revised'
151
+
152
+ page2.reload
153
+ page2.title.should == 'Home Revised'
154
+ end
155
+
156
+ it "should work with ids and modifier hash" do
157
+ page_class.set(page.id, page2.id, :title => 'Home Revised')
158
+
159
+ page.reload
160
+ page.title.should == 'Home Revised'
161
+
162
+ page2.reload
163
+ page2.title.should == 'Home Revised'
164
+ end
165
+
166
+ it "should typecast values before querying" do
167
+ page_class.key :tags, Set
168
+
169
+ expect {
170
+ page_class.set(page.id, :tags => ['foo', 'bar'].to_set)
171
+ page.reload
172
+ page.tags.should == Set.new(['foo', 'bar'])
173
+ }.to_not raise_error
174
+ end
175
+
176
+ it "should not typecast keys that are not defined in document" do
177
+ expect {
178
+ page_class.set(page.id, :colors => ['red', 'green'].to_set)
179
+ }.to raise_error(BSON::InvalidDocument)
180
+ end
181
+
182
+ it "should set keys that are not defined in document" do
183
+ page_class.set(page.id, :colors => %w[red green])
184
+ page.reload
185
+ page[:colors].should == %w[red green]
186
+ end
187
+
188
+ context "additional options (upsert & safe)" do
189
+ it "should be able to pass upsert option" do
190
+ new_key_value = DateTime.now.to_s
191
+ page_class.set({:title => new_key_value}, {:day_count => 1}, {:upsert => true})
192
+ page_class.count(:title => new_key_value).should == 1
193
+ page_class.first(:title => new_key_value).day_count.should == 1
194
+ end
195
+
196
+ it "should be able to pass safe option" do
197
+ page_class.create(:title => "Better Be Safe than Sorry")
198
+
199
+ Mongo::Collection.any_instance.should_receive(:update).with(
200
+ {:title => "Better Be Safe than Sorry"},
201
+ {'$set' => {:title => "I like safety."}},
202
+ {:w => 1, :multi => true}
203
+ )
204
+ page_class.set({:title => "Better Be Safe than Sorry"}, {:title => "I like safety."}, {:safe => true})
205
+ end
206
+
207
+ it "should be able to pass both safe and upsert options" do
208
+ new_key_value = DateTime.now.to_s
209
+ page_class.set({:title => new_key_value}, {:day_count => 1}, {:upsert => true, :safe => true})
210
+ page_class.count(:title => new_key_value).should == 1
211
+ page_class.first(:title => new_key_value).day_count.should == 1
212
+ end
213
+ end
214
+ end
215
+
216
+ context "push" do
217
+ it "should work with criteria and modifier hashes" do
218
+ page_class.push({:title => 'Home'}, :tags => 'foo')
219
+
220
+ page.reload
221
+ page.tags.should == %w(foo)
222
+
223
+ page2.reload
224
+ page2.tags.should == %w(foo)
225
+ end
226
+
227
+ it "should work with ids and modifier hash" do
228
+ page_class.push(page.id, page2.id, :tags => 'foo')
229
+
230
+ page.reload
231
+ page.tags.should == %w(foo)
232
+
233
+ page2.reload
234
+ page2.tags.should == %w(foo)
235
+ end
236
+ end
237
+
238
+ context "push_all" do
239
+ let(:tags) { %w(foo bar) }
240
+
241
+ it "should work with criteria and modifier hashes" do
242
+ page_class.push_all({:title => 'Home'}, :tags => tags)
243
+
244
+ page.reload
245
+ page.tags.should == tags
246
+
247
+ page2.reload
248
+ page2.tags.should == tags
249
+ end
250
+
251
+ it "should work with ids and modifier hash" do
252
+ page_class.push_all(page.id, page2.id, :tags => tags)
253
+
254
+ page.reload
255
+ page.tags.should == tags
256
+
257
+ page2.reload
258
+ page2.tags.should == tags
259
+ end
260
+ end
261
+
262
+ context "pull" do
263
+ let(:page) { page_class.create(:title => 'Home', :tags => %w(foo bar)) }
264
+ let(:page2) { page_class.create(:title => 'Home', :tags => %w(foo bar)) }
265
+
266
+ it "should work with criteria and modifier hashes" do
267
+ page_class.pull({:title => 'Home'}, :tags => 'foo')
268
+
269
+ page.reload
270
+ page.tags.should == %w(bar)
271
+
272
+ page2.reload
273
+ page2.tags.should == %w(bar)
274
+ end
275
+
276
+ it "should be able to pull with ids and modifier hash" do
277
+ page_class.pull(page.id, page2.id, :tags => 'foo')
278
+
279
+ page.reload
280
+ page.tags.should == %w(bar)
281
+
282
+ page2.reload
283
+ page2.tags.should == %w(bar)
284
+ end
285
+ end
286
+
287
+ context "pull_all" do
288
+ let(:page) { page_class.create(:title => 'Home', :tags => %w(foo bar baz)) }
289
+ let(:page2) { page_class.create(:title => 'Home', :tags => %w(foo bar baz)) }
290
+
291
+ it "should work with criteria and modifier hashes" do
292
+ page_class.pull_all({:title => 'Home'}, :tags => %w(foo bar))
293
+
294
+ page.reload
295
+ page.tags.should == %w(baz)
296
+
297
+ page2.reload
298
+ page2.tags.should == %w(baz)
299
+ end
300
+
301
+ it "should work with ids and modifier hash" do
302
+ page_class.pull_all(page.id, page2.id, :tags => %w(foo bar))
303
+
304
+ page.reload
305
+ page.tags.should == %w(baz)
306
+
307
+ page2.reload
308
+ page2.tags.should == %w(baz)
309
+ end
310
+ end
311
+
312
+ context "add_to_set" do
313
+ let(:page) { page_class.create(:title => 'Home', :tags => 'foo') }
314
+
315
+ it "should be able to add to set with criteria and modifier hash" do
316
+ page_class.add_to_set({:title => 'Home'}, :tags => 'foo')
317
+
318
+ page.reload
319
+ page.tags.should == %w(foo)
320
+
321
+ page2.reload
322
+ page2.tags.should == %w(foo)
323
+ end
324
+
325
+ it "should be able to add to set with ids and modifier hash" do
326
+ page_class.add_to_set(page.id, page2.id, :tags => 'foo')
327
+
328
+ page.reload
329
+ page.tags.should == %w(foo)
330
+
331
+ page2.reload
332
+ page2.tags.should == %w(foo)
333
+ end
334
+ end
335
+
336
+ context "push_uniq" do
337
+ let(:page) { page_class.create(:title => 'Home', :tags => 'foo') }
338
+
339
+ it "should be able to push uniq with criteria and modifier hash" do
340
+ page_class.push_uniq({:title => 'Home'}, :tags => 'foo')
341
+
342
+ page.reload
343
+ page.tags.should == %w(foo)
344
+
345
+ page2.reload
346
+ page2.tags.should == %w(foo)
347
+ end
348
+
349
+ it "should be able to push uniq with ids and modifier hash" do
350
+ page_class.push_uniq(page.id, page2.id, :tags => 'foo')
351
+
352
+ page.reload
353
+ page.tags.should == %w(foo)
354
+
355
+ page2.reload
356
+ page2.tags.should == %w(foo)
357
+ end
358
+ end
359
+
360
+ context "pop" do
361
+ let(:page) { page_class.create(:title => 'Home', :tags => %w(foo bar)) }
362
+
363
+ it "should be able to remove the last element the array" do
364
+ page_class.pop(page.id, :tags => 1)
365
+ page.reload
366
+ page.tags.should == %w(foo)
367
+ end
368
+
369
+ it "should be able to remove the first element of the array" do
370
+ page_class.pop(page.id, :tags => -1)
371
+ page.reload
372
+ page.tags.should == %w(bar)
373
+ end
374
+ end
375
+
376
+ context "additional options (upsert & safe)" do
377
+ it "should be able to pass upsert option" do
378
+ new_key_value = DateTime.now.to_s
379
+ page_class.increment({:title => new_key_value}, {:day_count => 1}, {:upsert => true})
380
+ page_class.count(:title => new_key_value).should == 1
381
+ page_class.first(:title => new_key_value).day_count.should == 1
382
+ end
383
+
384
+ it "should be able to pass safe option" do
385
+ page_class.create(:title => "Better Be Safe than Sorry")
386
+
387
+ # We are trying to increment a key of type string here which should fail
388
+ expect {
389
+ page_class.increment({:title => "Better Be Safe than Sorry"}, {:title => 1}, {:safe => true})
390
+ }.to raise_error(Mongo::OperationFailure)
391
+ end
392
+
393
+ it "should be able to pass both safe and upsert options" do
394
+ new_key_value = DateTime.now.to_s
395
+ page_class.increment({:title => new_key_value}, {:day_count => 1}, {:upsert => true, :safe => true})
396
+ page_class.count(:title => new_key_value).should == 1
397
+ page_class.first(:title => new_key_value).day_count.should == 1
398
+ end
399
+ end
400
+ end
401
+
402
+ context "compound keys" do
403
+ it "should create a document" do
404
+ expect {
405
+ page_class_with_compound_key.create(:title => 'Foo', :tags => %w(foo))
406
+ }.to change { page_class_with_compound_key.count }.by(1)
407
+ doc = page_class_with_compound_key.first
408
+ page_class_with_compound_key.find(doc._id).should == doc
409
+ end
410
+ end
411
+
412
+ context "instance methods" do
413
+ {
414
+ :page_class_with_standard_key => "with standard key",
415
+ :page_class_with_compound_key => "with compound key",
416
+ }.each do |klass, description|
417
+ context description do
418
+ let!(:page_class) { send(klass) }
419
+
420
+ it "should be able to unset with keys" do
421
+ page = page_class.create(:title => 'Foo', :tags => %w(foo))
422
+ page.unset(:title, :tags)
423
+ assert_keys_removed page, :title, :tags
424
+ end
425
+
426
+ it "should be able to increment with modifier hashes" do
427
+ page = page_class.create
428
+ page.increment(:day_count => 1, :week_count => 2, :month_count => 3)
429
+
430
+ assert_page_counts page, 1, 2, 3
431
+ end
432
+
433
+ it "should be able to decrement with modifier hashes" do
434
+ page = page_class.create(:day_count => 1, :week_count => 2, :month_count => 3)
435
+ page.decrement(:day_count => 1, :week_count => 2, :month_count => 3)
436
+
437
+ assert_page_counts page, 0, 0, 0
438
+ end
439
+
440
+ it "should always decrement when decrement is called whether number is positive or negative" do
441
+ page = page_class.create(:day_count => 1, :week_count => 2, :month_count => 3)
442
+ page.decrement(:day_count => -1, :week_count => 2, :month_count => -3)
443
+ assert_page_counts page, 0, 0, 0
444
+ end
445
+
446
+ it "should be able to set with modifier hashes" do
447
+ page = page_class.create(:title => 'Home')
448
+ page.set(:title => 'Home Revised')
449
+
450
+ page.reload
451
+ page.title.should == 'Home Revised'
452
+ end
453
+
454
+ it "should be able to push with modifier hashes" do
455
+ page = page_class.create
456
+ page.push(:tags => 'foo')
457
+
458
+ page.reload
459
+ page.tags.should == %w(foo)
460
+ end
461
+
462
+ it "should be able to push_all with modifier hashes" do
463
+ page = page_class.create
464
+ page.push_all(:tags => %w(foo bar))
465
+
466
+ page.reload
467
+ page.tags.should == %w(foo bar)
468
+ end
469
+
470
+ it "should be able to pull with criteria and modifier hashes" do
471
+ page = page_class.create(:tags => %w(foo bar))
472
+ page.pull(:tags => 'foo')
473
+
474
+ page.reload
475
+ page.tags.should == %w(bar)
476
+ end
477
+
478
+ it "should be able to pull_all with criteria and modifier hashes" do
479
+ page = page_class.create(:tags => %w(foo bar baz))
480
+ page.pull_all(:tags => %w(foo bar))
481
+
482
+ page.reload
483
+ page.tags.should == %w(baz)
484
+ end
485
+
486
+ it "should be able to add_to_set with criteria and modifier hash" do
487
+ page = page_class.create(:tags => 'foo')
488
+ page2 = page_class.create
489
+
490
+ page.add_to_set(:tags => 'foo')
491
+ page2.add_to_set(:tags => 'foo')
492
+
493
+ page.reload
494
+ page.tags.should == %w(foo)
495
+
496
+ page2.reload
497
+ page2.tags.should == %w(foo)
498
+ end
499
+
500
+ it "should be able to push uniq with criteria and modifier hash" do
501
+ page = page_class.create(:tags => 'foo')
502
+ page2 = page_class.create
503
+
504
+ page.push_uniq(:tags => 'foo')
505
+ page2.push_uniq(:tags => 'foo')
506
+
507
+ page.reload
508
+ page.tags.should == %w(foo)
509
+
510
+ page2.reload
511
+ page2.tags.should == %w(foo)
512
+ end
513
+
514
+ it "should be able to pop with modifier hashes" do
515
+ page = page_class.create(:tags => %w(foo bar))
516
+ page.pop(:tags => 1)
517
+
518
+ page.reload
519
+ page.tags.should == %w(foo)
520
+ end
521
+
522
+ it "should be able to pass upsert option" do
523
+ page = page_class.create(:title => "Upsert Page")
524
+ page.increment({:new_count => 1}, {:upsert => true})
525
+
526
+ page.reload
527
+ page.new_count.should == 1
528
+ end
529
+
530
+ it "should be able to pass safe option" do
531
+ page = page_class.create(:title => "Safe Page")
532
+
533
+ # We are trying to increment a key of type string here which should fail
534
+ expect {
535
+ page.increment({:title => 1}, {:safe => true})
536
+ }.to raise_error(Mongo::OperationFailure)
537
+ end
538
+
539
+ it "should be able to pass upsert and safe options" do
540
+ page = page_class.create(:title => "Upsert and Safe Page")
541
+ page.increment({:another_count => 1}, {:upsert => true, :safe => true})
542
+
543
+ page.reload
544
+ page.another_count.should == 1
545
+ end
546
+ end
547
+ end
548
+ end
549
+ end
550
+ end