mongo_db 0.1.8 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  require 'set'
2
2
  require 'date'
3
3
 
4
- module Mongo::Ext::Collection
4
+ module Mongo::CollectionExt
5
5
  #
6
6
  # CRUD
7
7
  #
@@ -38,6 +38,10 @@ module Mongo::Ext::Collection
38
38
  def destroy *args
39
39
  remove *args
40
40
  end
41
+
42
+ def create *args
43
+ insert *args
44
+ end
41
45
 
42
46
  #
43
47
  # Querying
@@ -100,7 +104,7 @@ module Mongo::Ext::Collection
100
104
  def symbolize_doc doc
101
105
  return doc unless Mongo.defaults[:symbolize]
102
106
 
103
- Mongo::Ext::Collection.convert_doc doc do |k, v, result|
107
+ Mongo::CollectionExt.convert_doc doc do |k, v, result|
104
108
  k = k.to_sym if k.is_a? String
105
109
  result[k] = v
106
110
  end
@@ -110,7 +114,7 @@ module Mongo::Ext::Collection
110
114
  def convert_underscore_to_dollar_in_selector selector
111
115
  return selector unless Mongo.defaults[:convert_underscore_to_dollar]
112
116
 
113
- Mongo::Ext::Collection.convert_doc selector do |k, v, result|
117
+ Mongo::CollectionExt.convert_doc selector do |k, v, result|
114
118
  k = "$#{k.to_s[1..-1]}".to_sym if QUERY_KEYWORDS.include?(k)
115
119
  result[k] = v
116
120
  end
@@ -120,7 +124,7 @@ module Mongo::Ext::Collection
120
124
  def convert_underscore_to_dollar_in_update update
121
125
  return update unless Mongo.defaults[:convert_underscore_to_dollar]
122
126
 
123
- Mongo::Ext::Collection.convert_doc update do |k, v, result|
127
+ Mongo::CollectionExt.convert_doc update do |k, v, result|
124
128
  k = "$#{k.to_s[1..-1]}".to_sym if UPDATE_KEYWORDS.include?(k)
125
129
  result[k] = v
126
130
  end
@@ -1,4 +1,4 @@
1
- module Mongo::Ext::DB
1
+ module Mongo::DBExt
2
2
  protected
3
3
  def method_missing collection_name
4
4
  self.collection collection_name
@@ -2,9 +2,6 @@ require 'mongo_db/gems'
2
2
 
3
3
  require 'mongo'
4
4
 
5
- # namespace for extensions
6
- class Mongo::Ext; end
7
-
8
5
  %w(
9
6
  database
10
7
  collection
@@ -19,11 +16,11 @@ Mongo.class_eval do
19
16
  end
20
17
 
21
18
  # database
22
- Mongo::DB.send :include, Mongo::Ext::DB
19
+ Mongo::DB.send :include, Mongo::DBExt
23
20
 
24
21
  # collection
25
22
  Mongo::Collection.class_eval do
26
- include Mongo::Ext::Collection
23
+ include Mongo::CollectionExt
27
24
 
28
25
  %w(insert update remove save).each do |method|
29
26
  alias_method "#{method}_without_ext", method
@@ -1,4 +1,4 @@
1
- module Mongo::Ext::CollectionFinders
1
+ module Mongo::CollectionFinders
2
2
  #
3
3
  # first_by_id, special case
4
4
  #
@@ -6,5 +6,5 @@ class Mongo::NotFound < StandardError; end
6
6
  collection_finders
7
7
  ).each{|f| require "mongo_db/driver/more/#{f}"}
8
8
 
9
- Mongo::Collection.send :include, Mongo::Ext::CollectionFinders
9
+ Mongo::Collection.send :include, Mongo::CollectionFinders
10
10
 
@@ -1,36 +1,37 @@
1
- module Mongo::Ext::ObjectHelper
1
+ module Mongo::ObjectHelper
2
2
  #
3
3
  # CRUD
4
4
  #
5
- def save_with_model doc, opts = {}
5
+ def save_with_object doc, opts = {}
6
6
  if doc.is_a? Hash
7
- save_without_model doc, opts
7
+ save_without_object doc, opts
8
8
  else
9
- if id = doc.instance_variable_get(:@_id)
10
- update({:_id => id}, doc, opts.merge(upsert: true))
11
- else
12
- insert doc, opts
13
- end
9
+ ::Mongo::ObjectSerializer.new(doc).save opts, self
14
10
  end
15
11
  end
16
12
 
17
- def insert_with_model args, opts = {}
18
- docs = args.is_a?(Array) ? args : [args]
19
- result = _insert_with_model docs, opts
20
- args.is_a?(Array) ? result : result.first
13
+ def insert_with_object args, opts = {}
14
+ if args.is_a?(Hash) or args.is_a?(Array)
15
+ insert_without_object args, opts
16
+ else
17
+ ::Mongo::ObjectSerializer.new(args).insert opts, self
18
+ end
21
19
  end
22
20
 
23
- def update_with_model selector, doc, opts = {}
24
- doc = Mongo::Ext::ObjectHelper.convert_object_to_doc doc unless doc.is_a?(Hash)
25
- update_without_model selector, doc, opts
21
+ def update_with_object selector, doc, opts = {}
22
+ if doc.is_a?(Hash)
23
+ update_without_object selector, doc, opts
24
+ else
25
+ raise "can't use update selector with object (#{selector}, {#{doc}})!" unless selector == nil
26
+ ::Mongo::ObjectSerializer.new(doc).update opts, self
27
+ end
26
28
  end
27
29
 
28
- def remove_with_model arg = {}, opts = {}
30
+ def remove_with_object arg = {}, opts = {}
29
31
  if arg.is_a? Hash
30
- remove_without_model arg, opts
32
+ remove_without_object arg, opts
31
33
  else
32
- id = arg.instance_variable_get(:@_id) || "can't remove object without _id (#{arg})!"
33
- remove_without_model({_id: id}, opts)
34
+ ::Mongo::ObjectSerializer.new(arg).remove opts, self
34
35
  end
35
36
  end
36
37
 
@@ -40,115 +41,14 @@
40
41
  #
41
42
  def first *args, &block
42
43
  doc = super *args, &block
43
- Mongo::Ext::ObjectHelper.convert_doc_to_object doc
44
+ ::Mongo::ObjectSerializer.build(doc)
44
45
  end
45
46
 
46
47
  def each *args, &block
47
48
  super *args do |doc|
48
- doc = Mongo::Ext::ObjectHelper.convert_doc_to_object(doc)
49
- block.call doc
49
+ obj = ::Mongo::ObjectSerializer.build(doc)
50
+ block.call obj
50
51
  end
51
52
  nil
52
53
  end
53
-
54
- protected
55
- def _insert_with_model docs, opts
56
- hashes = docs.collect do |doc|
57
- doc.is_a?(Hash) ? doc : Mongo::Ext::ObjectHelper.convert_object_to_doc(doc)
58
- end
59
- result = insert_without_model hashes, opts
60
- hashes.each_with_index do |h, i|
61
- Mongo::Ext::ObjectHelper.update_object_after_insertion docs[i], h
62
- end
63
- result
64
- end
65
-
66
-
67
- SIMPLE_TYPES = [
68
- Fixnum, Float,
69
- TrueClass, FalseClass,
70
- String, Symbol,
71
- Array, Hash, Set,
72
- Data, DateTime,
73
- NilClass, Time,
74
- BSON::ObjectId
75
- ].to_set
76
-
77
- class << self
78
- def update_object_after_insertion doc, hash
79
- return if doc.is_a? Hash
80
- if id = hash[:_id] || hash['_id']
81
- doc.instance_variable_set :@_id, id
82
- end
83
- end
84
-
85
- # converts object to hash (also works with nested & arrays)
86
- def convert_object_to_doc obj
87
- return obj.to_mongo if obj.respond_to? :to_mongo
88
-
89
- if obj.is_a? Hash
90
- doc = {}
91
- obj.each do |k, v|
92
- doc[k] = convert_object_to_doc v
93
- end
94
- doc
95
- elsif obj.is_a? Array
96
- obj.collect{|v| convert_object_to_doc v}
97
- elsif SIMPLE_TYPES.include? obj.class
98
- obj
99
- else
100
- doc = {}
101
-
102
- # copying instance variables to hash
103
- obj.instance_variables.each do |iv_name|
104
- # skipping variables starting with _xx, usually they
105
- # have specific meaning and used for example for cache
106
- next if iv_name =~ /^@_/ and iv_name != :@_id
107
-
108
- k = iv_name.to_s[1..-1]
109
- k = k.to_sym if Mongo.defaults[:symbolize]
110
- v = obj.instance_variable_get iv_name
111
- doc[k] = convert_object_to_doc v
112
- end
113
-
114
- # setting class
115
- class_name = '_class'
116
- class_name = class_name.to_sym if Mongo.defaults[:symbolize]
117
- doc[class_name] = obj.class.name
118
-
119
- doc
120
- end
121
- end
122
-
123
- def convert_doc_to_object doc
124
- if doc.is_a? Hash
125
- if class_name = doc[:_class] || doc['_class']
126
- klass = constantize class_name
127
- obj = klass.new
128
- doc.each do |k, v|
129
- next if k.to_sym == :_class
130
-
131
- v = convert_doc_to_object v
132
- obj.instance_variable_set "@#{k}", v
133
- end
134
- obj
135
- else
136
- doc
137
- end
138
- elsif doc.is_a? Array
139
- doc.collect{|v| convert_doc_to_object v}
140
- else
141
- doc
142
- end
143
- end
144
-
145
- def constantize class_name
146
- @constantize_cache ||= {}
147
- unless klass = @constantize_cache[class_name]
148
- klass = eval class_name, TOPLEVEL_BINDING, __FILE__, __LINE__
149
- @constantize_cache[class_name] = klass
150
- end
151
- klass
152
- end
153
- end
154
54
  end
@@ -0,0 +1,274 @@
1
+ class Mongo::ObjectSerializer
2
+ SIMPLE_TYPES = [
3
+ Fixnum, Float,
4
+ TrueClass, FalseClass,
5
+ String, Symbol,
6
+ Array, Hash, Set,
7
+ Data, DateTime,
8
+ NilClass, Time,
9
+ BSON::ObjectId
10
+ ].to_set
11
+
12
+ attr_reader :object
13
+
14
+ def initialize object
15
+ @object = object
16
+ end
17
+
18
+ def save opts, collection
19
+ if _id
20
+ self.update opts.merge(upsert: true), collection
21
+ else
22
+ self.insert opts, collection
23
+ end
24
+ end
25
+
26
+ def insert opts, collection
27
+ opts, validate, callbacks = parse_object_options opts
28
+
29
+ # before callbacks
30
+ return false if callbacks and !run_callbacks(objects, :before_validate, :before_save, :before_create)
31
+
32
+ # validation
33
+ return false if validate and !valid?
34
+
35
+ # saving document
36
+ doc = to_document
37
+ collection.insert_without_object doc, opts
38
+ id = doc[:_id] || doc['_id'] || raise("internal error: no id after document insertion (#{doc})!")
39
+ object.instance_variable_set :@_id, id
40
+ update_internal_state!
41
+
42
+ # after callbacks
43
+ run_callbacks(objects, :after_create, :after_save, :after_validate) if callbacks
44
+
45
+ true
46
+ end
47
+
48
+ def update opts, collection
49
+ opts, validate, callbacks = parse_object_options opts
50
+
51
+ # before callbacks.
52
+ # we need to sort out embedded objects into created, updated and destroyed
53
+ created_objects, updated_objects, destroyed_objects = [], [], []
54
+ if callbacks
55
+ original_ids = original_embedded_objects.collect{|obj| obj.object_id}.to_set
56
+ objects.each do |obj|
57
+ (original_ids.include?(obj.object_id) ? updated_objects : created_objects) << obj
58
+ end
59
+
60
+ objects_ids = objects.collect{|obj| obj.object_id}.to_set
61
+ destroyed_objects = original_embedded_objects.select{|obj| !objects_ids.include?(obj.object_id)}
62
+
63
+ all_successfull = [
64
+ run_callbacks(created_objects, :before_validate, :before_save, :before_create),
65
+ run_callbacks(updated_objects, :before_validate, :before_save, :before_update),
66
+ run_callbacks(destroyed_objects, :before_validate, :before_destroy)
67
+ ].reduce(:&)
68
+
69
+ return false unless all_successfull
70
+ end
71
+
72
+ # validation
73
+ return false if validate and !valid?
74
+
75
+ # saving document
76
+ doc = to_document
77
+ id = _id || raise("can't update document without id (#{doc})!")
78
+ collection.update_without_object({_id: id}, doc, opts)
79
+ update_internal_state!
80
+
81
+ # after callbacks
82
+ if callbacks
83
+ run_callbacks(created_objects, :after_create, :after_save, :after_validate)
84
+ run_callbacks(updated_objects, :after_update, :after_save, :after_validate)
85
+ run_callbacks(destroyed_objects, :after_destroy, :after_validate)
86
+ end
87
+
88
+ true
89
+ end
90
+
91
+ def remove opts, collection
92
+ opts, validate, callbacks = parse_object_options opts
93
+
94
+ # before callbacks
95
+ if callbacks
96
+ # we need to run :destroy callbacks also on detached embedded objects.
97
+ all_objects = (objects + original_embedded_objects).uniq{|o| o.object_id}
98
+ return false unless run_callbacks(all_objects, :before_validate, :before_destroy)
99
+ end
100
+
101
+ # validation
102
+ return false if validate and !valid?
103
+
104
+ # saving document
105
+ id = _id || "can't destroy object without _id (#{arg})!"
106
+ collection.remove_without_object({_id: id}, opts)
107
+ update_internal_state!
108
+
109
+ # after callbacks
110
+ run_callbacks(objects, :after_destroy, :after_validate) if callbacks
111
+
112
+ true
113
+ end
114
+
115
+ def to_document
116
+ _to_document object
117
+ end
118
+
119
+ def _id
120
+ object.instance_variable_get(:@_id)
121
+ end
122
+
123
+ def valid?
124
+ objects.each do |obj|
125
+ return false if obj.respond_to?(:_valid?) and !obj._valid?
126
+ end
127
+ true
128
+ end
129
+
130
+ def run_callbacks objects, *names
131
+ all_successfull = true
132
+ names.each do |name|
133
+ objects.each do |obj|
134
+ if obj.respond_to? :_run_callbacks
135
+ all_successfull = false if obj._run_callbacks(name) == false
136
+ end
137
+ end
138
+ end
139
+ all_successfull
140
+ end
141
+
142
+ def objects
143
+ @objects_cache ||= begin
144
+ objects = []
145
+ _each_object(object){|obj| objects << obj}
146
+ objects
147
+ end
148
+ end
149
+
150
+ def update_internal_state!
151
+ self.original_embedded_objects = objects if Mongo.defaults[:callbacks]
152
+ end
153
+
154
+ protected
155
+ def original_embedded_objects; object.instance_variable_get(:@_original_embedded_objects) end
156
+ def original_embedded_objects= objects; object.instance_variable_set(:@_original_embedded_objects, objects) end
157
+
158
+ def parse_object_options opts
159
+ opts = opts.clone
160
+ validate = opts.delete(:validate) == false ? false : true
161
+ callbacks = opts.delete(:callbacks) == false ? false : Mongo.defaults[:callbacks]
162
+ return opts, validate, callbacks
163
+ end
164
+
165
+ # need this to allow change it in specs
166
+ # RSpec adds @mock_proxy, and we need to skip it
167
+ SKIP_IV_REGEXP = /^@_/
168
+
169
+ def _each_instance_variable obj, &block
170
+ obj.instance_variables.each do |iv_name|
171
+ # skipping variables starting with _xx, usually they
172
+ # have specific meaning and used for example for cache
173
+ next if iv_name =~ SKIP_IV_REGEXP
174
+
175
+ block.call iv_name, obj.instance_variable_get(iv_name)
176
+ end
177
+ end
178
+
179
+ # converts object to document (also works with nested & arrays)
180
+ def _to_document obj
181
+ return obj.to_mongo if obj.respond_to? :to_mongo
182
+
183
+ if obj.is_a? Hash
184
+ doc = {}
185
+ obj.each do |k, v|
186
+ doc[k] = _to_document v
187
+ end
188
+ doc
189
+ elsif obj.is_a? Array
190
+ obj.collect{|v| _to_document v}
191
+ elsif SIMPLE_TYPES.include? obj.class
192
+ obj
193
+ else
194
+ doc = {}
195
+
196
+ # copying instance variables
197
+ _each_instance_variable obj do |iv_name, v|
198
+ k = iv_name.to_s[1..-1]
199
+ k = k.to_sym if Mongo.defaults[:symbolize]
200
+ doc[k] = _to_document v
201
+ end
202
+
203
+ # adding _id & _class
204
+ id_key, class_key = Mongo.defaults[:symbolize] ? [:_id, :_class] : ['_id', '_class']
205
+ id = instance_variable_get('@_id')
206
+ doc[id_key] = id if id
207
+ doc[class_key] = obj.class.name
208
+
209
+ doc
210
+ end
211
+ end
212
+
213
+ def _each_object obj, &block
214
+ if obj.is_a? Hash
215
+ obj.each{|k, v| _each_object v, &block}
216
+ elsif obj.is_a? Array
217
+ obj.each{|v| _each_object v, &block}
218
+ elsif SIMPLE_TYPES.include? obj.class
219
+ else
220
+ block.call obj
221
+ _each_instance_variable obj do |iv_name, v|
222
+ _each_object v, &block
223
+ end
224
+ end
225
+ nil
226
+ end
227
+
228
+
229
+ class << self
230
+ def build doc
231
+ obj = _build doc
232
+ serializer = Mongo::ObjectSerializer.new obj
233
+ serializer.update_internal_state!
234
+ obj
235
+ end
236
+
237
+ protected
238
+ def _build doc
239
+ if doc.is_a? Hash
240
+ if class_name = doc[:_class] || doc['_class']
241
+ klass = constantize class_name
242
+
243
+ if klass.respond_to? :to_object
244
+ klass.to_object doc
245
+ else
246
+ obj = klass.new
247
+ doc.each do |k, v|
248
+ next if k.to_sym == :_class
249
+
250
+ v = _build v
251
+ obj.instance_variable_set "@#{k}", v
252
+ end
253
+ obj
254
+ end
255
+ else
256
+ doc
257
+ end
258
+ elsif doc.is_a? Array
259
+ doc.collect{|v| _build v}
260
+ else
261
+ doc
262
+ end
263
+ end
264
+
265
+ def constantize class_name
266
+ @constantize_cache ||= {}
267
+ unless klass = @constantize_cache[class_name]
268
+ klass = eval class_name, TOPLEVEL_BINDING, __FILE__, __LINE__
269
+ @constantize_cache[class_name] = klass
270
+ end
271
+ klass
272
+ end
273
+ end
274
+ end
@@ -1,15 +1,18 @@
1
1
  require 'mongo_db/driver'
2
2
 
3
3
  %w(
4
+ object_serializer
4
5
  object_helper
5
6
  ).each{|f| require "mongo_db/object/#{f}"}
6
7
 
8
+ Mongo.defaults[:callbacks] = true
9
+
7
10
  # collection
8
11
  Mongo::Collection.class_eval do
9
- include Mongo::Ext::ObjectHelper
12
+ include Mongo::ObjectHelper
10
13
 
11
14
  %w(insert update remove save).each do |method|
12
- alias_method "#{method}_without_model", method
13
- alias_method method, "#{method}_with_model"
15
+ alias_method "#{method}_without_object", method
16
+ alias_method method, "#{method}_with_object"
14
17
  end
15
18
  end
data/readme.md CHANGED
@@ -18,45 +18,50 @@ These enhancements alter the driver's API and made it more simple and intuitive.
18
18
  ``` ruby
19
19
  require 'mongo_db/driver/core'
20
20
 
21
- # changing some defaults
21
+ # Changing some defaults.
22
22
  Mongo.defaults.merge! symbolize: true, multi: true, safe: true
23
23
 
24
- # connection & db
24
+ # Connection & db.
25
25
  connection = Mongo::Connection.new
26
26
  db = connection.db 'default_test'
27
+ db.units.drop
27
28
 
28
- # create
29
+ # Collection shortcuts.
30
+ db.some_collection
31
+
32
+ # Create.
29
33
  zeratul = {name: 'Zeratul', stats: {attack: 85, life: 300, shield: 100}}
30
34
  tassadar = {name: 'Tassadar', stats: {attack: 0, life: 80, shield: 300}}
31
35
 
32
36
  db.units.save zeratul
33
37
  db.units.save tassadar
34
38
 
35
- # udate (we made error - mistakenly set Tassadar's attack as zero, let's fix it)
39
+ # Udate (we made error - mistakenly set Tassadar's attack as zero, let's fix it).
36
40
  tassadar[:stats][:attack] = 20
37
41
  db.units.save tassadar
38
42
 
39
- # querying - first & all, there's also :each, the same as :all
40
- db.units.first name: 'Zeratul' # => zeratul
41
- db.units.all name: 'Zeratul' # => [zeratul]
43
+ # Querying first & all, there's also :each, the same as :all.
44
+ db.units.first name: 'Zeratul' # => zeratul
45
+ db.units.all name: 'Zeratul' # => [zeratul]
42
46
  db.units.all name: 'Zeratul' do |unit|
43
- unit # => zeratul
47
+ unit # => zeratul
44
48
  end
45
49
  ```
46
50
 
47
51
  Optionall stuff - simple query enchancements:
48
52
 
49
53
  ``` ruby
54
+ # Finders.
50
55
  require 'mongo_db/driver/more'
51
56
 
52
- # simple finders (bang versions also availiable)
57
+ # Simple finders (bang versions also availiable).
53
58
  db.units.by_name 'Zeratul' # => zeratul
54
59
  db.units.first_by_name 'Zeratul' # => zeratul
55
60
  db.units.all_by_name 'Zeratul' # => [zeratul]
56
61
 
57
- # query sugar, use {life: {_lt: 100}} instead of {life: {:$lt => 100}}
62
+ # Query sugar, use {name: {_gt: 'Z'}} instead of {name: {:$gt => 'Z'}}.
58
63
  Mongo.defaults.merge! convert_underscore_to_dollar: true
59
- db.units.all 'stats.life' => {_lt: 100} # => [tassadar]
64
+ db.units.all name: {_gt: 'Z'} # => [zeratul]
60
65
  ```
61
66
 
62
67
  More docs - there's no need for more docs, the whole point of this extension is to be small, intuitive, 100% compatible with the official driver, and require no extra knowledge.
@@ -69,7 +74,7 @@ Save any Ruby object to MongoDB, as if it's a document. Objects can be any type,
69
74
  Note: the :initialize method should allow to create object without arguments.
70
75
 
71
76
  ``` ruby
72
- # let's define the game unit
77
+ # Let's define the game unit.
73
78
  class Unit
74
79
  attr_reader :name, :stats
75
80
 
@@ -87,38 +92,39 @@ class Unit
87
92
  end
88
93
  end
89
94
 
90
- # connecting to MongoDB
95
+ # Connecting to MongoDB.
91
96
  require 'mongo_db/object'
92
97
  Mongo.defaults.merge! symbolize: true, multi: true, safe: true
93
98
  connection = Mongo::Connection.new
94
99
  db = connection.db 'default_test'
100
+ db.units.drop
95
101
 
96
- # create
102
+ # Create.
97
103
  zeratul = Unit.new('Zeratul', Unit::Stats.new(85, 300, 100))
98
104
  tassadar = Unit.new('Tassadar', Unit::Stats.new(0, 80, 300))
99
105
 
100
106
  db.units.save zeratul
101
107
  db.units.save tassadar
102
108
 
103
- # udate (we made error - mistakenly set Tassadar's attack as zero, let's fix it)
109
+ # Udate (we made error - mistakenly set Tassadar's attack as zero, let's fix it).
104
110
  tassadar.stats.attack = 20
105
111
  db.units.save tassadar
106
112
 
107
- # querying first & all, there's also :each, the same as :all
113
+ # Querying first & all, there's also :each, the same as :all.
108
114
  db.units.first name: 'Zeratul' # => zeratul
109
115
  db.units.all name: 'Zeratul' # => [zeratul]
110
116
  db.units.all name: 'Zeratul' do |unit|
111
117
  unit # => zeratul
112
118
  end
113
119
 
114
- # simple finders (bang versions also availiable)
120
+ # Simple finders (bang versions also availiable).
115
121
  db.units.by_name 'Zeratul' # => zeratul
116
122
  db.units.first_by_name 'Zeratul' # => zeratul
117
123
  db.units.all_by_name 'Zeratul' # => [zeratul]
118
124
 
119
- # query sugar, use {life: {_lt: 100}} instead of {life: {:$lt => 100}}
125
+ # Query sugar, use {name: {_gt: 'Z'}} instead of {name: {:$gt => 'Z'}}.
120
126
  Mongo.defaults.merge! convert_underscore_to_dollar: true
121
- db.units.all('stats.life' => {_lt: 100}) # => [tassadar]
127
+ db.units.all name: {_gt: 'Z'} # => [zeratul]
122
128
  ```
123
129
 
124
130
  # Migrations (work in progress)
@@ -3,7 +3,7 @@ require 'driver/spec_helper'
3
3
  describe "Collection" do
4
4
  before do
5
5
  @helper = Object.new
6
- @helper.send :extend, Mongo::Ext::Collection
6
+ @helper.send :extend, Mongo::CollectionExt
7
7
  end
8
8
 
9
9
  it "symbolize" do
@@ -0,0 +1,95 @@
1
+ require 'object/spec_helper'
2
+
3
+ describe 'Object callbacks' do
4
+ with_mongo
5
+
6
+ before do
7
+ class Player
8
+ include RSpec::CallbackHelper
9
+ attr_accessor :missions
10
+
11
+ class Mission
12
+ include RSpec::CallbackHelper
13
+ end
14
+ end
15
+
16
+ @mission = Player::Mission.new
17
+ @player = Player.new
18
+ @player.missions = [@mission]
19
+ end
20
+ after{remove_constants :Player}
21
+
22
+ it 'create' do
23
+ %w(before_validate before_save before_create after_create after_save after_validate).each do |name|
24
+ @player.should_receive(name).once.ordered
25
+ @mission.should_receive(name).once.ordered
26
+ end
27
+
28
+ db.players.save @player
29
+ end
30
+
31
+ it 'update' do
32
+ db.players.save(@player)
33
+
34
+ %w(before_validate before_save before_update after_update after_save after_validate).each do |name|
35
+ @player.should_receive(name).once.ordered
36
+ @mission.should_receive(name).once.ordered
37
+ end
38
+ db.players.save @player
39
+ end
40
+
41
+ it 'destroy' do
42
+ db.players.save @player
43
+
44
+ %w(before_validate before_destroy after_destroy after_validate).each do |name|
45
+ @player.should_receive(name).once.ordered
46
+ @mission.should_receive(name).once.ordered
47
+ end
48
+ db.players.destroy @player
49
+ end
50
+
51
+ it 'should be able skip callbacks' do
52
+ @player.should_not_receive(:_run_callbacks)
53
+ @mission.should_not_receive(:_run_callbacks)
54
+
55
+ db.players.save @player, callbacks: false
56
+ db.players.count.should == 1
57
+ db.players.save @player, callbacks: false
58
+ db.players.count.should == 1
59
+ db.players.destroy @player, callbacks: false
60
+ db.players.count.should == 0
61
+ end
62
+
63
+ it 'should be able interrupt CRUD' do
64
+ @mission.stub! :_run_callbacks do |name|
65
+ false if name == :before_save
66
+ end
67
+ db.players.save(@player).should be_false
68
+ db.players.count.should == 0
69
+ end
70
+
71
+ describe "embedded" do
72
+ it 'should fire :destroy on detached objects' do
73
+ db.players.save @player
74
+ @player.missions.clear
75
+ @mission.should_receive(:before_destroy).once
76
+ db.players.destroy @player
77
+ end
78
+
79
+ it 'should fire :destroy on deleted objects in update' do
80
+ db.players.save @player
81
+ @player.missions.clear
82
+ @mission.should_receive(:before_destroy).once
83
+ db.players.save @player
84
+ end
85
+
86
+ it 'should fire :create on new objects in update' do
87
+ db.players.save @player
88
+ mission2 = Player::Mission.new
89
+ @player.missions << mission2
90
+ mission2.should_receive(:before_create).once
91
+ mission2.should_not_receive(:before_update)
92
+ db.players.save @player
93
+ end
94
+ end
95
+ end
File without changes
@@ -1,3 +1,13 @@
1
1
  require 'mongo_db/object'
2
+ require 'driver/spec_helper'
2
3
 
3
- require 'driver/spec_helper'
4
+ # RSpec adds some instance variables and we need to skip it.
5
+ Mongo::ObjectSerializer.send :const_set, :SKIP_IV_REGEXP, /^@_|^@mock_proxy/
6
+ Mongo::ObjectSerializer::SIMPLE_TYPES << RSpec::Mocks::Proxy
7
+
8
+ # To simplify callback expectations
9
+ module RSpec::CallbackHelper
10
+ def _run_callbacks name
11
+ send name if respond_to? name
12
+ end
13
+ end
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require 'object/spec_helper'
2
2
 
3
3
  describe 'Object validation' do
4
4
  with_mongo
@@ -19,56 +19,56 @@ describe 'Object validation' do
19
19
 
20
20
  it 'should not save/update/destroy invalid objects' do
21
21
  # create
22
- @player.stub!(:valid?).and_return(false)
22
+ @player.stub!(:_valid?).and_return(false)
23
23
  db.players.save(@player).should be_false
24
24
 
25
- @player.stub!(:valid?).and_return(true)
25
+ @player.stub!(:_valid?).and_return(true)
26
26
  db.players.save(@player).should be_true
27
27
 
28
28
  # update
29
- @player.stub!(:valid?).and_return(false)
29
+ @player.stub!(:_valid?).and_return(false)
30
30
  db.players.save(@player).should be_false
31
31
 
32
- @player.stub!(:valid?).and_return(true)
32
+ @player.stub!(:_valid?).and_return(true)
33
33
  db.players.save(@player).should be_true
34
34
 
35
35
  # destroy
36
- @player.stub!(:valid?).and_return(false)
36
+ @player.stub!(:_valid?).and_return(false)
37
37
  db.players.destroy(@player).should be_false
38
38
 
39
- @player.stub!(:valid?).and_return(true)
39
+ @player.stub!(:_valid?).and_return(true)
40
40
  db.players.destroy(@player).should be_true
41
41
  end
42
42
 
43
43
  it 'should not save/update/destroy invalid embedded objects' do
44
44
  # create
45
- @mission.stub!(:valid?).and_return(false)
45
+ @mission.stub!(:_valid?).and_return(false)
46
46
  db.players.save(@player).should be_false
47
47
 
48
- @mission.stub!(:valid?).and_return(true)
48
+ @mission.stub!(:_valid?).and_return(true)
49
49
  db.players.save(@player).should be_true
50
50
 
51
51
  # update
52
- @mission.stub!(:valid?).and_return(false)
52
+ @mission.stub!(:_valid?).and_return(false)
53
53
  db.players.save(@player).should be_false
54
54
 
55
- @mission.stub!(:valid?).and_return(true)
55
+ @mission.stub!(:_valid?).and_return(true)
56
56
  db.players.save(@player).should be_true
57
57
 
58
58
  # destroy
59
- @mission.stub!(:valid?).and_return(false)
59
+ @mission.stub!(:_valid?).and_return(false)
60
60
  db.players.destroy(@player).should be_false
61
61
 
62
- @mission.stub!(:valid?).and_return(true)
62
+ @mission.stub!(:_valid?).and_return(true)
63
63
  db.players.destroy(@player).should be_true
64
64
  end
65
65
 
66
66
  it "should be able skip validation" do
67
- @player.stub!(:valid?).and_return(false)
67
+ @player.stub!(:_valid?).and_return(false)
68
68
  db.players.save(@player, validate: false).should be_true
69
69
 
70
- @player.stub!(:valid?).and_return(true)
71
- @mission.stub!(:valid?).and_return(false)
70
+ @player.stub!(:_valid?).and_return(true)
71
+ @mission.stub!(:_valid?).and_return(false)
72
72
  db.players.save(@player, validate: false).should be_true
73
73
  end
74
74
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongo_db
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.9
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-08-14 00:00:00.000000000Z
12
+ date: 2011-08-18 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mongo
@@ -39,12 +39,12 @@ files:
39
39
  - lib/mongo_db/driver.rb
40
40
  - lib/mongo_db/gems.rb
41
41
  - lib/mongo_db/object/object_helper.rb
42
+ - lib/mongo_db/object/object_serializer.rb
42
43
  - lib/mongo_db/object.rb
43
44
  - spec/driver/core/collection_spec.rb
44
45
  - spec/driver/core/crud_spec.rb
45
46
  - spec/driver/core/database_spec.rb
46
47
  - spec/driver/core/hash_helper_spec.rb
47
- - spec/driver/example_spec.rb
48
48
  - spec/driver/fixes_spec.rb
49
49
  - spec/driver/more/querying_spec.rb
50
50
  - spec/driver/spec_helper.rb
@@ -52,12 +52,11 @@ files:
52
52
  - spec/model/callbacks.rb
53
53
  - spec/model/example.rb
54
54
  - spec/model/model_crud.rb
55
- - spec/object/callbacks.rb
55
+ - spec/object/callbacks_spec.rb
56
56
  - spec/object/crud_shared.rb
57
- - spec/object/example_spec.rb
58
- - spec/object/object_crud_spec.rb
57
+ - spec/object/crud_spec.rb
59
58
  - spec/object/spec_helper.rb
60
- - spec/object/validation.rb
59
+ - spec/object/validation_spec.rb
61
60
  - spec/test.rb
62
61
  homepage: http://github.com/alexeypetrushin/mongo_db
63
62
  licenses: []
@@ -1,55 +0,0 @@
1
- require 'mongo_db/driver/core'
2
- require 'rspec'
3
-
4
- describe "Driver example" do
5
- defaults = nil
6
- before(:all){defaults = Mongo.defaults.clone}
7
- after(:all){Mongo.defaults = defaults}
8
-
9
- it "core" do
10
- require 'mongo_db/driver/core'
11
-
12
- # changing some defaults
13
- Mongo.defaults.merge! symbolize: true, multi: true, safe: true
14
-
15
- # connection & db
16
- connection = Mongo::Connection.new
17
- db = connection.db 'default_test'
18
-
19
- # collection shortcuts
20
- db.some_collection
21
-
22
- # create
23
- zeratul = {name: 'Zeratul', stats: {attack: 85, life: 300, shield: 100}}
24
- tassadar = {name: 'Tassadar', stats: {attack: 0, life: 80, shield: 300}}
25
-
26
- db.units.save zeratul
27
- db.units.save tassadar
28
-
29
- # udate (we made error - mistakenly set Tassadar's attack as zero, let's fix it)
30
- tassadar[:stats][:attack] = 20
31
- db.units.save tassadar
32
-
33
- # querying first & all, there's also :each, the same as :all
34
- db.units.first name: 'Zeratul' # => zeratul
35
- db.units.all name: 'Zeratul' # => [zeratul]
36
- db.units.all name: 'Zeratul' do |unit|
37
- unit # => zeratul
38
- end
39
-
40
-
41
- #
42
- # optional
43
- #
44
- require 'mongo_db/driver/more'
45
-
46
- # simple finders (bang versions also availiable)
47
- db.units.by_name 'Zeratul' # => zeratul
48
- db.units.first_by_name 'Zeratul' # => zeratul
49
- db.units.all_by_name 'Zeratul' # => [zeratul]
50
-
51
- # query sugar, use {life: {_lt: 100}} instead of {life: {:$lt => 100}}
52
- Mongo.defaults.merge! convert_underscore_to_dollar: true
53
- db.units.all 'stats.life' => {_lt: 100} # => [tassadar]
54
- end
55
- end
File without changes
@@ -1,64 +0,0 @@
1
- require 'mongo_db/object'
2
- require 'rspec'
3
-
4
- describe "Object example" do
5
- defaults = nil
6
- before(:all){defaults = Mongo.defaults.clone}
7
- after :all do
8
- Mongo.defaults = defaults
9
- Object.send :remove_const, :Unit if Object.const_defined? :Unit
10
- end
11
-
12
- it do
13
- # let's define the game unit
14
- class Unit
15
- attr_reader :name, :stats
16
-
17
- # don't forget to allow creating object with no arguments
18
- def initialize name = nil, stats = nil
19
- @name, @stats = name, stats
20
- end
21
-
22
- class Stats
23
- attr_accessor :attack, :life, :shield
24
-
25
- def initialize attack = nil, life = nil, shield = nil
26
- @attack, @life, @shield = attack, life, shield
27
- end
28
- end
29
- end
30
-
31
- # connecting to MongoDB
32
- require 'mongo_db/object'
33
- Mongo.defaults.merge! symbolize: true, multi: true, safe: true
34
- connection = Mongo::Connection.new
35
- db = connection.db 'default_test'
36
-
37
- # create
38
- zeratul = Unit.new('Zeratul', Unit::Stats.new(85, 300, 100))
39
- tassadar = Unit.new('Tassadar', Unit::Stats.new(0, 80, 300))
40
-
41
- db.units.save zeratul
42
- db.units.save tassadar
43
-
44
- # udate (we made error - mistakenly set Tassadar's attack as zero, let's fix it)
45
- tassadar.stats.attack = 20
46
- db.units.save tassadar
47
-
48
- # querying first & all, there's also :each, the same as :all
49
- db.units.first name: 'Zeratul' # => zeratul
50
- db.units.all name: 'Zeratul' # => [zeratul]
51
- db.units.all name: 'Zeratul' do |unit|
52
- unit # => zeratul
53
- end
54
-
55
- # simple finders (bang versions also availiable)
56
- db.units.by_name 'Zeratul' # => zeratul
57
- db.units.first_by_name 'Zeratul' # => zeratul
58
- db.units.all_by_name 'Zeratul' # => [zeratul]
59
-
60
- # query sugar, use {life: {_lt: 100}} instead of {life: {:$lt => 100}}
61
- Mongo.defaults.merge! convert_underscore_to_dollar: true
62
- db.units.all('stats.life' => {_lt: 100}) # => [tassadar]
63
- end
64
- end