mongo_db 0.1.8 → 0.1.9

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.
@@ -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