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 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
+