mongoid_optimistic_locking 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE +7 -0
- data/README.md +57 -0
- data/Rakefile +6 -0
- data/lib/config/locales/en.yml +9 -0
- data/lib/mongoid/errors/stale_document.rb +28 -0
- data/lib/mongoid/lockable.rb +13 -0
- data/lib/mongoid/optimistic_locking.rb +39 -0
- data/lib/mongoid/optimistic_locking/deprecated.rb +12 -0
- data/lib/mongoid/optimistic_locking/lock_version.rb +29 -0
- data/lib/mongoid/optimistic_locking/operations.rb +53 -0
- data/lib/mongoid/optimistic_locking/threaded_with_unlocked.rb +30 -0
- data/lib/mongoid/optimistic_locking/unlocked.rb +26 -0
- data/lib/mongoid/optimistic_locking/version.rb +5 -0
- data/lib/mongoid_optimistic_locking.rb +1 -0
- data/mongoid_optimistic_locking.gemspec +29 -0
- data/spec/lockable_spec.rb +119 -0
- data/spec/optimistic_locking_spec.rb +107 -0
- data/spec/spec_helper.rb +16 -0
- metadata +116 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Copyright (c) 2012 Boxee
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# mongoid\_optimistic\_locking
|
2
|
+
|
3
|
+
This gem helps to abstract the ["Update if Current"](http://www.mongodb.org/display/DOCS/Atomic+Operations#AtomicOperations-%22UpdateifCurrent%22) method which may be used as a replacement for [transactions in Mongo](http://docs.mongodb.org/manual/faq/developers/#how-do-i-do-transactions-and-locking-in-mongodb).
|
4
|
+
|
5
|
+
The gem is an addon over [Mongoid ODM](http://mongoid.org/) and is based on [ActiveRecord's Optimistic Locking](http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html).
|
6
|
+
|
7
|
+
## Compatibility
|
8
|
+
|
9
|
+
So far it works with the Rails 3 and Mongoid 2.4.
|
10
|
+
|
11
|
+
Created branch for edge *Mongoid 3*.
|
12
|
+
|
13
|
+
## Rails 3 Installation
|
14
|
+
|
15
|
+
Add the gem to your `Gemfile`:
|
16
|
+
|
17
|
+
gem 'mongoid_optimistic_locking'
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
To use it, all you have to do is add include `Mongoid::OptimisticLocking`:
|
22
|
+
|
23
|
+
class Post
|
24
|
+
include Mongoid::Document
|
25
|
+
include Mongoid::OptimisticLocking
|
26
|
+
|
27
|
+
field :text
|
28
|
+
end
|
29
|
+
|
30
|
+
This will add a `_lock_version` field in the document which will be incremented every time a save is called.
|
31
|
+
Be sure to rescue `Mongoid::Errors::StaleDocument` to handle applicative logic in case the object was changed.
|
32
|
+
|
33
|
+
For example:
|
34
|
+
|
35
|
+
class PostController < ApplicationController
|
36
|
+
## Adds an "UPDATE: ...some text..." to an existing document
|
37
|
+
def add_update
|
38
|
+
begin
|
39
|
+
post = Post.find(params[:id])
|
40
|
+
post.text += "---UPDATE--- " + params[:more_text]
|
41
|
+
post.save
|
42
|
+
rescue Mongoid::Errors::StaleDocument
|
43
|
+
retry
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
That's it!
|
49
|
+
|
50
|
+
## Open sourced by
|
51
|
+
|
52
|
+
[Boxee](http://www.boxee.tv)
|
53
|
+
|
54
|
+
## References
|
55
|
+
[Mongo Developer FAQ - How do I do transactions/locking?](http://docs.mongodb.org/manual/faq/developers/#how-do-i-do-transactions-and-locking-in-mongodb)
|
56
|
+
|
57
|
+
[Mongo Atomic Operations - "Update if Current"](http://www.mongodb.org/display/DOCS/Atomic+Operations#AtomicOperations-%22UpdateifCurrent%22)
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'mongoid/errors/mongoid_error'
|
2
|
+
|
3
|
+
module Mongoid
|
4
|
+
module Errors
|
5
|
+
|
6
|
+
# Raised when trying to update a document that has been updated by
|
7
|
+
# another process.
|
8
|
+
#
|
9
|
+
# @example Create the error.
|
10
|
+
# StaleDocument.new('update', document)
|
11
|
+
class StaleDocument < MongoidError
|
12
|
+
|
13
|
+
attr_reader :action, :document
|
14
|
+
|
15
|
+
def initialize(action, document)
|
16
|
+
@action = action
|
17
|
+
@document = document
|
18
|
+
|
19
|
+
super(
|
20
|
+
translate(
|
21
|
+
"stale_document.#{action}",
|
22
|
+
{ :klass => document.class.name }
|
23
|
+
)
|
24
|
+
)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Mongoid
|
2
|
+
|
3
|
+
module Lockable
|
4
|
+
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
ActiveSupport::Deprecation.warn 'Mongoid::Lockable is deprecated and will be removed. Use Mongoid::OptimisticLocking instead.', caller
|
9
|
+
include Mongoid::OptimisticLocking
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'mongoid'
|
2
|
+
require 'mongoid/errors/stale_document'
|
3
|
+
require 'mongoid/lockable'
|
4
|
+
require 'mongoid/optimistic_locking/deprecated'
|
5
|
+
require 'mongoid/optimistic_locking/lock_version'
|
6
|
+
require 'mongoid/optimistic_locking/operations'
|
7
|
+
require 'mongoid/optimistic_locking/threaded_with_unlocked'
|
8
|
+
require 'mongoid/optimistic_locking/unlocked'
|
9
|
+
require 'mongoid/optimistic_locking/version'
|
10
|
+
|
11
|
+
# monkey patch Threaded
|
12
|
+
Mongoid::Threaded.send :include, Mongoid::OptimisticLocking::ThreadedWithUnlocked
|
13
|
+
|
14
|
+
# add english load path to translations
|
15
|
+
I18n.load_path << File.expand_path('../../config/locales/en.yml', __FILE__)
|
16
|
+
|
17
|
+
module Mongoid
|
18
|
+
# == What is Optimistic Locking
|
19
|
+
#
|
20
|
+
# See <http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html>.
|
21
|
+
#
|
22
|
+
# == Usage
|
23
|
+
#
|
24
|
+
# TODO ...
|
25
|
+
module OptimisticLocking
|
26
|
+
|
27
|
+
extend ActiveSupport::Concern
|
28
|
+
|
29
|
+
include Deprecated
|
30
|
+
include LockVersion
|
31
|
+
include Operations
|
32
|
+
include Unlocked
|
33
|
+
|
34
|
+
included do
|
35
|
+
field LOCKING_FIELD, :type => Integer, :default => 0
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module OptimisticLocking
|
3
|
+
module Deprecated
|
4
|
+
|
5
|
+
def save_optimistic!(*args)
|
6
|
+
ActiveSupport::Deprecation.warn 'save_optimistic! is deprecated and will be removed. Use save or save! instead', caller
|
7
|
+
save! *args
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module OptimisticLocking
|
3
|
+
module LockVersion
|
4
|
+
|
5
|
+
LOCKING_FIELD = :_lock_version
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
attr_reader :lock_version_for_selector
|
10
|
+
|
11
|
+
def set_lock_version_for_selector
|
12
|
+
@lock_version_for_selector = self[LOCKING_FIELD]
|
13
|
+
yield
|
14
|
+
rescue Exception
|
15
|
+
@lock_version_for_selector = nil
|
16
|
+
raise
|
17
|
+
end
|
18
|
+
|
19
|
+
def increment_lock_version
|
20
|
+
self[LOCKING_FIELD] = self[LOCKING_FIELD] ? self[LOCKING_FIELD] + 1 : 1
|
21
|
+
yield
|
22
|
+
rescue Exception
|
23
|
+
self[LOCKING_FIELD] = self[LOCKING_FIELD] ? self[LOCKING_FIELD] - 1 : 0
|
24
|
+
raise
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module OptimisticLocking
|
3
|
+
module Operations
|
4
|
+
|
5
|
+
def insert(*args)
|
6
|
+
return super unless optimistic_locking?
|
7
|
+
increment_lock_version do
|
8
|
+
super
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def update(*args)
|
13
|
+
return super unless optimistic_locking?
|
14
|
+
set_lock_version_for_selector do
|
15
|
+
increment_lock_version do
|
16
|
+
result = super
|
17
|
+
unless Mongoid.database.command({:getlasterror => 1})['updatedExisting']
|
18
|
+
raise Mongoid::Errors::StaleDocument.new('update', self)
|
19
|
+
end
|
20
|
+
result
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def remove(*args)
|
26
|
+
return super unless optimistic_locking?
|
27
|
+
set_lock_version_for_selector do
|
28
|
+
result = super
|
29
|
+
unless Mongoid.database.command({:getlasterror => 1})['updatedExisting']
|
30
|
+
raise Mongoid::Errors::StaleDocument.new('destroy', self)
|
31
|
+
end
|
32
|
+
result
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def atomic_selector
|
37
|
+
result = super
|
38
|
+
if optimistic_locking? && lock_version_for_selector
|
39
|
+
key =
|
40
|
+
if metadata && metadata.embedded?
|
41
|
+
path = metadata.path(self)
|
42
|
+
"#{path.path}._lock_version"
|
43
|
+
else
|
44
|
+
'_lock_version'
|
45
|
+
end
|
46
|
+
result[key] = lock_version_for_selector
|
47
|
+
end
|
48
|
+
result
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module OptimisticLocking
|
3
|
+
module ThreadedWithUnlocked
|
4
|
+
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
|
9
|
+
def optimistic_locking?
|
10
|
+
!unlocked
|
11
|
+
end
|
12
|
+
|
13
|
+
def unlocked
|
14
|
+
!!Thread.current["[mongoid]:unlocked"]
|
15
|
+
end
|
16
|
+
|
17
|
+
def unlocked=(value)
|
18
|
+
Thread.current["[mongoid]:unlocked"] = value
|
19
|
+
end
|
20
|
+
|
21
|
+
def clear_options!
|
22
|
+
self.unlocked = false
|
23
|
+
super
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module OptimisticLocking
|
3
|
+
module Unlocked
|
4
|
+
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
def unlocked
|
8
|
+
Threaded.unlocked = true
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
def optimistic_locking?
|
13
|
+
Threaded.optimistic_locking?
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
|
18
|
+
def unlocked
|
19
|
+
Threaded.unlocked = true
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'mongoid/optimistic_locking'
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "mongoid/optimistic_locking/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "mongoid_optimistic_locking"
|
7
|
+
s.version = Mongoid::OptimisticLocking::VERSION
|
8
|
+
s.authors = ["Alon Burg", "John Nishinaga"]
|
9
|
+
s.email = ["burgalon@gmail.com", "jingoro@casa-z.org"]
|
10
|
+
s.homepage = "https://github.com/burgalon/mongoid_optimistic_locking"
|
11
|
+
s.summary = %q{Allows optimisitic locking for Mongoid models}
|
12
|
+
s.description = %q{Allows optimisitic locking for Mongoid models. See https://github.com/burgalon/mongoid_optimistic_locking}
|
13
|
+
|
14
|
+
s.add_dependency 'mongoid', '~> 2.4'
|
15
|
+
s.add_development_dependency 'rake', '~> 0.9.0'
|
16
|
+
s.add_development_dependency 'rspec', '~> 2.6'
|
17
|
+
s.add_development_dependency 'bson_ext', '~> 1.5'
|
18
|
+
|
19
|
+
s.rubyforge_project = "mongoid_optimistic_locking"
|
20
|
+
|
21
|
+
s.files = `git ls-files`.split("\n")
|
22
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
23
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
24
|
+
s.require_paths = ["lib"]
|
25
|
+
|
26
|
+
# specify any dependencies here; for example:
|
27
|
+
# s.add_development_dependency "rspec"
|
28
|
+
# s.add_runtime_dependency "rest-client"
|
29
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Post
|
4
|
+
include Mongoid::Document
|
5
|
+
::ActiveSupport::Deprecation.silence do
|
6
|
+
include Mongoid::Lockable
|
7
|
+
end
|
8
|
+
|
9
|
+
field :title
|
10
|
+
|
11
|
+
embeds_many :comments
|
12
|
+
end
|
13
|
+
|
14
|
+
class Comment
|
15
|
+
include Mongoid::Document
|
16
|
+
::ActiveSupport::Deprecation.silence do
|
17
|
+
include Mongoid::Lockable
|
18
|
+
end
|
19
|
+
|
20
|
+
field :text
|
21
|
+
embedded_in :post
|
22
|
+
end
|
23
|
+
|
24
|
+
describe Mongoid::Lockable do
|
25
|
+
|
26
|
+
before { ::ActiveSupport::Deprecation.silenced = true }
|
27
|
+
after { ::ActiveSupport::Deprecation.silenced = false }
|
28
|
+
|
29
|
+
before :all do
|
30
|
+
@post = Post.create(:text => 'original-text')
|
31
|
+
@post._lock_version.should == 1
|
32
|
+
end
|
33
|
+
|
34
|
+
it "simulate a migration situation in which _lock_version did not exist" do
|
35
|
+
Post.update_all(:_lock_version => nil)
|
36
|
+
@post.reload.save_optimistic!.should == true
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "test root documents" do
|
40
|
+
it "saves regularly if there's no other process changing the data in the background" do
|
41
|
+
@post.text = 'changed-text'
|
42
|
+
@post.save_optimistic!.should == true
|
43
|
+
@post._lock_version.should == 2
|
44
|
+
@post.reload
|
45
|
+
@post.text.should == 'changed-text'
|
46
|
+
@post._lock_version.should == 2
|
47
|
+
end
|
48
|
+
|
49
|
+
it "raises Stale exception if another process/background code updates the object" do
|
50
|
+
post_clone = Post.find(@post.id)
|
51
|
+
# the before_filter should increments the version by one, thus making the object stale
|
52
|
+
post_clone.text = 'changed-in-background'
|
53
|
+
post_clone.save
|
54
|
+
|
55
|
+
@post.text = 'changed-text'
|
56
|
+
expect { @post.save_optimistic! }.to raise_error(Mongoid::Errors::StaleDocument)
|
57
|
+
|
58
|
+
# Should fail again if still not refreshed
|
59
|
+
# i.e: test that _lock_version is decremented upon failure
|
60
|
+
expect { @post.save_optimistic! }.to raise_error(Mongoid::Errors::StaleDocument)
|
61
|
+
|
62
|
+
# Test StaleDocument error
|
63
|
+
begin
|
64
|
+
@post.save_optimistic!
|
65
|
+
rescue Mongoid::Errors::StaleDocument => e
|
66
|
+
e.message().should match('Post')
|
67
|
+
end
|
68
|
+
|
69
|
+
@post.reload
|
70
|
+
@post.text.should == 'changed-in-background'
|
71
|
+
|
72
|
+
post_clone.text = 'changed-in-background'
|
73
|
+
post_clone.save
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "test embedded documents" do
|
78
|
+
before :all do
|
79
|
+
@post = Post.create(:text => 'original-text')
|
80
|
+
@comment = @post.comments.create!(:text => 'First comment')
|
81
|
+
end
|
82
|
+
|
83
|
+
it "saves regularly if there's no other process changing the data in the background" do
|
84
|
+
@comment.text = 'First comment updated!'
|
85
|
+
@comment.save_optimistic!.should == true
|
86
|
+
@comment._lock_version.should == 2
|
87
|
+
@comment.reload
|
88
|
+
@comment.text.should == 'First comment updated!'
|
89
|
+
@comment._lock_version.should == 2
|
90
|
+
end
|
91
|
+
|
92
|
+
it "raises Stale exception if another process/background code updates the object" do
|
93
|
+
@comment_clone = Post.find(@post.id).comments.find(@comment.id)
|
94
|
+
# the before_filter should increments the version by one, thus making the object stale
|
95
|
+
@comment_clone.text = 'changed-in-background'
|
96
|
+
@comment_clone.save
|
97
|
+
|
98
|
+
@comment.text = 'changed-text'
|
99
|
+
expect { @comment.save_optimistic! }.to raise_error(Mongoid::Errors::StaleDocument)
|
100
|
+
|
101
|
+
# Should fail again if still not refreshed
|
102
|
+
# i.e: test that _lock_version is decremented upon failure
|
103
|
+
expect { @comment.save_optimistic! }.to raise_error(Mongoid::Errors::StaleDocument)
|
104
|
+
|
105
|
+
# Test StaleDocument error
|
106
|
+
begin
|
107
|
+
@comment.save_optimistic!
|
108
|
+
rescue Mongoid::Errors::StaleDocument => e
|
109
|
+
e.message().should match('Comment')
|
110
|
+
end
|
111
|
+
|
112
|
+
@comment.reload
|
113
|
+
@comment.text.should == 'changed-in-background'
|
114
|
+
|
115
|
+
@comment_clone.text = 'changed-in-background'
|
116
|
+
@comment_clone.save
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Mongoid::OptimisticLocking do
|
4
|
+
|
5
|
+
context 'without optimistic locking' do
|
6
|
+
|
7
|
+
let(:company_class) do
|
8
|
+
Class.new do
|
9
|
+
include Mongoid::Document
|
10
|
+
self.collection_name = 'companies'
|
11
|
+
field :name
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should allow collisions' do
|
16
|
+
c1 = company_class.create!(:name => 'Acme')
|
17
|
+
c2 = company_class.find(c1.id)
|
18
|
+
c1.name = 'Biz'
|
19
|
+
c1.save.should == true
|
20
|
+
c2.name = 'Baz'
|
21
|
+
c2.save.should == true
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'three instances of the same document' do
|
27
|
+
|
28
|
+
let(:person_class) do
|
29
|
+
Class.new do
|
30
|
+
include Mongoid::Document
|
31
|
+
include Mongoid::OptimisticLocking
|
32
|
+
self.collection_name = 'people'
|
33
|
+
field :name
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
before do
|
38
|
+
@p1 = person_class.create!(:name => 'Bob')
|
39
|
+
@p2 = person_class.find(@p1.id)
|
40
|
+
@p3 = person_class.find(@p1.id)
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'after updating the first' do
|
44
|
+
|
45
|
+
before do
|
46
|
+
@p1.name = 'Michael'
|
47
|
+
@p1.save.should == true
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should fail when updating the second' do
|
51
|
+
expect {
|
52
|
+
@p2.name = 'George'
|
53
|
+
@p2.save
|
54
|
+
}.to raise_error(Mongoid::Errors::StaleDocument)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should succeed when updating the second without locking' do
|
58
|
+
@p2.name = 'George'
|
59
|
+
@p2.unlocked.save.should == true
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should succeed when updating the second without locking, ' +
|
63
|
+
'then fail when updating the third' do
|
64
|
+
@p2.name = 'George'
|
65
|
+
@p2.unlocked.save.should == true
|
66
|
+
expect {
|
67
|
+
@p3.name = 'Sally'
|
68
|
+
@p3.save
|
69
|
+
}.to raise_error(Mongoid::Errors::StaleDocument)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should fail when destroying the second' do
|
73
|
+
expect {
|
74
|
+
@p2.destroy
|
75
|
+
}.to raise_error(Mongoid::Errors::StaleDocument)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should succeed when destroying the second without locking' do
|
79
|
+
@p2.unlocked.destroy
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should succeed when destroying the second without locking, ' +
|
83
|
+
'then fail when destroying the third' do
|
84
|
+
@p2.unlocked.destroy
|
85
|
+
expect {
|
86
|
+
@p3.destroy
|
87
|
+
}.to raise_error(Mongoid::Errors::StaleDocument)
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should give a deprecation warning for #save_optimistic!' do
|
93
|
+
::ActiveSupport::Deprecation.should_receive(:warn).once
|
94
|
+
@p1.save_optimistic!
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should give a deprecation warning for including Mongoid::Lockable' do
|
98
|
+
::ActiveSupport::Deprecation.should_receive(:warn).once
|
99
|
+
Class.new do
|
100
|
+
include Mongoid::Document
|
101
|
+
include Mongoid::Lockable
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
Bundler.setup
|
4
|
+
|
5
|
+
require 'mongoid'
|
6
|
+
|
7
|
+
Mongoid.configure do |config|
|
8
|
+
config.master = Mongo::Connection.new.db("mongoid_optimistic_locking_test")
|
9
|
+
end
|
10
|
+
|
11
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
12
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
13
|
+
|
14
|
+
|
15
|
+
require 'mongoid_optimistic_locking'
|
16
|
+
require 'rspec'
|
metadata
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mongoid_optimistic_locking
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Alon Burg
|
9
|
+
- John Nishinaga
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2012-05-31 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: mongoid
|
17
|
+
requirement: &2161833600 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '2.4'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *2161833600
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rake
|
28
|
+
requirement: &2161832880 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.9.0
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *2161832880
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: rspec
|
39
|
+
requirement: &2161832180 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ~>
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '2.6'
|
45
|
+
type: :development
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *2161832180
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: bson_ext
|
50
|
+
requirement: &2161831560 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ~>
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '1.5'
|
56
|
+
type: :development
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: *2161831560
|
59
|
+
description: Allows optimisitic locking for Mongoid models. See https://github.com/burgalon/mongoid_optimistic_locking
|
60
|
+
email:
|
61
|
+
- burgalon@gmail.com
|
62
|
+
- jingoro@casa-z.org
|
63
|
+
executables: []
|
64
|
+
extensions: []
|
65
|
+
extra_rdoc_files: []
|
66
|
+
files:
|
67
|
+
- .gitignore
|
68
|
+
- .rspec
|
69
|
+
- .travis.yml
|
70
|
+
- Gemfile
|
71
|
+
- LICENSE
|
72
|
+
- README.md
|
73
|
+
- Rakefile
|
74
|
+
- lib/config/locales/en.yml
|
75
|
+
- lib/mongoid/errors/stale_document.rb
|
76
|
+
- lib/mongoid/lockable.rb
|
77
|
+
- lib/mongoid/optimistic_locking.rb
|
78
|
+
- lib/mongoid/optimistic_locking/deprecated.rb
|
79
|
+
- lib/mongoid/optimistic_locking/lock_version.rb
|
80
|
+
- lib/mongoid/optimistic_locking/operations.rb
|
81
|
+
- lib/mongoid/optimistic_locking/threaded_with_unlocked.rb
|
82
|
+
- lib/mongoid/optimistic_locking/unlocked.rb
|
83
|
+
- lib/mongoid/optimistic_locking/version.rb
|
84
|
+
- lib/mongoid_optimistic_locking.rb
|
85
|
+
- mongoid_optimistic_locking.gemspec
|
86
|
+
- spec/lockable_spec.rb
|
87
|
+
- spec/optimistic_locking_spec.rb
|
88
|
+
- spec/spec_helper.rb
|
89
|
+
homepage: https://github.com/burgalon/mongoid_optimistic_locking
|
90
|
+
licenses: []
|
91
|
+
post_install_message:
|
92
|
+
rdoc_options: []
|
93
|
+
require_paths:
|
94
|
+
- lib
|
95
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ! '>='
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
none: false
|
103
|
+
requirements:
|
104
|
+
- - ! '>='
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
requirements: []
|
108
|
+
rubyforge_project: mongoid_optimistic_locking
|
109
|
+
rubygems_version: 1.8.10
|
110
|
+
signing_key:
|
111
|
+
specification_version: 3
|
112
|
+
summary: Allows optimisitic locking for Mongoid models
|
113
|
+
test_files:
|
114
|
+
- spec/lockable_spec.rb
|
115
|
+
- spec/optimistic_locking_spec.rb
|
116
|
+
- spec/spec_helper.rb
|