Class: ActsAsRevisionable::RevisionRecord

Inherits:
ActiveRecord::Base
  • Object
show all
Defined in:
lib/acts_as_revisionable/revision_record.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(record, encoding = :ruby) ⇒ RevisionRecord

Create a revision record based on a record passed in. The attributes of the original record will be serialized. If it uses the acts_as_revisionable behavior, associations will be revisioned as well.



77
78
79
80
81
82
83
84
# File 'lib/acts_as_revisionable/revision_record.rb', line 77

def initialize(record, encoding = :ruby)
  super({})
  @data_encoding = encoding
  self.revisionable_type = record.class.base_class.name
  self.revisionable_id = record.id
  associations = record.class.revisionable_associations if record.class.respond_to?(:revisionable_associations)
  self.data = Zlib::Deflate.deflate(serialize_hash(serialize_attributes(record, associations)))
end

Instance Attribute Details

#data_encodingObject (readonly)

Returns the value of attribute data_encoding.



8
9
10
# File 'lib/acts_as_revisionable/revision_record.rb', line 8

def data_encoding
  @data_encoding
end

Class Method Details

.create_tableObject

Create the table to store revision records.



48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/acts_as_revisionable/revision_record.rb', line 48

def create_table
  connection.create_table table_name do |t|
    t.string :revisionable_type, :null => false, :limit => 100
    t.integer :revisionable_id, :null => false
    t.integer :revision, :null => false
    t.binary :data, :limit => (connection.adapter_name.match(/mysql/i) ? 5.megabytes : nil)
    t.timestamp :created_at, :null => false
    t.boolean :trash, :default => false
  end
  
  connection.add_index table_name, :revisionable_id, :name => "#{table_name}_id"
  connection.add_index table_name, [:revisionable_type, :created_at, :trash], :name => "#{table_name}_type_and_created_at"
end

.empty_trash(revisionable_type, max_age) ⇒ Object

Empty the trash by deleting records older than the specified maximum age in seconds. The revisionable_type argument specifies the class to delete revision records for.



41
42
43
44
45
# File 'lib/acts_as_revisionable/revision_record.rb', line 41

def empty_trash(revisionable_type, max_age)
  sql = "revisionable_id IN (SELECT revisionable_id from #{table_name} WHERE created_at <= ? AND revisionable_type = ? AND trash = ?) AND revisionable_type = ?"
  args = [max_age.ago, revisionable_type.name, true, revisionable_type.name]
  delete_all([sql] + args)
end

.find_revision(klass, id, revision) ⇒ Object

Find a specific revision record.



14
15
16
# File 'lib/acts_as_revisionable/revision_record.rb', line 14

def find_revision(klass, id, revision)
  find(:first, :conditions => {:revisionable_type => klass.base_class.to_s, :revisionable_id => id, :revision => revision})
end

.last_revision(klass, id, revision = nil) ⇒ Object

Find the last revision record for a class.



19
20
21
# File 'lib/acts_as_revisionable/revision_record.rb', line 19

def last_revision(klass, id, revision = nil)
  find(:first, :conditions => {:revisionable_type => klass.base_class.to_s, :revisionable_id => id}, :order => "revision DESC")
end

.truncate_revisions(revisionable_type, revisionable_id, options) ⇒ Object

Truncate the revisions for a record. Available options are :limit and :max_age.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/acts_as_revisionable/revision_record.rb', line 24

def truncate_revisions(revisionable_type, revisionable_id, options)
  return unless options[:limit] || options[:minimum_age]

  conditions = ['revisionable_type = ? AND revisionable_id = ?', revisionable_type.base_class.to_s, revisionable_id]
  if options[:minimum_age]
    conditions.first << ' AND created_at <= ?'
    conditions << options[:minimum_age].ago
  end

  start_deleting_revision = find(:first, :conditions => conditions, :order => 'revision DESC', :offset => options[:limit])
  if start_deleting_revision
    delete_all(['revisionable_type = ? AND revisionable_id = ? AND revision <= ?', revisionable_type.base_class.to_s, revisionable_id, start_deleting_revision.revision])
  end
end

.update_version_1_tableObject

Update a version 1.0.x table to the latest version. This method only needs to be called from a migration if you originally created the table with a version 1.0.x version of the gem.



64
65
66
67
68
69
70
71
72
# File 'lib/acts_as_revisionable/revision_record.rb', line 64

def update_version_1_table
  # Added in version 1.1.0
  connection.add_column(:revision_records, :trash, :boolean, :default => false)
  connection.add_index :revision_records, :revisionable_id, :name => "#{table_name}_id"
  connection.add_index :revision_records, [:revisionable_type, :created_at, :trash], :name => "#{table_name}_type_and_created_at"

  # Removed in 1.1.0
  connection.remove_index(:revision_records, :name => "revisionable")
end

Instance Method Details

#restoreObject

Restore the revision to the original record. If any errors are encountered restoring attributes, they will be added to the errors object of the restored record.



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/acts_as_revisionable/revision_record.rb', line 95

def restore
  restore_class = self.revisionable_type.constantize

  # Check if we have a type field, if yes, assume single table inheritance and restore the actual class instead of the stored base class
  sti_type = self.revision_attributes[restore_class.inheritance_column]
  if sti_type
    begin
      if !restore_class.store_full_sti_class && !sti_type.start_with?("::")
        sti_type = "#{restore_class.parent.name}::#{sti_type}"
      end
      restore_class = sti_type.constantize
    rescue NameError => e
      raise e
      # Seems our assumption was wrong and we have no STI
    end
  end

  record = restore_class.new
  restore_record(record, revision_attributes)
  return record
end

#revision_attributesObject

Returns the attributes that are saved in the revision.



87
88
89
90
91
# File 'lib/acts_as_revisionable/revision_record.rb', line 87

def revision_attributes
  return nil unless self.data
  uncompressed = Zlib::Inflate.inflate(self.data)
  deserialize_hash(uncompressed)
end

#trash!Object

Mark this revision as being trash. When trash records are restored, all their revision history is restored as well.



119
120
121
# File 'lib/acts_as_revisionable/revision_record.rb', line 119

def trash!
  update_attribute(:trash, true)
end