module VestalVersions
# Adds the functionality necessary to control version creation on a versioned instance of
# ActiveRecord::Base.
module Creation
extend ActiveSupport::Concern
included do
after_create :create_initial_version, :if => :create_initial_version?
after_update :create_version, :if => :create_version?
after_update :update_version, :if => :update_version?
end
# Class methods added to ActiveRecord::Base to facilitate the creation of new versions.
module ClassMethods
# Overrides the basal +prepare_versioned_options+ method defined in VestalVersions::Options
# to extract the :only, :except and :initial_version options
# into +vestal_versions_options+.
def prepare_versioned_options(options)
result = super(options)
self.vestal_versions_options[:only] = Array(options.delete(:only)).map(&:to_s).uniq if options[:only]
self.vestal_versions_options[:except] = Array(options.delete(:except)).map(&:to_s).uniq if options[:except]
self.vestal_versions_options[:initial_version] = options.delete(:initial_version)
result
end
end
# Instance methods that determine whether to save a version and actually perform the save.
private
# Returns whether an initial version should be created upon creation of the parent record.
def create_initial_version?
vestal_versions_options[:initial_version] == true
end
# Creates an initial version upon creation of the parent record.
def create_initial_version
versions.create(version_attributes.merge(:number => 1))
reset_version_changes
reset_version
end
# Returns whether a new version should be created upon updating the parent record.
def create_version?
!version_changes.blank?
end
# Creates a new version upon updating the parent record.
def create_version(attributes = nil)
versions.create(attributes || version_attributes)
reset_version_changes
reset_version
end
# Returns whether the last version should be updated upon updating the parent record.
# This method is overridden in VestalVersions::Control to account for a control block that
# merges changes onto the previous version.
def update_version?
false
end
# Updates the last version's changes by appending the current version changes.
def update_version
return create_version unless v = versions.last
v.modifications_will_change!
v.update_attribute(:modifications, v.changes.append_changes(version_changes))
reset_version_changes
reset_version
end
# Returns an array of column names that should be included in the changes of created
# versions. If vestal_versions_options[:only] is specified, only those columns
# will be versioned. Otherwise, if vestal_versions_options[:except] is specified,
# all columns will be versioned other than those specified. Without either option, the
# default is to version all columns. At any rate, the four "automagic" timestamp columns
# maintained by Rails are never versioned.
def versioned_columns
case
when vestal_versions_options[:only] then self.class.column_names & vestal_versions_options[:only]
when vestal_versions_options[:except] then self.class.column_names - vestal_versions_options[:except]
else self.class.column_names
end - %w(created_at created_on updated_at updated_on)
end
# Specifies the attributes used during version creation. This is separated into its own
# method so that it can be overridden by the VestalVersions::Users feature.
def version_attributes
{:modifications => version_changes, :number => last_version + 1}
end
end
end