mongo_doc_rails2 0.6.1

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 (142) hide show
  1. data/.document +5 -0
  2. data/.gitignore +8 -0
  3. data/HISTORY.md +11 -0
  4. data/LICENSE +20 -0
  5. data/README.textile +185 -0
  6. data/Rakefile +188 -0
  7. data/TODO +40 -0
  8. data/VERSION +1 -0
  9. data/data/.gitignore +2 -0
  10. data/examples/simple_document.rb +46 -0
  11. data/examples/simple_object.rb +34 -0
  12. data/features/collections.feature +9 -0
  13. data/features/embed_hash.feature +16 -0
  14. data/features/finders.feature +76 -0
  15. data/features/indexes.feature +28 -0
  16. data/features/mongodb.yml +7 -0
  17. data/features/mongodoc_base.feature +128 -0
  18. data/features/new_record.feature +36 -0
  19. data/features/partial_updates.feature +95 -0
  20. data/features/removing_documents.feature +68 -0
  21. data/features/saving_an_object.feature +15 -0
  22. data/features/scopes.feature +66 -0
  23. data/features/step_definitions/collection_steps.rb +17 -0
  24. data/features/step_definitions/document_steps.rb +149 -0
  25. data/features/step_definitions/documents.rb +40 -0
  26. data/features/step_definitions/embed_hash_steps.rb +6 -0
  27. data/features/step_definitions/finder_steps.rb +15 -0
  28. data/features/step_definitions/index_steps.rb +10 -0
  29. data/features/step_definitions/json_steps.rb +9 -0
  30. data/features/step_definitions/object_steps.rb +50 -0
  31. data/features/step_definitions/objects.rb +24 -0
  32. data/features/step_definitions/partial_update_steps.rb +31 -0
  33. data/features/step_definitions/query_steps.rb +66 -0
  34. data/features/step_definitions/removing_documents_steps.rb +14 -0
  35. data/features/step_definitions/scope_steps.rb +18 -0
  36. data/features/step_definitions/string_casting_steps.rb +29 -0
  37. data/features/step_definitions/util_steps.rb +7 -0
  38. data/features/string_casting.feature +10 -0
  39. data/features/support/support.rb +10 -0
  40. data/features/using_criteria.feature +142 -0
  41. data/lib/mongo_doc.rb +12 -0
  42. data/lib/mongo_doc/associations.rb +109 -0
  43. data/lib/mongo_doc/associations/collection_proxy.rb +121 -0
  44. data/lib/mongo_doc/associations/document_proxy.rb +65 -0
  45. data/lib/mongo_doc/associations/hash_proxy.rb +102 -0
  46. data/lib/mongo_doc/associations/proxy_base.rb +48 -0
  47. data/lib/mongo_doc/attributes.rb +84 -0
  48. data/lib/mongo_doc/bson.rb +31 -0
  49. data/lib/mongo_doc/collection.rb +82 -0
  50. data/lib/mongo_doc/connection.rb +88 -0
  51. data/lib/mongo_doc/contexts.rb +31 -0
  52. data/lib/mongo_doc/contexts/ids.rb +41 -0
  53. data/lib/mongo_doc/contexts/mongo.rb +272 -0
  54. data/lib/mongo_doc/criteria.rb +70 -0
  55. data/lib/mongo_doc/cursor.rb +32 -0
  56. data/lib/mongo_doc/document.rb +205 -0
  57. data/lib/mongo_doc/ext.rb +16 -0
  58. data/lib/mongo_doc/ext/array.rb +5 -0
  59. data/lib/mongo_doc/ext/binary.rb +7 -0
  60. data/lib/mongo_doc/ext/boolean_class.rb +17 -0
  61. data/lib/mongo_doc/ext/date.rb +19 -0
  62. data/lib/mongo_doc/ext/date_time.rb +17 -0
  63. data/lib/mongo_doc/ext/dbref.rb +7 -0
  64. data/lib/mongo_doc/ext/hash.rb +7 -0
  65. data/lib/mongo_doc/ext/min_max_keys.rb +13 -0
  66. data/lib/mongo_doc/ext/nil_class.rb +5 -0
  67. data/lib/mongo_doc/ext/numeric.rb +17 -0
  68. data/lib/mongo_doc/ext/object.rb +19 -0
  69. data/lib/mongo_doc/ext/object_id.rb +7 -0
  70. data/lib/mongo_doc/ext/regexp.rb +5 -0
  71. data/lib/mongo_doc/ext/string.rb +5 -0
  72. data/lib/mongo_doc/ext/symbol.rb +5 -0
  73. data/lib/mongo_doc/ext/time.rb +9 -0
  74. data/lib/mongo_doc/finders.rb +38 -0
  75. data/lib/mongo_doc/index.rb +46 -0
  76. data/lib/mongo_doc/matchers.rb +35 -0
  77. data/lib/mongo_doc/root.rb +26 -0
  78. data/lib/mongo_doc/scope.rb +64 -0
  79. data/lib/mongo_doc/validations.rb +12 -0
  80. data/lib/mongo_doc/validations/macros.rb +11 -0
  81. data/lib/mongo_doc/validations/validates_embedded.rb +13 -0
  82. data/lib/mongoid/contexts/enumerable.rb +151 -0
  83. data/lib/mongoid/contexts/paging.rb +42 -0
  84. data/lib/mongoid/criteria.rb +239 -0
  85. data/lib/mongoid/criterion/complex.rb +21 -0
  86. data/lib/mongoid/criterion/exclusion.rb +65 -0
  87. data/lib/mongoid/criterion/inclusion.rb +93 -0
  88. data/lib/mongoid/criterion/optional.rb +136 -0
  89. data/lib/mongoid/extensions/hash/criteria_helpers.rb +20 -0
  90. data/lib/mongoid/extensions/symbol/inflections.rb +36 -0
  91. data/lib/mongoid/matchers/all.rb +11 -0
  92. data/lib/mongoid/matchers/default.rb +26 -0
  93. data/lib/mongoid/matchers/exists.rb +13 -0
  94. data/lib/mongoid/matchers/gt.rb +11 -0
  95. data/lib/mongoid/matchers/gte.rb +11 -0
  96. data/lib/mongoid/matchers/in.rb +11 -0
  97. data/lib/mongoid/matchers/lt.rb +11 -0
  98. data/lib/mongoid/matchers/lte.rb +11 -0
  99. data/lib/mongoid/matchers/ne.rb +11 -0
  100. data/lib/mongoid/matchers/nin.rb +11 -0
  101. data/lib/mongoid/matchers/size.rb +11 -0
  102. data/mongo_doc_rails2.gemspec +237 -0
  103. data/mongod.example.yml +2 -0
  104. data/mongodb.example.yml +14 -0
  105. data/perf/mongo_doc_object.rb +83 -0
  106. data/perf/mongo_document.rb +84 -0
  107. data/perf/ruby_driver.rb +49 -0
  108. data/script/console +8 -0
  109. data/spec/array_including_argument_matcher.rb +62 -0
  110. data/spec/associations/collection_proxy_spec.rb +233 -0
  111. data/spec/associations/document_proxy_spec.rb +45 -0
  112. data/spec/associations/hash_proxy_spec.rb +181 -0
  113. data/spec/associations/proxy_base_spec.rb +92 -0
  114. data/spec/associations_spec.rb +218 -0
  115. data/spec/attributes_accessor_spec.rb +33 -0
  116. data/spec/attributes_spec.rb +145 -0
  117. data/spec/bson_matchers.rb +54 -0
  118. data/spec/bson_spec.rb +196 -0
  119. data/spec/collection_spec.rb +169 -0
  120. data/spec/connection_spec.rb +147 -0
  121. data/spec/contexts/ids_spec.rb +49 -0
  122. data/spec/contexts/mongo_spec.rb +235 -0
  123. data/spec/contexts_spec.rb +56 -0
  124. data/spec/criteria_spec.rb +69 -0
  125. data/spec/cursor_spec.rb +91 -0
  126. data/spec/document_ext.rb +9 -0
  127. data/spec/document_spec.rb +553 -0
  128. data/spec/embedded_save_spec.rb +73 -0
  129. data/spec/ext_spec.rb +89 -0
  130. data/spec/finders_spec.rb +61 -0
  131. data/spec/hash_matchers.rb +27 -0
  132. data/spec/index_spec.rb +79 -0
  133. data/spec/matchers_spec.rb +342 -0
  134. data/spec/mongodb.yml +6 -0
  135. data/spec/mongodb_pairs.yml +8 -0
  136. data/spec/new_record_spec.rb +128 -0
  137. data/spec/root_spec.rb +41 -0
  138. data/spec/scope_spec.rb +79 -0
  139. data/spec/spec.opts +2 -0
  140. data/spec/spec_helper.rb +14 -0
  141. data/spec/validations_spec.rb +30 -0
  142. metadata +346 -0
@@ -0,0 +1,147 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ # Resets for testing
4
+ module MongoDoc
5
+ module Connection
6
+
7
+ def reset
8
+ @config_path = nil
9
+ @configuration = nil
10
+ @connection = nil
11
+ @database = nil
12
+ @host = nil
13
+ @name = nil
14
+ @options = nil
15
+ @port = nil
16
+ @strict = nil
17
+ end
18
+
19
+ end
20
+ end
21
+
22
+ describe "MongoDoc::Connection.Connections" do
23
+
24
+ context "Non-rails environment" do
25
+
26
+ it "does not see Rails" do
27
+ Object.const_defined?('Rails').should be_false
28
+ end
29
+
30
+ it "default the configuration location to './mongodb.yml'" do
31
+ MongoDoc::Connection.config_path.should == './mongodb.yml'
32
+ end
33
+
34
+ context "without a configuration" do
35
+ let(:connection) { stub('connection') }
36
+
37
+ before do
38
+ MongoDoc::Connection.reset
39
+ MongoDoc::Connection.stub(:connect).and_return(connection)
40
+ end
41
+
42
+ it "creates a default connection" do
43
+ MongoDoc::Connection.should_receive(:connect).and_return(connection)
44
+ MongoDoc::Connection.connection
45
+ end
46
+
47
+ it "creates a default database with strict false" do
48
+ connection.should_receive(:db).with("mongo_doc", :strict => false)
49
+ MongoDoc::Connection.database
50
+ end
51
+ end
52
+ end
53
+
54
+ context "Rails environment" do
55
+
56
+ module FauxRails
57
+ extend self
58
+
59
+ def env
60
+ 'development'
61
+ end
62
+
63
+ def root
64
+ Pathname.new('rails_root')
65
+ end
66
+ end
67
+
68
+ before do
69
+ Object.const_set('Rails', FauxRails)
70
+ end
71
+
72
+ after do
73
+ Object.send(:remove_const, 'Rails')
74
+ end
75
+
76
+ it "sees Rails" do
77
+ Object.const_defined?('Rails').should be_true
78
+ end
79
+
80
+ it "default the configuration location to Rails.root + '/config/mongodb.yml'" do
81
+ MongoDoc::Connection.config_path.should == FauxRails.root + 'config/mongodb.yml'
82
+ end
83
+
84
+ context "without a configuration" do
85
+ let(:connection) { stub('connection') }
86
+
87
+ before do
88
+ MongoDoc::Connection.reset
89
+ MongoDoc::Connection.stub(:connect).and_return(connection)
90
+ end
91
+
92
+ it "creates a default connection" do
93
+ MongoDoc::Connection.should_receive(:connect).and_return(connection)
94
+ MongoDoc::Connection.connection
95
+ end
96
+
97
+ it "creates a default database with strict false" do
98
+ connection.should_receive(:db).with("rails_root_development", :strict => false)
99
+ MongoDoc::Connection.database
100
+ end
101
+ end
102
+ end
103
+
104
+ context ".verify_server_version" do
105
+ let(:connection) { stub('connection') }
106
+
107
+ it "raises when the server version is unsupported" do
108
+ connection.stub(:server_version).and_return(Mongo::ServerVersion.new('1.3.4'))
109
+ lambda { MongoDoc::Connection.send(:verify_server_version, connection) }.should raise_error(MongoDoc::UnsupportedServerVersionError)
110
+ end
111
+
112
+ it "returns when the server version is supported" do
113
+ connection.stub(:server_version).and_return(Mongo::ServerVersion.new('1.4.0'))
114
+ lambda { MongoDoc::Connection.send(:verify_server_version, connection) }.should_not raise_error(MongoDoc::UnsupportedServerVersionError)
115
+ end
116
+ end
117
+
118
+ describe ".connect" do
119
+ let(:connection) { stub('connection') }
120
+
121
+ before do
122
+ MongoDoc::Connection.stub(:verify_server_version)
123
+ end
124
+
125
+ it "creates a Mongo::Connection" do
126
+ host = 'host'
127
+ port = 'port'
128
+ options = 'options'
129
+ MongoDoc::Connection.stub(:host).and_return(host)
130
+ MongoDoc::Connection.stub(:port).and_return(port)
131
+ MongoDoc::Connection.stub(:options).and_return(options)
132
+ Mongo::Connection.should_receive(:new).with(host, port, options).and_return(connection)
133
+ MongoDoc::Connection.send(:connect)
134
+ end
135
+
136
+ it "raises NoConnectionError if the connection fails" do
137
+ Mongo::Connection.stub(:new).and_return(nil)
138
+ lambda { MongoDoc::Connection.send(:connect) }.should raise_error(MongoDoc::NoConnectionError)
139
+ end
140
+
141
+ it "verifies the connection version" do
142
+ Mongo::Connection.stub(:new).and_return(connection)
143
+ MongoDoc::Connection.should_receive(:verify_server_version)
144
+ MongoDoc::Connection.send(:connect)
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ describe "MongoDoc::Contexts::Ids" do
4
+
5
+ class Address
6
+ include MongoDoc::Document
7
+ include MongoDoc::Matchers
8
+
9
+ attr_accessor :number
10
+ attr_accessor :street
11
+ end
12
+
13
+ let(:criteria) { Mongoid::Criteria.new(Address) }
14
+ let(:context) { criteria.context }
15
+
16
+ context "#id_criteria" do
17
+ context "single id" do
18
+ let(:id) { 'a' * 24 }
19
+ let(:obj_id) { BSON::ObjectID.from_string(id) }
20
+
21
+ it "converts strings to an object id" do
22
+ criteria.should_receive(:id).with(obj_id)
23
+ context.stub(:one)
24
+ context.id_criteria(id)
25
+ end
26
+
27
+ it "delegates to one if passed a string or ObjectID" do
28
+ context.should_receive(:one)
29
+ context.id_criteria(id)
30
+ end
31
+ end
32
+
33
+ context "mutliple ids" do
34
+ let(:ids) { ['a' * 24, 'b' * 24] }
35
+ let(:obj_ids) { [BSON::ObjectID.from_string(ids.first), BSON::ObjectID.from_string(ids.last)] }
36
+
37
+ it "converts strings to an object id" do
38
+ criteria.should_receive(:id).with(obj_ids)
39
+ criteria.stub(:entries)
40
+ context.id_criteria(ids)
41
+ end
42
+
43
+ it "delegates to entries if passed an array" do
44
+ criteria.should_receive(:entries)
45
+ context.id_criteria(ids)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,235 @@
1
+ require 'spec_helper'
2
+
3
+ describe "MongoDoc::Contexts::Mongo" do
4
+
5
+ class Address
6
+ include MongoDoc::Document
7
+ include MongoDoc::Matchers
8
+
9
+ attr_accessor :number
10
+ attr_accessor :street
11
+ end
12
+
13
+ let(:criteria) { Mongoid::Criteria.new(Address) }
14
+ let(:context) { criteria.context }
15
+
16
+ context "#initialize" do
17
+ it "sets the criteria" do
18
+ context.criteria.should == criteria
19
+ end
20
+ end
21
+
22
+ context "#collection" do
23
+ it "delegates to klass" do
24
+ klass = mock('klass', :collection => true)
25
+ context.should_receive(:klass).and_return(klass)
26
+ context.collection
27
+ end
28
+ end
29
+
30
+ context "querying" do
31
+ let(:collection) { stub('collection') }
32
+
33
+ before { context.stub(:collection).and_return(collection) }
34
+
35
+ context "#aggregate" do
36
+ it "uses group with the appropriate JS" do
37
+ collection.should_receive(:group).with(nil, {}, {:count=>0}, MongoDoc::Contexts::Mongo::AGGREGATE_REDUCE, true)
38
+ context.aggregate
39
+ end
40
+ end
41
+
42
+ context "#avg" do
43
+ it "is the sum/count" do
44
+ context.should_receive(:count).and_return(1)
45
+ context.should_receive(:sum).with(:field_name).and_return(1)
46
+ context.avg(:field_name)
47
+ end
48
+
49
+ it "returns nil if there is no sum" do
50
+ context.stub(:sum).and_return(nil)
51
+ context.avg(:field_name).should be_nil
52
+ end
53
+ end
54
+
55
+ context "#count" do
56
+ it "uses find and count" do
57
+ result = stub('result')
58
+ result.should_receive(:count)
59
+ collection.should_receive(:find).and_return(result)
60
+ context.count
61
+ end
62
+ end
63
+
64
+ context "#distinct" do
65
+ it "uses distinct" do
66
+ collection.should_receive(:distinct).with(:field_name, {})
67
+ context.distinct(:field_name)
68
+ end
69
+ end
70
+
71
+ context "#execute" do
72
+ it "uses find" do
73
+ collection.should_receive(:find)
74
+ context.execute
75
+ end
76
+
77
+ it "returns [] if nothing returned from find" do
78
+ collection.stub(:find => nil)
79
+ context.execute.should == []
80
+ end
81
+
82
+ it "returns the cursor if one is returned from find" do
83
+ cursor = stub('cursor')
84
+ collection.stub(:find => cursor)
85
+ context.execute.should == cursor
86
+ end
87
+
88
+ it "memoizes the count if paginating" do
89
+ count = 20
90
+ cursor = stub('cursor', :count => count)
91
+ collection.stub(:find => cursor)
92
+ context.execute
93
+ context.count.should == count
94
+ end
95
+ end
96
+
97
+ context "#group" do
98
+ it "uses group with the appropriate JS" do
99
+ collection.should_receive(:group).with(nil, {}, {:group=>[]}, MongoDoc::Contexts::Mongo::GROUP_REDUCE, true).and_return([])
100
+ context.group
101
+ end
102
+
103
+ it "decodes returned documents" do
104
+ doc = stub('doc')
105
+ collection.stub(:group).and_return([{:group => [doc]}])
106
+ MongoDoc::BSON.should_receive(:decode).and_return(doc)
107
+ context.group
108
+ end
109
+ end
110
+
111
+ context "#iterate" do
112
+
113
+ it "delegates to caching if cached" do
114
+ context.should_receive(:caching)
115
+ criteria.cache
116
+ context.iterate
117
+ end
118
+
119
+ it "iterates over the results of execute" do
120
+ item = stub('item')
121
+ context.stub(:execute).and_return([item])
122
+ context.iterate do |doc|
123
+ doc.should == item
124
+ end
125
+ end
126
+
127
+ context "#caching" do
128
+ let(:item) { stub('item') }
129
+
130
+ before do
131
+ criteria.cache
132
+ end
133
+
134
+ context "when not previously cached" do
135
+ it "iterates over the results of execute" do
136
+ context.stub(:execute).and_return([item])
137
+ context.iterate do |doc|
138
+ doc.should == item
139
+ end
140
+ end
141
+
142
+ it "memoizes the result" do
143
+ context.stub(:execute).and_return([item])
144
+ context.iterate
145
+ context.cache.should == [item]
146
+ end
147
+ end
148
+
149
+ context "when previously cached" do
150
+ before do
151
+ context.instance_variable_set(:@cache, [item])
152
+ end
153
+
154
+ it "does not execute" do
155
+ context.should_not_receive(:execute)
156
+ context.iterate do |doc|
157
+ doc.should == item
158
+ end
159
+ end
160
+
161
+ it "iterates over the results of execute" do
162
+ context.should_not_receive(:execute)
163
+ acc = []
164
+ context.iterate do |doc|
165
+ acc << doc
166
+ end
167
+ acc.should == [item]
168
+ end
169
+ end
170
+ end
171
+ end
172
+
173
+ context "#last" do
174
+ it "delegates to find_one" do
175
+ collection.should_receive(:find_one).with({}, {:sort=>[[:_id, :desc]]})
176
+ context.last
177
+ end
178
+ end
179
+
180
+ context "#max" do
181
+ it "delegates to grouped" do
182
+ context.should_receive(:grouped).with(:max, "number", MongoDoc::Contexts::Mongo::MAX_REDUCE)
183
+ context.max(:number)
184
+ end
185
+ end
186
+
187
+ context "#min" do
188
+ it "delegates to grouped" do
189
+ context.should_receive(:grouped).with(:min, "number", MongoDoc::Contexts::Mongo::MIN_REDUCE)
190
+ context.min(:number)
191
+ end
192
+ end
193
+
194
+ context "#one" do
195
+ it "delegates to find_one" do
196
+ collection.should_receive(:find_one).with({}, {})
197
+ context.one
198
+ end
199
+ end
200
+
201
+ context "#sum" do
202
+ it "delegates to grouped" do
203
+ context.should_receive(:grouped).with(:sum, "number", MongoDoc::Contexts::Mongo::SUM_REDUCE)
204
+ context.sum(:number)
205
+ end
206
+ end
207
+
208
+ context "#grouped" do
209
+ it "delegates to group" do
210
+ op = 'op'
211
+ field = 'name'
212
+ reduce = '[field]'
213
+ collection.should_receive(:group).with(nil, {}, {op =>"start"}, field, true).and_return([])
214
+ context.send(:grouped, op, field, reduce)
215
+ end
216
+ end
217
+
218
+ context "#empty? or #blank?" do
219
+ it "delegates to find_one" do
220
+ collection.should_receive(:find_one).with({}, {})
221
+ context.empty?
222
+ end
223
+
224
+ it "returns true if find_one returns no result" do
225
+ collection.stub(:find_one).and_return(nil)
226
+ context.should be_blank
227
+ end
228
+
229
+ it "returns false if find_one returns any result" do
230
+ collection.stub(:find_one).and_return(stub('result'))
231
+ context.should_not be_empty
232
+ end
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,56 @@
1
+ require "spec_helper"
2
+
3
+ describe MongoDoc::Contexts do
4
+
5
+ class ContextTest
6
+ include MongoDoc::Document
7
+
8
+ embed_many :children
9
+ end
10
+
11
+ context ".context_for" do
12
+ let(:criteria) { stub('criteria', :klass => klass) }
13
+
14
+ context "when criteria is for a top-level MongoDoc::Document" do
15
+ let(:klass) { ContextTest }
16
+
17
+ it "creates a Mongo context" do
18
+ MongoDoc::Contexts::Mongo.should_receive(:new).with(criteria)
19
+ Mongoid::Contexts.context_for(criteria)
20
+ end
21
+ end
22
+
23
+ context "when criteria is for an association" do
24
+ let(:klass) { ContextTest.new.children }
25
+
26
+ it "creates an Enumerable context" do
27
+ Mongoid::Contexts::Enumerable.should_receive(:new).with(criteria)
28
+ Mongoid::Contexts.context_for(criteria)
29
+ end
30
+ end
31
+
32
+ context "when criteria is for a MongoDoc::Collection" do
33
+ let(:klass) { MongoDoc::Collection.new('collection') }
34
+
35
+ before do
36
+ MongoDoc::Collection.stub(:mongo_collection).and_return(stub('collection'))
37
+ end
38
+
39
+ it "creates a Mongo context" do
40
+ MongoDoc::Contexts::Mongo.should_receive(:new).with(criteria)
41
+ Mongoid::Contexts.context_for(criteria)
42
+ end
43
+ end
44
+
45
+ context "when criteria is not recognized" do
46
+ let(:klass) { Object }
47
+
48
+ it "raises an exception" do
49
+ expect do
50
+ Mongoid::Contexts.context_for(criteria)
51
+ end.should raise_error(Mongoid::Contexts::UnknownContext)
52
+ end
53
+ end
54
+ end
55
+ end
56
+