moser-dm-optlock 0.1.5

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