Hi. I've updated the code mentioned below, if anyone is interested or
want to comment. Tables with version-control now have the extra
columns version (primary key) and version_deleted (boolean indicating
if the object is deleted in this version). I needed some complicated
SQL to get it working with find(:all) and find(:first) :). More
details in the old mail.
Have a nice day!
Tobias
-- code --
# acts_as_multi_versioned
class ActiveRecord::Base
class << self
attr_accessor :current_version
end
self.current_version = 0
def self.acts_as_multi_versioned
class_eval %q{
class << self
def find_version(obj, version, args = {})
# Adding a join-statement which selects the rows with
highest
# version =< current_version for every unique value of id.
args[:joins] = "INNER JOIN (SELECT
#{primary_key},MAX(version) as version FROM #{table_name} WHERE
version < #{version+1} GROUP BY #{primary_key}) #{table_name}_versions
ON #{table_name}_versions.#{primary_key} =
#{table_name}.#{primary_key} AND #{table_name}_versions.version =
#{table_name}.version AND #{table_name}.version_deleted = 0
#{args[:joins]}"
args[:readonly] ||= false
find_without_version(obj, args)
end
def find_with_version(obj, args = {})
find_version(obj, ActiveRecord::Base.current_version, args)
end
alias_method_chain :find, :version
end
def before_create
self[:version] = ActiveRecord::Base.current_version
end
def update_without_callbacks
if self.version != ActiveRecord::Base.current_version
self[:version] = ActiveRecord::Base.current_version
# Must create a new object when storing a new version-
number. But the
# old record will work correctly for the next update.
o = self.clone
o[self.class.primary_key] = self[self.class.primary_key] #
why is this needed?
o.save!
else
# Modified update_without_callbacks from
composite_primary_keys.
where_class = "(#{self.class.primary_key} = #{quoted_id})
AND (version = #{self[:version]})"
connection.update(
"UPDATE #{self.class.table_name} " +
"SET #{quoted_comma_pair_list(connection,
attributes_with_quotes_versioned)} " +
"WHERE #{where_class}",
"#{self.class.name} Update"
)
return true
end
end
def destroy_without_callbacks
begin
self.class.find_version(self.id, self.version - 1) # older
version exists?
self.version_deleted = true
self.save! # (this will clone and create a deleted record if
only readaccess was made earlier)
rescue ActiveRecord::RecordNotFound # older record doesn't
exist, remove it from database.
where_class = "(#{self.class.primary_key} = #{quoted_id})
AND (version = #{self[:version]})"
unless new_record?
connection.delete(
"DELETE FROM #{self.class.table_name} " +
"WHERE #{where_class}",
"#{self.class.name} Destroy"
)
end
end
freeze
end
def attributes_with_quotes_versioned # All attributes except
primary-key and 'version'.
x = attributes_with_quotes(false)
x.delete "version"
x
end
}
end
end
ActiveRecord::Base.current_version = 3