Permalink
Browse files

bump to version 1.2.0 with support for customizing and subclassing Re…

…visionRecord.
  • Loading branch information...
1 parent d8fd52c commit 123c659461f6c01c7d259509d253a3e402a81bc4 Brian Durand committed Aug 6, 2011
Showing with 174 additions and 87 deletions.
  1. +5 −0 .travis.yml
  2. +9 −0 Gemfile
  3. +30 −0 Gemfile.lock
  4. +2 −0 README.rdoc
  5. +4 −0 RELEASES.txt
  6. +0 −9 Rakefile
  7. +1 −1 VERSION
  8. +49 −15 lib/acts_as_revisionable.rb
  9. +5 −7 lib/acts_as_revisionable/revision_record.rb
  10. +54 −41 spec/acts_as_revisionable_spec.rb
  11. +14 −14 spec/revision_record_spec.rb
  12. +1 −0 spec/spec_helper.rb
View
@@ -0,0 +1,5 @@
+rvm:
+ - 1.8.7
+ - 1.9.2
+ - rbx
+ - jruby
View
@@ -0,0 +1,9 @@
+source 'http://rubygems.org'
+
+gem 'rake'
+gem 'rspec', '>=2.6.0'
+
+gem 'activerecord', '~>2.3.9'
+gem 'activesupport', '~>2.3.9'
+gem 'composite_primary_keys'
+gem 'sqlite3', '~>1.3.4'
View
@@ -0,0 +1,30 @@
+GEM
+ remote: http://rubygems.org/
+ specs:
+ activerecord (2.3.9)
+ activesupport (= 2.3.9)
+ activesupport (2.3.9)
+ composite_primary_keys (2.3.5.1)
+ activerecord (>= 2.3.5)
+ diff-lcs (1.1.2)
+ rake (0.9.2)
+ rspec (2.6.0)
+ rspec-core (~> 2.6.0)
+ rspec-expectations (~> 2.6.0)
+ rspec-mocks (~> 2.6.0)
+ rspec-core (2.6.4)
+ rspec-expectations (2.6.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.6.0)
+ sqlite3 (1.3.4)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ activerecord (~> 2.3.9)
+ activesupport (~> 2.3.9)
+ composite_primary_keys
+ rake
+ rspec (>= 2.6.0)
+ sqlite3 (~> 1.3.4)
View
@@ -53,6 +53,8 @@ Note that the table definition changed from version 1.0.x to 1.1.x. If you origi
ActsAsRevisionable::RevisionRecord.update_version_1_table
+As of version 1.2, you can customize the RevisionRecord model by adding your own columns or even create a subclass of RevisionRecord that uses its own table. See the documentation in ActsAsRevisionable for more details.
+
== Destroying
By default, the revision history of a record is destroyed along with the record. If you'd like to keep the history and be able to restore deleted records, then you can specify <tt>:dependent => :keep, :on_destroy => true</tt> in options of the +acts_as_revisionable+ call. This will keep the revision records and also automatically wrap all destroy calls in a +store_revision+ block. It is highly recommended that you turn on these options.
View
@@ -1,3 +1,7 @@
+1.2.0
+ - Removed label column from revision_records table
+ - Added support for customizing and subclassing RevisionRecord
+
1.1.2
- Add support for labels on revision records
View
@@ -1,6 +1,5 @@
require 'rubygems'
require 'rake'
-require 'rdoc/task'
desc 'Default: run unit tests.'
task :default => :test
@@ -16,14 +15,6 @@ rescue LoadError
end
end
-desc 'Generate documentation for the gem.'
-Rake::RDocTask.new(:rdoc) do |rdoc|
- rdoc.rdoc_dir = 'rdoc'
- rdoc.options << '--title' << 'Acts As Revisionable' << '--line-numbers' << '--inline-source' << '--main' << 'README.rdoc'
- rdoc.rdoc_files.include('README.rdoc')
- rdoc.rdoc_files.include('lib/**/*.rb')
-end
-
begin
require 'jeweler'
Jeweler::Tasks.new do |gem|
View
@@ -1 +1 @@
-1.1.2
+1.2.0
@@ -29,19 +29,37 @@ module ActsMethods
# <tt>empty_trash</tt> method. You can set <tt>:on_destroy => true</tt> to automatically create the trash revision
# whenever a record is destroyed. It is recommended that you turn both of these features on.
#
- # Revision records have an optional +label+ field which can be used for display purposes to distinguish revisions
- # in a view. This value will only be set if you provide a Proc for the <tt>:label</tt> option to the +acts_as_revisionable+
- # call. The proc will be yielded to with the record before it is revisioned.
+ # Revision records can be extended to include other fields as needed and set with the <tt>:meta</tt> option.
+ # In order to extend a revision record, you must add columns to the database table. The values of the <tt>:meta</tt>
+ # option hash will be provided to the newly created revision record.
#
- # acts_as_revisionable :label => lambda{|record| "Updated by #{record.updated_by} at #{record.updated_at}"}
+ # acts_as_revisionable :meta => {
+ # :updated_by => :last_updated_by,
+ # :label => lambda{|record| "Updated by #{record.updated_by} at #{record.updated_at}"},
+ # :version => 1
+ # }
+ #
+ # The values to the <tt>:meta</tt> hash can be either symbols or Procs. If it is a symbol, the method
+ # so named will be called on the record being revisioned. If it is a Proc, it will be called with the
+ # record as the argument. Any other class will be sent directly to the revision record.
+ #
+ # You can also use a subclass of RevisionRecord if desired so that you can add your own model logic as
+ # necessary. To specify a different class to use for revision records, simply subclass RevisionRecord and
+ # provide the class name to the <tt>:class_name</tt> option.
+ #
+ # acts_as_revisionable :class_name => "MyRevisionRecord"
#
# A has_many :revision_records will also be added to the model for accessing the revisions.
def acts_as_revisionable(options = {})
class_attribute :acts_as_revisionable_options, :instance_writer => false, :instance_reader => false
- self.acts_as_revisionable_options = options.clone
- extend ClassMethods
+ defaults = {:class_name => "ActsAsRevisionable::RevisionRecord"}
+ self.acts_as_revisionable_options = defaults.merge(options)
+ acts_as_revisionable_options[:class_name] = acts_as_revisionable_options[:class_name].name if acts_as_revisionable_options[:class_name].is_a?(Class)
+ extend ClassMethods
include InstanceMethods
- has_many_options = {:as => :revisionable, :order => 'revision DESC', :class_name => "ActsAsRevisionable::RevisionRecord"}
+ class_name =
+ class_name = acts_as_revisionable_options[:class_name].to_s if acts_as_revisionable_options[:class_name]
+ has_many_options = {:as => :revisionable, :order => 'revision DESC', :class_name => class_name}
has_many_options[:dependent] = :destroy unless options[:dependent] == :keep
has_many :revision_records, has_many_options
alias_method_chain :update, :revision if options[:on_update]
@@ -52,12 +70,12 @@ def acts_as_revisionable(options = {})
module ClassMethods
# Get a revision for a specified id.
def revision(id, revision_number)
- RevisionRecord.find_revision(self, id, revision_number)
+ revision_record_class.find_revision(self, id, revision_number)
end
# Get the last revision for a specified id.
def last_revision(id)
- RevisionRecord.last_revision(self, id)
+ revision_record_class.last_revision(self, id)
end
# Load a revision for a record with a particular id. Associations added since the revision
@@ -119,7 +137,11 @@ def revisionable_associations(options = acts_as_revisionable_options[:associatio
# Delete all revision records for deleted items that are older than the specified maximum age in seconds.
def empty_trash(max_age)
- RevisionRecord.empty_trash(self, max_age)
+ revision_record_class.empty_trash(self, max_age)
+ end
+
+ def revision_record_class
+ acts_as_revisionable_options[:class_name].constantize
end
private
@@ -187,7 +209,7 @@ def store_revision
retval = nil
revision = nil
begin
- RevisionRecord.transaction do
+ revision_record_class.transaction do
begin
read_only = self.class.first(:conditions => {self.class.primary_key => self.id}, :readonly => true)
if read_only
@@ -225,9 +247,17 @@ def store_revision
# Create a revision record based on this record and save it to the database.
def create_revision!
- revision = RevisionRecord.new(self, acts_as_revisionable_options[:encoding])
- if self.acts_as_revisionable_options[:label].is_a?(Proc)
- revision.label = self.acts_as_revisionable_options[:label].call(self)
+ revision = revision_record_class.new(self, acts_as_revisionable_options[:encoding])
+ if self.acts_as_revisionable_options[:meta].is_a?(Hash)
+ self.acts_as_revisionable_options[:meta].each do |attribute, value|
+ case value
+ when Symbol
+ value = self.send(value)
+ when Proc
+ value = value.call(self)
+ end
+ revision.send("#{attribute}=", value)
+ end
end
revision.save!
return revision
@@ -236,7 +266,7 @@ def create_revision!
# Truncate the number of revisions kept for this record. Available options are :limit and :minimum_age.
def truncate_revisions!(options = nil)
options = {:limit => acts_as_revisionable_options[:limit], :minimum_age => acts_as_revisionable_options[:minimum_age]} unless options
- RevisionRecord.truncate_revisions(self.class, self.id, options)
+ revision_record_class.truncate_revisions(self.class, self.id, options)
end
# Disable the revisioning behavior inside of a block passed to the method.
@@ -259,6 +289,10 @@ def destroy_with_revision
end
end
+ def revision_record_class
+ self.class.revision_record_class
+ end
+
private
# Update the record while recording the revision.
@@ -46,28 +46,26 @@ def empty_trash(revisionable_type, max_age)
# Create the table to store revision records.
def create_table
- connection.create_table :revision_records do |t|
+ 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
- t.string :label, :limit => 255, :null => true
end
- connection.add_index :revision_records, :revisionable_id, :name => "revision_record_id"
- connection.add_index :revision_records, [:revisionable_type, :created_at, :trash], :name => "revisionable_type_and_created_at"
+ 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
# 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.
def update_version_1_table
# Added in version 1.1.0
connection.add_column(:revision_records, :trash, :boolean, :default => false)
- connection.add_column(:revision_records, :label, :string, :limit => 255, :null => true)
- connection.add_index :revision_records, :revisionable_id, :name => "revision_record_id"
- connection.add_index :revision_records, [:revisionable_type, :created_at, :trash], :name => "revisionable_type_and_created_at"
+ 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")
Oops, something went wrong.

0 comments on commit 123c659

Please sign in to comment.