mongodb 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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