moser-dm-optlock 0.1.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.
- data/History.txt +12 -0
- data/README.rdoc +19 -0
- data/Rakefile +21 -0
- data/lib/dm-optlock.rb +57 -0
- data/spec/dm-optlock_spec.rb +71 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +34 -0
- data/tasks/rspec.rake +21 -0
- metadata +70 -0
data/History.txt
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
== 0.1.3 2009-01-10
|
2
|
+
* 1 major enhancement:
|
3
|
+
* replaced 'set_locking_column' by 'add_locking_column'
|
4
|
+
|
5
|
+
== 0.1.2 2008-10-01
|
6
|
+
* 1 major enhancement:
|
7
|
+
* Added class method 'set_locking_column'
|
8
|
+
|
9
|
+
== 0.1.0 2008-09-30
|
10
|
+
* 1 major enhancement:
|
11
|
+
* Initial release
|
12
|
+
|
data/README.rdoc
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
= dm-optlock
|
2
|
+
Adds optimistic locking to Datamapper.
|
3
|
+
|
4
|
+
== Usage:
|
5
|
+
class Model
|
6
|
+
include DataMapper::Resource
|
7
|
+
...
|
8
|
+
add_locking_column :my_version_col
|
9
|
+
end
|
10
|
+
|
11
|
+
add_locking_column takes a options hash just like DataMapper’s property method does.
|
12
|
+
|
13
|
+
== REQUIREMENTS:
|
14
|
+
|
15
|
+
* dm-core >= 0.9.5
|
16
|
+
|
17
|
+
== LICENSE:
|
18
|
+
GPL v3
|
19
|
+
http://www.gnu.de/documents/gpl-3.0.en.html
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
%w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
|
2
|
+
require File.dirname(__FILE__) + '/lib/dm-optlock'
|
3
|
+
|
4
|
+
# Generate all the Rake tasks
|
5
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
6
|
+
$hoe = Hoe.new('dm-optlock', DataMapper::DmOptlock::VERSION) do |p|
|
7
|
+
p.developer('Martin Vielsmaier', 'martin.vielsmaier@gmail.com')
|
8
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
9
|
+
p.rubyforge_name = p.name # TODO this is default value
|
10
|
+
p.extra_deps = [
|
11
|
+
['dm-core','>= 0.9.5'],
|
12
|
+
]
|
13
|
+
|
14
|
+
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
15
|
+
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
16
|
+
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
17
|
+
p.rsync_args = '-av --delete --ignore-errors'
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'newgem/tasks' # load /tasks/*.rake
|
21
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
data/lib/dm-optlock.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
gem 'dm-core', '>=0.9.5'
|
6
|
+
require 'dm-core'
|
7
|
+
|
8
|
+
module DataMapper
|
9
|
+
class StaleObjectError < StandardError
|
10
|
+
end
|
11
|
+
|
12
|
+
module DmOptlock
|
13
|
+
VERSION = '0.1.4'
|
14
|
+
DEFAULT_LOCKING_COLUMN = :lock_version
|
15
|
+
|
16
|
+
def self.included(base) #:nodoc:
|
17
|
+
base.extend ClassMethods
|
18
|
+
base.before :save, :check_lock_version
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
# Checks if the row has been changed since being loaded from the database.
|
23
|
+
def check_lock_version
|
24
|
+
if !new_record? && dirty? && respond_to?(self.class.locking_column.to_s)
|
25
|
+
if original_values.include?(:id)
|
26
|
+
row = self.class.get(original_values[:id])
|
27
|
+
else
|
28
|
+
row = self.class.get(id)
|
29
|
+
end
|
30
|
+
if !row.nil? && row.attribute_get(self.class.locking_column) != attribute_get(self.class.locking_column)
|
31
|
+
attributes = original_values
|
32
|
+
raise DataMapper::StaleObjectError
|
33
|
+
else
|
34
|
+
attribute_set(self.class.locking_column, attribute_get(self.class.locking_column) + 1)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module ClassMethods
|
40
|
+
@@lock_column = nil
|
41
|
+
|
42
|
+
# Set the column to use for optimistic locking. Defaults to lock_version.
|
43
|
+
def add_locking_column(name = DEFAULT_LOCKING_COLUMN, options = {})
|
44
|
+
options.merge!({:default => 0, :writer => :protected})
|
45
|
+
@@lock_column = name
|
46
|
+
property name, Integer, options
|
47
|
+
end
|
48
|
+
|
49
|
+
# The version column used for optimistic locking. Defaults to lock_version.
|
50
|
+
def locking_column
|
51
|
+
return @@lock_column
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
Resource::append_inclusions DmOptlock
|
57
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
#:nodoc:
|
2
|
+
require 'pathname'
|
3
|
+
require Pathname(__FILE__).dirname.expand_path + 'spec_helper'
|
4
|
+
|
5
|
+
if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
|
6
|
+
describe 'DataMapper::DmOptlock' do
|
7
|
+
before :all do
|
8
|
+
class Thing
|
9
|
+
include DataMapper::Resource
|
10
|
+
property :id, Integer, :serial => true
|
11
|
+
property :name, String
|
12
|
+
|
13
|
+
add_locking_column :row_version
|
14
|
+
|
15
|
+
auto_migrate!(:default)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
after do
|
20
|
+
repository(:default).adapter.execute('DELETE from things');
|
21
|
+
end
|
22
|
+
|
23
|
+
it "shouldn't save second" do
|
24
|
+
t = Thing.new(:name => "Banana")
|
25
|
+
t.save
|
26
|
+
tA = Thing.first
|
27
|
+
tB = Thing.first
|
28
|
+
tA.name = 'Apple'
|
29
|
+
tB.name = 'Pineapple'
|
30
|
+
tA.save.should be_true
|
31
|
+
lambda { tB.save.should }.should raise_error(DataMapper::StaleObjectError)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should increment lock version" do
|
35
|
+
t = Thing.new(:name => "Dollar")
|
36
|
+
t.save
|
37
|
+
t.row_version.should equal(0)
|
38
|
+
t.name = "Euro"
|
39
|
+
t.save
|
40
|
+
t.row_version.should equal(1)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should be aware of ID changes" do
|
44
|
+
t = Thing.new(:name => "abc")
|
45
|
+
t.save
|
46
|
+
tA = Thing.first
|
47
|
+
tB = Thing.first
|
48
|
+
tA.name = 'Apple'
|
49
|
+
tB.id = 122
|
50
|
+
tA.save.should be_true
|
51
|
+
lambda { tB.save.should }.should raise_error(DataMapper::StaleObjectError)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "'s version column should be not nullable, have default 0 and a protected writer" do
|
55
|
+
class Foo
|
56
|
+
include DataMapper::Resource
|
57
|
+
property :id, Integer, :serial => true
|
58
|
+
|
59
|
+
add_locking_column :foo_version, :default => 1, :nullable => false
|
60
|
+
|
61
|
+
auto_migrate!(:default)
|
62
|
+
end
|
63
|
+
|
64
|
+
col = Foo.properties[:foo_version]
|
65
|
+
col.nullable?.should be_false
|
66
|
+
col.default.should be(0)
|
67
|
+
col.writer_visibility.should equal(:protected)
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#:nodoc:
|
2
|
+
begin
|
3
|
+
require 'spec'
|
4
|
+
rescue LoadError
|
5
|
+
require 'rubygems'
|
6
|
+
gem 'rspec'
|
7
|
+
require 'spec'
|
8
|
+
end
|
9
|
+
|
10
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
11
|
+
require 'dm-optlock'
|
12
|
+
|
13
|
+
def load_driver(name, default_uri)
|
14
|
+
return false if ENV['ADAPTER'] != name.to_s
|
15
|
+
|
16
|
+
lib = "do_#{name}"
|
17
|
+
|
18
|
+
begin
|
19
|
+
gem lib, '>=0.9.5'
|
20
|
+
require lib
|
21
|
+
DataMapper.setup(name, ENV["#{name.to_s.upcase}_SPEC_URI"] || default_uri)
|
22
|
+
DataMapper::Repository.adapters[:default] = DataMapper::Repository.adapters[name]
|
23
|
+
true
|
24
|
+
rescue Gem::LoadError => e
|
25
|
+
warn "Could not load #{lib}: #{e}"
|
26
|
+
false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
ENV['ADAPTER'] ||= 'sqlite3'
|
31
|
+
|
32
|
+
HAS_SQLITE3 = load_driver(:sqlite3, 'sqlite3::memory:')
|
33
|
+
HAS_MYSQL = load_driver(:mysql, 'mysql://localhost/dm_core_test')
|
34
|
+
HAS_POSTGRES = load_driver(:postgres, 'postgres://postgres@localhost/dm_core_test')
|
data/tasks/rspec.rake
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
begin
|
2
|
+
require 'spec'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems'
|
5
|
+
require 'spec'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'spec/rake/spectask'
|
9
|
+
rescue LoadError
|
10
|
+
puts <<-EOS
|
11
|
+
To use rspec for testing you must install rspec gem:
|
12
|
+
gem install rspec
|
13
|
+
EOS
|
14
|
+
exit(0)
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Run the specs under spec/models"
|
18
|
+
Spec::Rake::SpecTask.new do |t|
|
19
|
+
t.spec_opts = ['--options', "spec/spec.opts"]
|
20
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: moser-dm-optlock
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.5
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Martin Vielsmaier
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-03-03 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: dm-core
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.9.5
|
24
|
+
version:
|
25
|
+
description: Adds optimistic locking to Datamapper.
|
26
|
+
email: tom@mojombo.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files: []
|
32
|
+
|
33
|
+
files:
|
34
|
+
- History.txt
|
35
|
+
- README.rdoc
|
36
|
+
- Rakefile
|
37
|
+
- lib/dm-optlock.rb
|
38
|
+
- spec/dm-optlock_spec.rb
|
39
|
+
- spec/spec.opts
|
40
|
+
- spec/spec_helper.rb
|
41
|
+
- tasks/rspec.rake
|
42
|
+
has_rdoc: true
|
43
|
+
homepage: http://moserei.de/index.php/tag/dm-optlock
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options:
|
46
|
+
- --inline-source
|
47
|
+
- --charset=UTF-8
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
version:
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: "0"
|
61
|
+
version:
|
62
|
+
requirements: []
|
63
|
+
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 1.2.0
|
66
|
+
signing_key:
|
67
|
+
specification_version: 2
|
68
|
+
summary: Adds optimistic locking to Datamapper.
|
69
|
+
test_files: []
|
70
|
+
|