# Extends the stamping functionality of ActiveRecord by automatically recording the model # responsible for creating, updating, and deleting the current object. See the +Stamper+ and # +ControllerAdditions+ modules for further documentation on how the entire process works. module ActiveRecord::Userstamp::Stampable extend ActiveSupport::Concern included do # Should ActiveRecord record userstamps? Defaults to true. class_attribute :record_userstamp self.record_userstamp = true class_attribute :stamper_class_name before_validation :set_updater_attribute, if: :record_userstamp before_validation :set_creator_attribute, on: :create, if: :record_userstamp before_save :set_updater_attribute, if: :record_userstamp before_save :set_creator_attribute, on: :create, if: :record_userstamp before_destroy :set_deleter_attribute, if: :record_userstamp end module ClassMethods def inherited(klass) super klass.class_eval do add_userstamp_associations({}) end end # This method customizes how the gem functions. For example: # # class Post < ActiveRecord::Base # stampable stamper_class_name: Person.name, # with_deleted: true # end # # The method will set up all the associations. Extra arguments (like +:with_deleted+) will be # propagated to the associations. # # By default, the deleter association is not defined unless the :deleter_attribute is set in # the gem configuration. def stampable(options = {}) self.stamper_class_name = options.delete(:stamper_class_name) if options.key?(:stamper_class_name) add_userstamp_associations(options) end # Temporarily allows you to turn stamping off. For example: # # Post.without_stamps do # post = Post.find(params[:id]) # post.update_attributes(params[:post]) # post.save # end def without_stamps original_value = self.record_userstamp self.record_userstamp = false yield ensure self.record_userstamp = original_value end def stamper_class #:nodoc: stamper_class_name.to_s.camelize.constantize rescue nil end private # Defines the associations for Userstamp. def add_userstamp_associations(options) ActiveRecord::Userstamp::Utilities.remove_association(self, :creator) ActiveRecord::Userstamp::Utilities.remove_association(self, :updater) ActiveRecord::Userstamp::Utilities.remove_association(self, :deleter) associations = ActiveRecord::Userstamp::Utilities.available_association_columns(self) return if associations.nil? config = ActiveRecord::Userstamp.config klass = stamper_class.try(:name) relation_options = options.reverse_merge(class_name: klass) belongs_to :creator, relation_options.reverse_merge(foreign_key: config.creator_attribute) if associations.first belongs_to :updater, relation_options.reverse_merge(foreign_key: config.updater_attribute) if associations.second belongs_to :deleter, relation_options.reverse_merge(foreign_key: config.deleter_attribute) if associations.third end end private def has_stamper? !self.class.stamper_class.nil? && !self.class.stamper_class.stamper.nil? end def set_creator_attribute creator_attribute = ActiveRecord::Userstamp.config.creator_attribute return if !has_stamper? || creator_attribute.nil? || !has_attribute?(creator_attribute) current_attribute_value = send(creator_attribute) return if current_attribute_value.present? send("#{creator_attribute}=", self.class.stamper_class.stamper) end def set_updater_attribute updater_attribute = ActiveRecord::Userstamp.config.updater_attribute return if !has_stamper? || updater_attribute.nil? || !has_attribute?(updater_attribute) return if !self.new_record? && !self.changed? send("#{updater_attribute}=", self.class.stamper_class.stamper) end def set_deleter_attribute deleter_attribute = ActiveRecord::Userstamp.config.deleter_attribute return if !has_stamper? || deleter_attribute.nil? || !has_attribute?(deleter_attribute) send("#{deleter_attribute}=", self.class.stamper_class.stamper) save end end