Browse files

Added has_one and has_many reify by using a version_assocations table

  • Loading branch information...
1 parent 3688c3b commit 122f7bb83b915bb278dc38d50ac40b58f79bcdab @tderks committed Mar 31, 2011
View
1 lib/generators/paper_trail/install_generator.rb
@@ -13,6 +13,7 @@ class InstallGenerator < Rails::Generators::Base
def create_migration_file
migration_template 'create_versions.rb', 'db/migrate/create_versions.rb'
+ migration_template 'create_version_associations.rb', 'db/migrate/create_version_associations.rb'
end
end
end
View
15 lib/generators/paper_trail/templates/create_version_associations.rb
@@ -0,0 +1,15 @@
+class CreateVersionAssociations < ActiveRecord::Migration
+ def self.up
+ create_table :version_associations do |t|
+ t.integer :version_id
+ t.string :foreign_key_name, :null => false
+ t.integer :foreign_key_id
+ end
+ add_index :version_associations, [:version_id, :foreign_key_name, :foreign_key_id]
+ end
+
+ def self.down
+ remove_index :version_associations, [:version_id, :foreign_key_name, :foreign_key_id]
+ drop_table :version_associations
+ end
+end
View
11 lib/paper_trail/has_paper_trail.rb
@@ -82,6 +82,7 @@ def originator
def version_at(timestamp, reify_options={})
# Because a version stores how its object looked *before* the change,
# we need to look for the first version created *after* the timestamp.
+ reify_options.merge(:version_at => timestamp)
version = versions.after(timestamp).first
version ? version.reify(reify_options) : self
end
@@ -108,6 +109,7 @@ def record_create
:whodunnit => PaperTrail.whodunnit,
:transaction_id => PaperTrail.transaction_id)
set_transaction_id(version)
+ save_associations(version)
end
end
@@ -118,6 +120,7 @@ def record_update
:whodunnit => PaperTrail.whodunnit,
:transaction_id => PaperTrail.transaction_id)
set_transaction_id(version)
+ save_associations(version)
end
end
@@ -129,11 +132,19 @@ def record_destroy
:object => object_to_string(item_before_change),
:whodunnit => PaperTrail.whodunnit,
:transaction_id => PaperTrail.transaction_id)
+
set_transaction_id(version)
+ save_associations(version)
end
versions.send :load_target
end
+ def save_associations(version)
+ self.class.name.constantize.reflect_on_all_associations(:belongs_to).each do |assoc|
+ VersionAssociation.create(:version_id => version.id, :foreign_key_name => assoc.primary_key_name, :foreign_key_id => self.send(assoc.primary_key_name))
+ end
+ end
+
def set_transaction_id(version)
if(PaperTrail.transaction?&&PaperTrail.transaction_id.nil?)
PaperTrail.transaction_id=version.id
View
50 lib/paper_trail/version.rb
@@ -1,5 +1,7 @@
class Version < ActiveRecord::Base
belongs_to :item, :polymorphic => true
+ has_many :version_associations, :dependent => :destroy
+
validates_presence_of :event
scope :with_item_keys, lambda { |item_type, item_id|
@@ -34,6 +36,7 @@ class Version < ActiveRecord::Base
# set to a float to change the lookback time (check whether your db supports
# sub-second datetimes if you want them).
def reify(options = {})
+ options.reverse_merge!(:version_at => created_at)
unless object.nil?
attrs = YAML::load object
@@ -68,6 +71,15 @@ def reify(options = {})
end
model.version = self
+
+ unless options[:has_one] == false
+ reify_has_ones(model,options)
+ end
+
+ unless options[:has_many] == false
+ reify_has_manys(model,options)
+ end
+
model
end
end
@@ -77,7 +89,7 @@ def transact
end
def rollback
- #rollback all changes with in transaction
+ #rollback all changes within transaction
transaction do
transact.reverse_each do |version|
version.reify.save!
@@ -110,4 +122,40 @@ def previous
def index
sibling_versions.select(:id).order("id ASC").map(&:id).index(self.id)
end
+
+ private
+ # Restore the `model`'s has_one associations as they were at version_at timestamp
+ # We lookup the first child version after version_at timestamp or in same transaction.
+ def reify_has_ones(model,options = {})
+ model.class.reflect_on_all_associations(:has_one).each do |assoc|
+ version_association=VersionAssociation.includes(:version).
+ where(["item_type = ?",assoc.class_name]).
+ where(["foreign_key_name = ?",assoc.primary_key_name]).
+ where(["foreign_key_id = ?", model.id]).
+ where(['created_at >= ? OR transaction_id = ?', options[:version_at], transaction_id]).
+ order('created_at ASC, versions.id ASC').
+ limit(1).first
+ child=version_association.version.reify(options)
+ model.send(assoc.name.to_s+"=",child)
+ end
+ end
+
+ # Restore the `model`'s has_many associations as they were at version_at timestamp
+ # We lookup the first child versions after version_at timestamp or in same transaction.
+ def reify_has_manys(model,options = {})
+ model.class.reflect_on_all_associations(:has_many).each do |assoc|
+ next if(assoc.name==:versions)
+ version_associations=VersionAssociation.includes(:version).
+ where(["item_type = ?",assoc.class_name]).
+ where(["foreign_key_name = ?",assoc.primary_key_name]).
+ where(["foreign_key_id = ?", model.id]).
+ where(['created_at >= ? OR transaction_id = ?', options[:version_at], transaction_id]).
+ group("versions.item_id").order('created_at ASC, versions.id ASC')
+
+ version_associations.each do |version_association|
+ child=version_association.version.reify(options)
+ model.send(assoc.name) << child
+ end
+ end
+ end
end
View
3 lib/paper_trail/version_association.rb
@@ -0,0 +1,3 @@
+class VersionAssociation < ActiveRecord::Base
+ belongs_to :version
+end

0 comments on commit 122f7bb

Please sign in to comment.