Acts_as_versioned with lock_version bug?


#1

Hello.

The acts_as_versioned plugin works fine … till the adjunction
of optimistic locking via the lock_version table column.

Here is the problem description:

Without optimistic locking things are fine:

# ++++ The table creation migration ++++
% cat db/migrate/001_create_tables.rb
class CreateTables < ActiveRecord::Migration
  def self.up
    create_table :softwares, :force => true do |t|
      t.column :name, :string, :limit => 20, :null => false
      t.column :version, :integer, :default => 0, :null => false
    end
    Software.create_versioned_table
  end

  def self.down
    drop_table :softwares
    Software.drop_versioned_table
  end
end

% rake migrate
(in /home/krassi/MyProjects/Rails/pl2)
== CreateTables: migrating 

====================================================
– create_table(:softwares, {:force=>true})
-> 0.0218s
== CreateTables: migrated (0.0589s)

# ++++ Using the versioned data ++++
%  ./script/console
Loading development environment.
>> s = Software.create :name => "Initial name"
=> #<Software:0xb7b3c290 @new_record_before_save=true, 

@errors=#<ActiveRecord::Errors:0xb7ae3ff0 @errors={},
@base=#<Software:0xb7b3c290 …>>, @attributes={“name”=>“Initial name”,
“id”=>1, “version”=>1}, @new_record=false, @changed_attributes=[]>
>> s.version
=> 1
>> s.save
=> true
>> s.version
=> 2
>> ############ WHY? THERE SHOULD BE ONLY ONE VERSION! ############
?>
?> >> s.reload
=> #<Software:0xb7b3c290 @new_record_before_save=false,
@errors=#<ActiveRecord::Errors:0xb7ae3ff0 @errors={},
@base=#<Software:0xb7b3c290 …>>, @versions=nil,
@attributes={“name”=>“Initial name”, “id”=>“1”, “version”=>“2”},
@new_record=false, @changed_attributes=[]>
>> s.version
=> 2
>> s.name = “Changed name”
=> “Changed name”
>> s.version
=> 2
>> s.save
=> true
>> s.version
=> 3
>> s.versions
=> [#<Software::Version:0xb7a8d290 @attributes={“name”=>“Initial
name”, “updated_at”=>“2006-05-16 21:25:28”, “software_id”=>“1”,
“id”=>“1”, “version”=>“1”}>, #<Software::Version:0xb7a8d240
@attributes={“name”=>“Initial name”, “updated_at”=>“2006-05-16
21:25:33”, “software_id”=>“1”, “id”=>“2”, “version”=>“2”}>,
#<Software::Version:0xb7a8d13c @attributes={“name”=>“Changed name”,
“updated_at”=>“2006-05-16 21:26:10”, “software_id”=>“1”, “id”=>“3”,
“version”=>“3”}>]
>> s.name = “Name changed again”
=> “Name changed again”
>> s.save
=> true
>> s.version
=> 4
>> s.versions
=> [#<Software::Version:0xb7a8d290 @attributes={“name”=>“Initial
name”, “updated_at”=>“2006-05-16 21:25:28”, “software_id”=>“1”,
“id”=>“1”, “version”=>“1”}>, #<Software::Version:0xb7a8d240
@attributes={“name”=>“Initial name”, “updated_at”=>“2006-05-16
21:25:33”, “software_id”=>“1”, “id”=>“2”, “version”=>“2”}>,
#<Software::Version:0xb7a8d13c @attributes={“name”=>“Changed name”,
“updated_at”=>“2006-05-16 21:26:10”, “software_id”=>“1”, “id”=>“3”,
“version”=>“3”}>]
>>

A question: is it normal for the first save without optimistic
locking to generate two object versions?

% ++++ And the database content ++++
mysql> select * from softwares; select * from software_versions;
+----+--------------------+---------+
| id | name               | version |
+----+--------------------+---------+
|  1 | Name changed again |       4 |
+----+--------------------+---------+
1 row in set (0.00 sec)

+----+-------------+---------+--------------------+---------------------+
| id | software_id | version | name               | updated_at 

|
±—±------------±--------±-------------------±--------------------+
| 1 | 1 | 1 | Initial name | 2006-05-16
21:25:28 |
| 2 | 1 | 2 | Initial name | 2006-05-16
21:25:33 |
| 3 | 1 | 3 | Changed name | 2006-05-16
21:26:10 |
| 4 | 1 | 4 | Name changed again | 2006-05-16
21:26:22 |
±—±------------±--------±-------------------±--------------------+
4 rows in set (0.00 sec)

And now the same thing with optimistic locking:

# ++++ The table creation migration ++++
% rake migrate VERSION=0
(in /home/krassi/MyProjects/Rails/pl2)
== CreateTables: reverting 

====================================================
– drop_table(:softwares)
-> 0.0230s
== CreateTables: reverted (0.0308s)

%  cat  db/migrate/001_create_tables.rb
class CreateTables < ActiveRecord::Migration
  def self.up
    create_table :softwares, :force => true do |t|
      t.column :name, :string, :limit => 20, :null => false
      t.column :version, :integer, :default => 0, :null => false
      t.column :lock_version, :integer, :default => 0, :null => 

false
end
Software.create_versioned_table
end

  def self.down
    drop_table :softwares
    Software.drop_versioned_table
  end
end

% rake migrate
(in /home/krassi/MyProjects/Rails/pl2)
== CreateTables: migrating 

====================================================
– create_table(:softwares, {:force=>true})
-> 0.0225s
== CreateTables: migrated (0.0681s)

# ++++ Using the versioned data ++++
% ./script/console
Loading development environment.
>> s = Software.create :name => "Initial name"
=> #<Software:0xb7a94290 @errors=#<ActiveRecord::Errors:0xb7a3b7e4 

@errors={}, @base=#<Software:0xb7a94290 …>>,
@new_record_before_save=true, @attributes={“name”=>“Initial name”,
“lock_version”=>0, “id”=>1, “version”=>1}, @new_record=false,
@changed_attributes=[]>
>> s.version
=> 1
>> s.save
=> true
>> s.version
=> 1
>> s.reload
=> #<Software:0xb7a94290 @errors=#<ActiveRecord::Errors:0xb7a3b7e4
@errors={}, @base=#<Software:0xb7a94290 …>>,
@new_record_before_save=false, @attributes={“name”=>“Initial name”,
“lock_version”=>“1”, “id”=>“1”, “version”=>“1”}, @versions=nil,
@new_record=false, @changed_attributes=[]>
>> s.version
=> 1
>> s.name = “Changed name”
=> “Changed name”
>> s.version
=> 1
>> s.save
=> true
>> s.version
=> 1
>> s.versions
=> [#<Software::Version:0xb79e5790 @attributes={“name”=>“Initial
name”, “updated_at”=>“2006-05-16 21:28:45”, “software_id”=>“1”,
“id”=>“1”, “version”=>“1”}>, #<Software::Version:0xb79e5678
@attributes={“name”=>“Initial name”, “updated_at”=>“2006-05-16
21:28:50”, “software_id”=>“1”, “id”=>“2”, “version”=>“1”}>,
#<Software::Version:0xb79e563c @attributes={“name”=>“Changed name”,
“updated_at”=>“2006-05-16 21:29:15”, “software_id”=>“1”, “id”=>“3”,
“version”=>“1”}>]
>> s.name = “Name changed again”
=> “Name changed again”
>> s.save
=> true
>> s.version
=> 1
>> s.versions
=> [#<Software::Version:0xb79e5790 @attributes={“name”=>“Initial
name”, “updated_at”=>“2006-05-16 21:28:45”, “software_id”=>“1”,
“id”=>“1”, “version”=>“1”}>, #<Software::Version:0xb79e5678
@attributes={“name”=>“Initial name”, “updated_at”=>“2006-05-16
21:28:50”, “software_id”=>“1”, “id”=>“2”, “version”=>“1”}>,
#<Software::Version:0xb79e563c @attributes={“name”=>“Changed name”,
“updated_at”=>“2006-05-16 21:29:15”, “software_id”=>“1”, “id”=>“3”,
“version”=>“1”}>]
>>

% ++++ And the BAD database content ++++
mysql> select * from softwares; select * from software_versions;
+----+--------------------+---------+--------------+
| id | name               | version | lock_version |
+----+--------------------+---------+--------------+
|  1 | Name changed again |       1 |            3 |
+----+--------------------+---------+--------------+
1 row in set (0.00 sec)

+----+-------------+---------+--------------------+---------------------+
| id | software_id | version | name               | updated_at 

|
±—±------------±--------±-------------------±--------------------+
| 1 | 1 | 1 | Initial name | 2006-05-16
21:28:45 |
| 2 | 1 | 1 | Initial name | 2006-05-16
21:28:50 |
| 3 | 1 | 1 | Changed name | 2006-05-16
21:29:15 |
| 4 | 1 | 1 | Name changed again | 2006-05-16
21:29:35 |
±—±------------±--------±-------------------±--------------------+
4 rows in set (0.00 sec)

As I see, there is a mismatch between the version and the
lock_version columns usage. Or am I using the plugin incorrectly?

Thank you very much for any suggestion.