mongodb 0.0.4 → 0.0.5

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,4 +1,4 @@
1
- require 'mongodb/gems'
1
+ require 'mongo/gems'
2
2
 
3
3
  require 'mongo'
4
4
 
@@ -9,7 +9,7 @@ class Mongo::NotFound < Mongo::Error; end
9
9
  database
10
10
  collection
11
11
  dynamic_finders
12
- ).each{|f| require "mongodb/driver/#{f}"}
12
+ ).each{|f| require "mongo/driver/#{f}"}
13
13
 
14
14
  # defaults
15
15
  Mongo.class_eval do
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,8 @@
1
+ require 'mongo/driver'
2
+
3
+ class Mongo::Migration; end
4
+
5
+ %w(
6
+ definition
7
+ migration
8
+ ).each{|f| require "mongo/migration/#{f}"}
File without changes
File without changes
File without changes
@@ -1,9 +1,9 @@
1
- require 'mongodb/driver'
1
+ require 'mongo/driver'
2
2
 
3
3
  %w(
4
- object_serializer
4
+ object
5
5
  object_helper
6
- ).each{|f| require "mongodb/object/#{f}"}
6
+ ).each{|f| require "mongo/object/#{f}"}
7
7
 
8
8
  Mongo.defaults[:callbacks] = true
9
9
 
@@ -11,7 +11,7 @@ Mongo.defaults[:callbacks] = true
11
11
  Mongo::Collection.class_eval do
12
12
  include Mongo::ObjectHelper
13
13
 
14
- %w(insert update remove save).each do |method|
14
+ %w(create update save destroy).each do |method|
15
15
  alias_method "#{method}_without_object", method
16
16
  alias_method method, "#{method}_with_object"
17
17
  end
@@ -0,0 +1,284 @@
1
+ module Mongo::Object
2
+ attr_accessor :_id
3
+
4
+ def valid? opts = {}
5
+ opts = ::Mongo::Object.parse_options opts
6
+ begin
7
+ return false unless errors.empty?
8
+
9
+ return false if opts[:callbacks] and !::Mongo::Object.run_callbacks(self, :before, :validate)
10
+
11
+ child_opts = opts.merge internal: true
12
+ if child_objects.all?{|group| group.all?{|obj| obj.valid?(child_opts)}} and errors.empty?
13
+ ::Mongo::Object.run_callbacks(self, :after, :validate) if opts[:callbacks]
14
+ true
15
+ else
16
+ false
17
+ end
18
+ ensure
19
+ clear_child_objects unless opts[:internal]
20
+ end
21
+ end
22
+
23
+ def errors
24
+ @_errors ||= {}
25
+ end
26
+
27
+ def create_object collection, opts
28
+ with_object_callbacks :create, opts do |opts|
29
+ doc = ::Mongo::Object.to_mongo self
30
+ collection.create(doc, opts)
31
+ self._id = doc[:_id] || doc['_id']
32
+ end
33
+ end
34
+
35
+ def update_object collection, opts
36
+ with_object_callbacks :update, opts do |opts|
37
+ id = _id || "can't update object without _id (#{self})!"
38
+ doc = ::Mongo::Object.to_mongo self
39
+ collection.update({_id: id}, doc, opts)
40
+ end
41
+ end
42
+
43
+ def destroy_object collection, opts
44
+ with_object_callbacks :destroy, opts do |opts|
45
+ id = _id || "can't destroy object without _id (#{self})!"
46
+ collection.destroy({_id: id}, opts)
47
+ end
48
+ end
49
+
50
+ def save_object collection, opts
51
+ if _id
52
+ update_object collection, opts
53
+ else
54
+ create_object collection, opts
55
+ end
56
+ end
57
+
58
+ # need this to allow change it in specs
59
+ # RSpec adds @mock_proxy, and we need to skip it
60
+ SKIP_IV_REGEXP = /^@_/
61
+
62
+ class << self
63
+ def parse_options opts
64
+ opts = opts.clone
65
+ opts[:validate] = true unless opts.include?(:validate)
66
+ opts[:callbacks] = Mongo.defaults[:callbacks] unless opts.include?(:callbacks)
67
+ return opts
68
+ end
69
+
70
+ def to_mongo_options opts
71
+ opts = opts.clone
72
+ opts.delete :validate
73
+ opts.delete :callbacks
74
+ opts
75
+ end
76
+
77
+ def each_instance_variable obj, &block
78
+ obj.instance_variables.each do |iv_name|
79
+ # skipping variables starting with _xx, usually they
80
+ # have specific meaning and used for example for cache
81
+ next if iv_name =~ SKIP_IV_REGEXP
82
+
83
+ block.call iv_name, obj.instance_variable_get(iv_name)
84
+ end
85
+ end
86
+
87
+ # converts object to document (also works with nested & arrays)
88
+ def to_mongo obj
89
+ return obj.to_mongo if obj.respond_to? :to_mongo
90
+ symbolize = ::Mongo.defaults[:symbolize]
91
+
92
+ if obj.is_a? Hash
93
+ doc = {}
94
+ obj.each do |k, v|
95
+ doc[k] = to_mongo v
96
+ end
97
+ doc
98
+ elsif obj.is_a? Array
99
+ obj.collect{|v| to_mongo v}
100
+ elsif obj.is_a? Mongo::Object
101
+ doc = {}
102
+
103
+ # copying instance variables
104
+ each_instance_variable obj do |iv_name, v|
105
+ k = iv_name.to_s[1..-1]
106
+ k = k.to_sym if symbolize
107
+ doc[k] = to_mongo v
108
+ end
109
+
110
+ # adding _id & _class
111
+ id_key, class_key = symbolize ? [:_id, :_class] : ['_id', '_class']
112
+ id = instance_variable_get('@_id')
113
+ doc[id_key] = id if id
114
+ doc[class_key] = obj.class.name
115
+
116
+ doc
117
+ else # simple type
118
+ obj
119
+ end
120
+ end
121
+
122
+ def each_object obj, include_first = true, &block
123
+ if obj.is_a? Hash
124
+ obj.each{|k, v| each_object v, &block}
125
+ elsif obj.is_a? Array
126
+ obj.each{|v| each_object v, &block}
127
+ elsif obj.is_a? ::Mongo::Object
128
+ block.call obj if include_first
129
+ each_instance_variable obj do |iv_name, v|
130
+ each_object v, &block
131
+ end
132
+ end
133
+ nil
134
+ end
135
+
136
+ def build doc
137
+ return unless doc
138
+ obj = _build doc
139
+ obj.send :update_internal_state! if obj.is_a? ::Mongo::Object
140
+ obj
141
+ end
142
+
143
+ def run_callbacks obj, type, method_name
144
+ obj.respond_to?(:run_callbacks) ? obj.run_callbacks(type, method_name) : true
145
+ end
146
+
147
+ protected
148
+ def _build doc
149
+ if doc.is_a? Hash
150
+ if class_name = doc[:_class] || doc['_class']
151
+ klass = constantize class_name
152
+
153
+ if klass.respond_to? :from_mongo
154
+ klass.from_mongo doc
155
+ else
156
+ obj = klass.new
157
+ doc.each do |k, v|
158
+ next if k.to_sym == :_class
159
+
160
+ v = _build v
161
+ obj.instance_variable_set "@#{k}", v
162
+ end
163
+ obj
164
+ end
165
+ else
166
+ doc
167
+ end
168
+ elsif doc.is_a? Array
169
+ doc.collect{|v| _build v}
170
+ else
171
+ doc
172
+ end
173
+ end
174
+
175
+ def constantize class_name
176
+ @constantize_cache ||= {}
177
+ unless klass = @constantize_cache[class_name]
178
+ klass = eval class_name, TOPLEVEL_BINDING, __FILE__, __LINE__
179
+ @constantize_cache[class_name] = klass
180
+ end
181
+ klass
182
+ end
183
+ end
184
+
185
+ protected
186
+ attr_writer :_original_children
187
+ def _original_children
188
+ @_original_children ||= []
189
+ end
190
+
191
+ def update_internal_state!
192
+ return unless ::Mongo.defaults[:callbacks]
193
+
194
+ _original_children.clear
195
+ ::Mongo::Object.each_object self, false do |obj|
196
+ _original_children << obj
197
+ end
198
+ end
199
+
200
+ def clear_child_objects
201
+ if instance_variable_get(:@_child_objects)
202
+ child_objects.each do |group|
203
+ group.each{|obj| obj.clear_child_objects}
204
+ end
205
+ remove_instance_variable :@_child_objects
206
+ end
207
+ end
208
+
209
+ def child_objects
210
+ unless @_child_objects
211
+ created_children, updated_children, destroyed_children = [], [], []
212
+
213
+ original_children_ids = Set.new; _original_children.each{|obj| original_children_ids << obj.object_id}
214
+ ::Mongo::Object.each_object self, false do |obj|
215
+ (original_children_ids.include?(obj.object_id) ? updated_children : created_children) << obj
216
+ end
217
+
218
+ children_ids = Set.new; ::Mongo::Object.each_object(self, false){|obj| children_ids << obj.object_id}
219
+ destroyed_children = _original_children.select{|obj| !children_ids.include?(obj.object_id)}
220
+
221
+ @_child_objects = [created_children, updated_children, destroyed_children]
222
+ end
223
+ @_child_objects
224
+ end
225
+
226
+ def with_object_callbacks method_name, opts, &block
227
+ opts = ::Mongo::Object.parse_options opts
228
+
229
+ # validation
230
+ return false if opts[:validate] and !valid?(opts.merge(internal: true))
231
+
232
+ # before callbacks
233
+ return false if opts[:callbacks] and !run_all_callbacks(:before, method_name)
234
+
235
+ # saving
236
+ block.call ::Mongo::Object.to_mongo_options(opts)
237
+ update_internal_state!
238
+
239
+ # after callbacks
240
+ run_all_callbacks :after, method_name if opts[:callbacks]
241
+
242
+ true
243
+ ensure
244
+ clear_child_objects
245
+ end
246
+
247
+ # TODO1 move to static method
248
+ def run_all_callbacks type, method_name
249
+ result = if type == :before
250
+ ::Mongo::Object.run_callbacks self, type, method_name
251
+ else
252
+ true
253
+ end
254
+
255
+ result &= if method_name == :create
256
+ child_objects.all? do |group|
257
+ group.all? do |obj|
258
+ obj.run_all_callbacks type, method_name
259
+ end
260
+ end
261
+ elsif method_name == :update
262
+ created_children, updated_children, destroyed_children = child_objects
263
+ created_children.all?{|obj| obj.run_all_callbacks type, :create} and
264
+ updated_children.all?{|obj| obj.run_all_callbacks type, :update} and
265
+ destroyed_children.all?{|obj| obj.run_all_callbacks type, :destroy}
266
+ elsif method_name == :destroy
267
+ child_objects.all? do |group|
268
+ group.all? do |obj|
269
+ obj.run_all_callbacks type, method_name
270
+ end
271
+ end
272
+ else
273
+ raise_error "unknown callback method (#{method_name})!"
274
+ end
275
+
276
+ result &= if type == :after
277
+ ::Mongo::Object.run_callbacks self, type, method_name
278
+ else
279
+ true
280
+ end
281
+
282
+ result
283
+ end
284
+ end
@@ -0,0 +1,80 @@
1
+ module Mongo::ObjectHelper
2
+ #
3
+ # CRUD
4
+ #
5
+ def create_with_object doc, opts = {}
6
+ if doc.is_a? ::Mongo::Object
7
+ doc.create_object self, opts
8
+ else
9
+ create_without_object doc, opts
10
+ end
11
+ end
12
+
13
+ def update_with_object *args
14
+ if args.first.is_a? ::Mongo::Object
15
+ doc, opts = args
16
+ opts ||= {}
17
+ doc.update_object self, opts
18
+ else
19
+ update_without_object *args
20
+ end
21
+ end
22
+
23
+ def save_with_object doc, opts = {}
24
+ if doc.is_a? ::Mongo::Object
25
+ doc.save_object self, opts
26
+ else
27
+ save_without_object doc, opts
28
+ end
29
+ end
30
+
31
+ def destroy_with_object *args
32
+ if args.first.is_a? ::Mongo::Object
33
+ doc, opts = args
34
+ opts ||= {}
35
+ doc.destroy_object self, opts
36
+ else
37
+ destroy_without_object *args
38
+ end
39
+ end
40
+
41
+ def create! *args
42
+ create(*args) || raise(Mongo::Error, "can't create #{doc.inspect}!")
43
+ end
44
+
45
+ def update! *args
46
+ update(*args) || raise(Mongo::Error, "can't update #{doc.inspect}!")
47
+ end
48
+
49
+ def save! *args
50
+ save(*args) || raise(Mongo::Error, "can't save #{doc.inspect}!")
51
+ end
52
+
53
+ def destroy! *args
54
+ destroy(*args) || raise(Mongo::Error, "can't destroy #{doc.inspect}!")
55
+ end
56
+
57
+
58
+ #
59
+ # Querying
60
+ #
61
+ def first selector = {}, opts = {}, &block
62
+ opts = opts.clone
63
+ if opts.delete(:object) == false
64
+ super selector, opts, &block
65
+ else
66
+ ::Mongo::Object.build super(selector, opts, &block)
67
+ end
68
+ end
69
+
70
+ def each selector = {}, opts = {}, &block
71
+ opts = opts.clone
72
+ if opts.delete(:object) == false
73
+ super selector, opts, &block
74
+ else
75
+ super selector, opts do |doc|
76
+ block.call ::Mongo::Object.build(doc)
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,4 @@
1
+ require 'mongo/driver/spec'
2
+
3
+ # RSpec adds some instance variables and we need to skip it.
4
+ Mongo::Object.send :const_set, :SKIP_IV_REGEXP, /^@_|^@mock_proxy/
@@ -7,7 +7,7 @@ shared_examples_for 'object CRUD' do
7
7
 
8
8
  # create
9
9
  db.units.save(@zeratul).should be_true
10
- @zeratul.instance_variable_get(:@_id).should_not be_nil
10
+ @zeratul._id.should_not be_nil
11
11
 
12
12
  # read
13
13
  db.units.count.should == 1
@@ -31,7 +31,7 @@ shared_examples_for 'embedded object CRUD' do
31
31
  it 'crud' do
32
32
  # create
33
33
  db.players.save(@player)
34
- @player.instance_variable_get(:@_id).should_not be_nil
34
+ @player._id.should_not be_nil
35
35
 
36
36
  # read
37
37
  db.players.count.should == 1
data/readme.md CHANGED
@@ -16,7 +16,7 @@ These enhancements alter the driver's API and made it more simple and intuitive.
16
16
  - 100% backward compatibility with original driver API (if not - it's a bug, report it please)
17
17
 
18
18
  ``` ruby
19
- require 'mongodb/driver'
19
+ require 'mongo/driver'
20
20
 
21
21
  # Changing some defaults.
22
22
  Mongo.defaults.merge! symbolize: true, multi: true, safe: true
@@ -67,7 +67,7 @@ So, please use standard Ruby driver documentation.
67
67
  Define migration steps, specify desired version and apply it (usually all this should be done via Rake task).
68
68
 
69
69
  ``` ruby
70
- require 'mongodb/migration'
70
+ require 'mongo/migration'
71
71
 
72
72
  # Connection & db.
73
73
  connection = Mongo::Connection.new
@@ -121,8 +121,16 @@ Save any Ruby object to MongoDB, as if it's a document. Objects can be any type,
121
121
  Note: the :initialize method should allow to create object without arguments.
122
122
 
123
123
  ``` ruby
124
+ # Connecting to MongoDB.
125
+ require 'mongo/object'
126
+ Mongo.defaults.merge! symbolize: true, multi: true, safe: true
127
+ connection = Mongo::Connection.new
128
+ db = connection.db 'default_test'
129
+ db.units.drop
130
+
124
131
  # Let's define the game unit.
125
132
  class Unit
133
+ include Mongo::Object
126
134
  attr_reader :name, :stats
127
135
 
128
136
  # don't forget to allow creating object with no arguments
@@ -131,6 +139,7 @@ class Unit
131
139
  end
132
140
 
133
141
  class Stats
142
+ include Mongo::Object
134
143
  attr_accessor :attack, :life, :shield
135
144
 
136
145
  def initialize attack = nil, life = nil, shield = nil
@@ -139,13 +148,6 @@ class Unit
139
148
  end
140
149
  end
141
150
 
142
- # Connecting to MongoDB.
143
- require 'mongodb/object'
144
- Mongo.defaults.merge! symbolize: true, multi: true, safe: true
145
- connection = Mongo::Connection.new
146
- db = connection.db 'default_test'
147
- db.units.drop
148
-
149
151
  # Create.
150
152
  zeratul = Unit.new('Zeratul', Unit::Stats.new(85, 300, 100))
151
153
  tassadar = Unit.new('Tassadar', Unit::Stats.new(0, 80, 300))
@@ -1,4 +1,4 @@
1
- require 'mongodb/driver'
1
+ require 'mongo/driver'
2
2
 
3
3
  Mongo.defaults.merge! \
4
4
  symbolize: true,
@@ -9,7 +9,7 @@ Mongo.defaults.merge! \
9
9
 
10
10
  require 'ruby_ext'
11
11
  require 'rspec_ext'
12
- require 'mongodb/driver/spec'
12
+ require 'mongo/driver/spec'
13
13
 
14
14
  #
15
15
  # Handy spec helpers
@@ -1,5 +1,5 @@
1
1
  require 'driver/spec_helper'
2
- require 'mongodb/migration'
2
+ require 'mongo/migration'
3
3
 
4
4
  describe "Migration" do
5
5
  with_mongo
@@ -5,11 +5,11 @@ describe 'Object callbacks' do
5
5
 
6
6
  before :all do
7
7
  class Player
8
- include RSpec::CallbackHelper
8
+ include Mongo::Object, RSpec::CallbackHelper
9
9
  attr_accessor :missions
10
10
 
11
11
  class Mission
12
- include RSpec::CallbackHelper
12
+ include Mongo::Object, RSpec::CallbackHelper
13
13
  end
14
14
  end
15
15
  end
@@ -22,49 +22,49 @@ describe 'Object callbacks' do
22
22
  end
23
23
 
24
24
  it 'create' do
25
- %w(before_validate before_save before_create after_create after_save after_validate).each do |name|
26
- @player.should_receive(name).once.ordered
27
- @mission.should_receive(name).once.ordered
25
+ %w(before_validate after_validate before_create after_create).each do |name|
26
+ @player.should_receive(name).once.ordered.and_return(true)
27
+ @mission.should_receive(name).once.ordered.and_return(true)
28
28
  end
29
29
 
30
- db.players.save @player
30
+ db.players.save(@player).should be_true
31
31
  end
32
32
 
33
33
  it 'update' do
34
- db.players.save(@player)
34
+ db.players.save(@player).should be_true
35
35
 
36
- %w(before_validate before_save before_update after_update after_save after_validate).each do |name|
37
- @player.should_receive(name).once.ordered
38
- @mission.should_receive(name).once.ordered
36
+ %w(before_validate after_validate before_update after_update).each do |name|
37
+ @player.should_receive(name).once.ordered.and_return(true)
38
+ @mission.should_receive(name).once.ordered.and_return(true)
39
39
  end
40
- db.players.save @player
40
+ db.players.save(@player).should be_true
41
41
  end
42
42
 
43
43
  it 'destroy' do
44
- db.players.save @player
44
+ db.players.save(@player).should be_true
45
45
 
46
- %w(before_validate before_destroy after_destroy after_validate).each do |name|
47
- @player.should_receive(name).once.ordered
48
- @mission.should_receive(name).once.ordered
46
+ %w(before_validate after_validate before_destroy after_destroy).each do |name|
47
+ @player.should_receive(name).once.ordered.and_return(true)
48
+ @mission.should_receive(name).once.ordered.and_return(true)
49
49
  end
50
- db.players.destroy @player
50
+ db.players.destroy(@player).should be_true
51
51
  end
52
52
 
53
53
  it 'should be able skip callbacks' do
54
- @player.should_not_receive(:_run_callbacks)
55
- @mission.should_not_receive(:_run_callbacks)
54
+ @player.should_not_receive(:run_callbacks)
55
+ @mission.should_not_receive(:run_callbacks)
56
56
 
57
- db.players.save @player, callbacks: false
57
+ db.players.save(@player, callbacks: false).should be_true
58
58
  db.players.count.should == 1
59
- db.players.save @player, callbacks: false
59
+ db.players.save(@player, callbacks: false).should be_true
60
60
  db.players.count.should == 1
61
- db.players.destroy @player, callbacks: false
61
+ db.players.destroy(@player, callbacks: false).should be_true
62
62
  db.players.count.should == 0
63
63
  end
64
64
 
65
65
  it 'should be able interrupt CRUD' do
66
- @mission.stub! :_run_callbacks do |type, method_name|
67
- false if type == :before and method_name == :save
66
+ @mission.stub! :run_callbacks do |type, method_name|
67
+ false if type == :before and method_name == :create
68
68
  end
69
69
  db.players.save(@player).should be_false
70
70
  db.players.count.should == 0
@@ -72,26 +72,26 @@ describe 'Object callbacks' do
72
72
 
73
73
  describe "embedded" do
74
74
  it 'should fire :destroy on detached objects' do
75
- db.players.save @player
75
+ db.players.save(@player).should be_true
76
76
  @player.missions.clear
77
- @mission.should_receive(:before_destroy).once
78
- db.players.destroy @player
77
+ @mission.should_receive(:before_destroy).once.and_return(true)
78
+ db.players.destroy(@player).should be_true
79
79
  end
80
80
 
81
81
  it 'should fire :destroy on deleted objects in update' do
82
- db.players.save @player
82
+ db.players.save(@player).should be_true
83
83
  @player.missions.clear
84
- @mission.should_receive(:before_destroy).once
85
- db.players.save @player
84
+ @mission.should_receive(:before_destroy).once.and_return(true)
85
+ db.players.save(@player).should be_true
86
86
  end
87
87
 
88
88
  it 'should fire :create on new objects in update' do
89
- db.players.save @player
89
+ db.players.save(@player).should be_true
90
90
  mission2 = Player::Mission.new
91
91
  @player.missions << mission2
92
- mission2.should_receive(:before_create).once
92
+ mission2.should_receive(:before_create).once.and_return(true)
93
93
  mission2.should_not_receive(:before_update)
94
- db.players.save @player
94
+ db.players.save(@player).should be_true
95
95
  end
96
96
  end
97
97
  end
@@ -1,5 +1,5 @@
1
1
  require 'object/spec_helper'
2
- require 'object/crud_shared'
2
+ require 'mongo/object/spec/crud_shared'
3
3
 
4
4
  describe "Object CRUD" do
5
5
  with_mongo
@@ -7,6 +7,8 @@ describe "Object CRUD" do
7
7
  describe 'simple' do
8
8
  before :all do
9
9
  class Unit2
10
+ include Mongo::Object
11
+
10
12
  def initialize name = nil, info = nil; @name, @info = name, info end
11
13
  attr_accessor :name, :info
12
14
  def == o; [self.class, name, info] == [o.class, o.respond_to(:name), o.respond_to(:info)] end
@@ -29,10 +31,14 @@ describe "Object CRUD" do
29
31
  describe 'embedded' do
30
32
  before :all do
31
33
  class Player2
34
+ include Mongo::Object
35
+
32
36
  attr_accessor :missions
33
37
  def == o; [self.class, self.missions] == [o.class, o.respond_to(:missions)] end
34
38
 
35
39
  class Mission
40
+ include Mongo::Object
41
+
36
42
  def initialize name = nil, stats = nil; @name, @stats = name, stats end
37
43
  attr_accessor :name, :stats
38
44
  def == o; [self.class, self.name, self.stats] == [o.class, o.respond_to(:name), o.respond_to(:stats)] end
@@ -1,14 +1,14 @@
1
- require 'mongodb/object'
2
- require 'driver/spec_helper'
1
+ require 'mongo/object'
2
+
3
+ require 'rspec_ext'
4
+ require 'mongo/object/spec'
3
5
 
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
6
+ require 'driver/spec_helper'
7
7
 
8
8
  # To simplify callback expectations
9
9
  module RSpec::CallbackHelper
10
- def _run_callbacks type, method_name
10
+ def run_callbacks type, method_name
11
11
  callback_method_name = :"#{type}_#{method_name}"
12
- send callback_method_name if respond_to? callback_method_name
12
+ respond_to?(callback_method_name) ? send(callback_method_name) : true
13
13
  end
14
14
  end
@@ -5,9 +5,12 @@ describe 'Object validation' do
5
5
 
6
6
  before :all do
7
7
  class Player
8
+ include Mongo::Object
9
+
8
10
  attr_accessor :missions
9
11
 
10
12
  class Mission
13
+ include Mongo::Object
11
14
  end
12
15
  end
13
16
  end
@@ -21,56 +24,56 @@ describe 'Object validation' do
21
24
 
22
25
  it 'should not save/update/destroy invalid objects' do
23
26
  # create
24
- @player.stub!(:_valid?).and_return(false)
27
+ @player.stub!(:valid?).and_return(false)
25
28
  db.players.save(@player).should be_false
26
29
 
27
- @player.stub!(:_valid?).and_return(true)
30
+ @player.stub!(:valid?).and_return(true)
28
31
  db.players.save(@player).should be_true
29
32
 
30
33
  # update
31
- @player.stub!(:_valid?).and_return(false)
34
+ @player.stub!(:valid?).and_return(false)
32
35
  db.players.save(@player).should be_false
33
36
 
34
- @player.stub!(:_valid?).and_return(true)
37
+ @player.stub!(:valid?).and_return(true)
35
38
  db.players.save(@player).should be_true
36
39
 
37
40
  # destroy
38
- @player.stub!(:_valid?).and_return(false)
41
+ @player.stub!(:valid?).and_return(false)
39
42
  db.players.destroy(@player).should be_false
40
43
 
41
- @player.stub!(:_valid?).and_return(true)
44
+ @player.stub!(:valid?).and_return(true)
42
45
  db.players.destroy(@player).should be_true
43
46
  end
44
47
 
45
48
  it 'should not save/update/destroy invalid embedded objects' do
46
49
  # create
47
- @mission.stub!(:_valid?).and_return(false)
50
+ @mission.stub!(:valid?).and_return(false)
48
51
  db.players.save(@player).should be_false
49
52
 
50
- @mission.stub!(:_valid?).and_return(true)
53
+ @mission.stub!(:valid?).and_return(true)
51
54
  db.players.save(@player).should be_true
52
55
 
53
56
  # update
54
- @mission.stub!(:_valid?).and_return(false)
57
+ @mission.stub!(:valid?).and_return(false)
55
58
  db.players.save(@player).should be_false
56
59
 
57
- @mission.stub!(:_valid?).and_return(true)
60
+ @mission.stub!(:valid?).and_return(true)
58
61
  db.players.save(@player).should be_true
59
62
 
60
63
  # destroy
61
- @mission.stub!(:_valid?).and_return(false)
64
+ @mission.stub!(:valid?).and_return(false)
62
65
  db.players.destroy(@player).should be_false
63
66
 
64
- @mission.stub!(:_valid?).and_return(true)
67
+ @mission.stub!(:valid?).and_return(true)
65
68
  db.players.destroy(@player).should be_true
66
69
  end
67
70
 
68
71
  it "should be able skip validation" do
69
- @player.stub!(:_valid?).and_return(false)
72
+ @player.stub!(:valid?).and_return(false)
70
73
  db.players.save(@player, validate: false).should be_true
71
74
 
72
- @player.stub!(:_valid?).and_return(true)
73
- @mission.stub!(:_valid?).and_return(false)
75
+ @player.stub!(:valid?).and_return(true)
76
+ @mission.stub!(:valid?).and_return(false)
74
77
  db.players.save(@player, validate: false).should be_true
75
78
  end
76
79
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongodb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,19 +9,8 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-08-28 00:00:00.000000000Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: mongo
16
- requirement: &2820900 !ruby/object:Gem::Requirement
17
- none: false
18
- requirements:
19
- - - ~>
20
- - !ruby/object:Gem::Version
21
- version: '1.3'
22
- type: :runtime
23
- prerelease: false
24
- version_requirements: *2820900
12
+ date: 2011-08-30 00:00:00.000000000Z
13
+ dependencies: []
25
14
  description:
26
15
  email:
27
16
  executables: []
@@ -30,19 +19,21 @@ extra_rdoc_files: []
30
19
  files:
31
20
  - Rakefile
32
21
  - readme.md
33
- - lib/mongodb/driver/collection.rb
34
- - lib/mongodb/driver/database.rb
35
- - lib/mongodb/driver/dynamic_finders.rb
36
- - lib/mongodb/driver/spec.rb
37
- - lib/mongodb/driver.rb
38
- - lib/mongodb/gems.rb
39
- - lib/mongodb/migration/definition.rb
40
- - lib/mongodb/migration/migration.rb
41
- - lib/mongodb/migration/tasks.rb
42
- - lib/mongodb/migration.rb
43
- - lib/mongodb/object/object_helper.rb
44
- - lib/mongodb/object/object_serializer.rb
45
- - lib/mongodb/object.rb
22
+ - lib/mongo/driver/collection.rb
23
+ - lib/mongo/driver/database.rb
24
+ - lib/mongo/driver/dynamic_finders.rb
25
+ - lib/mongo/driver/spec.rb
26
+ - lib/mongo/driver.rb
27
+ - lib/mongo/gems.rb
28
+ - lib/mongo/migration/definition.rb
29
+ - lib/mongo/migration/migration.rb
30
+ - lib/mongo/migration/tasks.rb
31
+ - lib/mongo/migration.rb
32
+ - lib/mongo/object/object.rb
33
+ - lib/mongo/object/object_helper.rb
34
+ - lib/mongo/object/spec/crud_shared.rb
35
+ - lib/mongo/object/spec.rb
36
+ - lib/mongo/object.rb
46
37
  - spec/driver/collection_spec.rb
47
38
  - spec/driver/crud_spec.rb
48
39
  - spec/driver/database_spec.rb
@@ -52,7 +43,6 @@ files:
52
43
  - spec/driver/spec_helper.rb
53
44
  - spec/migration/migration_spec.rb
54
45
  - spec/object/callbacks_spec.rb
55
- - spec/object/crud_shared.rb
56
46
  - spec/object/crud_spec.rb
57
47
  - spec/object/spec_helper.rb
58
48
  - spec/object/validation_spec.rb
@@ -1,8 +0,0 @@
1
- require 'mongodb/driver'
2
-
3
- class Mongo::Migration; end
4
-
5
- %w(
6
- definition
7
- migration
8
- ).each{|f| require "mongodb/migration/#{f}"}
@@ -1,62 +0,0 @@
1
- module Mongo::ObjectHelper
2
- #
3
- # CRUD
4
- #
5
- def save_with_object doc, opts = {}
6
- if doc.is_a? Hash
7
- save_without_object doc, opts
8
- else
9
- ::Mongo::ObjectSerializer.new(doc).save opts, self
10
- end
11
- end
12
-
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
19
- end
20
-
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
28
- end
29
-
30
- def remove_with_object arg = {}, opts = {}
31
- if arg.is_a? Hash
32
- remove_without_object arg, opts
33
- else
34
- ::Mongo::ObjectSerializer.new(arg).remove opts, self
35
- end
36
- end
37
-
38
- def save! doc, opts = {}
39
- save(doc, opts) || raise(Mongo::Error, "can't save #{doc.inspect}!")
40
- end
41
-
42
-
43
- #
44
- # Querying
45
- #
46
- def first selector = {}, opts = {}, &block
47
- opts = opts.clone
48
- object = (opts.delete(:object) == false) ? false : true
49
- doc = super selector, opts, &block
50
- object ? ::Mongo::ObjectSerializer.build(doc) : doc
51
- end
52
-
53
- def each selector = {}, opts = {}, &block
54
- opts = opts.clone
55
- object = (opts.delete(:object) == false) ? false : true
56
- super selector, opts do |doc|
57
- doc = ::Mongo::ObjectSerializer.build(doc) if object
58
- block.call doc
59
- end
60
- nil
61
- end
62
- end
@@ -1,273 +0,0 @@
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_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_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_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, *callbacks
131
- callbacks.each do |type, method_name|
132
- objects.each do |obj|
133
- if obj.respond_to? :_run_callbacks
134
- return false if obj._run_callbacks(type, method_name) == false
135
- end
136
- end
137
- end
138
- true
139
- end
140
-
141
- def objects
142
- @objects_cache ||= begin
143
- objects = []
144
- _each_object(object){|obj| objects << obj}
145
- objects
146
- end
147
- end
148
-
149
- def update_internal_state!
150
- self.original_objects = objects if Mongo.defaults[:callbacks]
151
- end
152
-
153
- protected
154
- def original_objects; object.instance_variable_get(:@_original_objects) end
155
- def original_objects= objects; object.instance_variable_set(:@_original_objects, objects) end
156
-
157
- def parse_object_options opts
158
- opts = opts.clone
159
- validate = opts.delete(:validate) == false ? false : true
160
- callbacks = opts.delete(:callbacks) == false ? false : Mongo.defaults[:callbacks]
161
- return opts, validate, callbacks
162
- end
163
-
164
- # need this to allow change it in specs
165
- # RSpec adds @mock_proxy, and we need to skip it
166
- SKIP_IV_REGEXP = /^@_/
167
-
168
- def _each_instance_variable obj, &block
169
- obj.instance_variables.each do |iv_name|
170
- # skipping variables starting with _xx, usually they
171
- # have specific meaning and used for example for cache
172
- next if iv_name =~ SKIP_IV_REGEXP
173
-
174
- block.call iv_name, obj.instance_variable_get(iv_name)
175
- end
176
- end
177
-
178
- # converts object to document (also works with nested & arrays)
179
- def _to_document obj
180
- return obj.to_mongo if obj.respond_to? :to_mongo
181
-
182
- if obj.is_a? Hash
183
- doc = {}
184
- obj.each do |k, v|
185
- doc[k] = _to_document v
186
- end
187
- doc
188
- elsif obj.is_a? Array
189
- obj.collect{|v| _to_document v}
190
- elsif SIMPLE_TYPES.include? obj.class
191
- obj
192
- else
193
- doc = {}
194
-
195
- # copying instance variables
196
- _each_instance_variable obj do |iv_name, v|
197
- k = iv_name.to_s[1..-1]
198
- k = k.to_sym if Mongo.defaults[:symbolize]
199
- doc[k] = _to_document v
200
- end
201
-
202
- # adding _id & _class
203
- id_key, class_key = Mongo.defaults[:symbolize] ? [:_id, :_class] : ['_id', '_class']
204
- id = instance_variable_get('@_id')
205
- doc[id_key] = id if id
206
- doc[class_key] = obj.class.name
207
-
208
- doc
209
- end
210
- end
211
-
212
- def _each_object obj, &block
213
- if obj.is_a? Hash
214
- obj.each{|k, v| _each_object v, &block}
215
- elsif obj.is_a? Array
216
- obj.each{|v| _each_object v, &block}
217
- elsif SIMPLE_TYPES.include? obj.class
218
- else
219
- block.call obj
220
- _each_instance_variable obj do |iv_name, v|
221
- _each_object v, &block
222
- end
223
- end
224
- nil
225
- end
226
-
227
-
228
- class << self
229
- def build doc
230
- obj = _build doc
231
- serializer = Mongo::ObjectSerializer.new obj
232
- serializer.update_internal_state!
233
- obj
234
- end
235
-
236
- protected
237
- def _build doc
238
- if doc.is_a? Hash
239
- if class_name = doc[:_class] || doc['_class']
240
- klass = constantize class_name
241
-
242
- if klass.respond_to? :to_object
243
- klass.to_object doc
244
- else
245
- obj = klass.new
246
- doc.each do |k, v|
247
- next if k.to_sym == :_class
248
-
249
- v = _build v
250
- obj.instance_variable_set "@#{k}", v
251
- end
252
- obj
253
- end
254
- else
255
- doc
256
- end
257
- elsif doc.is_a? Array
258
- doc.collect{|v| _build v}
259
- else
260
- doc
261
- end
262
- end
263
-
264
- def constantize class_name
265
- @constantize_cache ||= {}
266
- unless klass = @constantize_cache[class_name]
267
- klass = eval class_name, TOPLEVEL_BINDING, __FILE__, __LINE__
268
- @constantize_cache[class_name] = klass
269
- end
270
- klass
271
- end
272
- end
273
- end