Module: Whodunit::Stampable

Extended by:
ActiveSupport::Concern
Defined in:
lib/whodunit/stampable.rb

Overview

Main concern for adding creator/updater/deleter tracking to ActiveRecord models.

This module provides automatic tracking of who created, updated, and deleted records. It intelligently sets up callbacks and associations based on available columns and soft-delete detection.

rubocop:disable Metrics/ModuleLength

Examples:

Basic usage

class Post < ApplicationRecord
  include Whodunit::Stampable
end

# Requires creator_id and/or updater_id columns
# Automatically adds deleter_id tracking if soft-delete is detected

Migration

class CreatePosts < ActiveRecord::Migration[7.0]
  def change
    create_table :posts do |t|
      t.string :title
      t.text :body
      t.whodunit_stamps  # Adds creator_id, updater_id columns
      t.timestamps
    end
  end
end

Manual deleter tracking

class Post < ApplicationRecord
  include Whodunit::Stampable

  # Force enable deleter tracking even without soft-delete
  enable_whodunit_deleter!
end

Since:

  • 0.1.0

Callback Methods collapse

Column Presence Checks collapse

Instance Method Details

#being_soft_deleted?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Check if the current update operation is a soft-delete.

Uses ActiveRecord’s dirty tracking to detect if any soft-delete columns are being changed from nil to a timestamp, which indicates a soft-delete operation.

Returns:

  • (Boolean)

    true if this update is setting a soft-delete timestamp

Since:

  • 0.1.0



249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/whodunit/stampable.rb', line 249

def being_soft_deleted?
  return false unless deleter_column?
  return false unless Whodunit::Current.user_id

  soft_delete_column = self.class.whodunit_setting(:soft_delete_column)
  return false unless soft_delete_column

  # Simple: just check the configured soft-delete column
  column_name = soft_delete_column.to_s
  attribute_changed?(column_name) &&
    attribute_was(column_name).nil? &&
    !send(column_name).nil?
end

#creator_column?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Check if the model has a creator column and it’s enabled.

Returns:

  • (Boolean)

    true if the creator column exists and is enabled

Since:

  • 0.1.0



213
214
215
216
217
218
# File 'lib/whodunit/stampable.rb', line 213

def creator_column?
  return false unless self.class.model_creator_enabled?

  column_name = self.class.whodunit_setting(:creator_column).to_s
  self.class.column_names.include?(column_name)
end

#deleter_column?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Check if the model has a deleter column and it’s enabled.

Returns:

  • (Boolean)

    true if the deleter column exists and is enabled

Since:

  • 0.1.0



235
236
237
238
239
240
# File 'lib/whodunit/stampable.rb', line 235

def deleter_column?
  return false unless self.class.model_deleter_enabled?

  column_name = self.class.whodunit_setting(:deleter_column).to_s
  self.class.column_names.include?(column_name)
end

#set_whodunit_creatorvoid

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Set the creator ID when a record is created.

This method is automatically called before_create if the model has a creator column. It sets the creator_id to the current user from Whodunit::Current.

Since:

  • 0.1.0



167
168
169
170
171
# File 'lib/whodunit/stampable.rb', line 167

def set_whodunit_creator
  return unless Whodunit::Current.user_id

  self[self.class.whodunit_setting(:creator_column)] = Whodunit::Current.user_id
end

#set_whodunit_deletervoid

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Set the deleter ID when a record is destroyed or soft-deleted.

This method is automatically called in two scenarios: 1. before_destroy for hard deletes (if deleter column exists) 2. before_update for soft-deletes (when being_soft_deleted? returns true)

It sets the deleter_id to the current user from Whodunit::Current.

Since:

  • 0.1.0



201
202
203
204
205
# File 'lib/whodunit/stampable.rb', line 201

def set_whodunit_deleter
  return unless Whodunit::Current.user_id

  self[self.class.whodunit_setting(:deleter_column)] = Whodunit::Current.user_id
end

#set_whodunit_updatervoid

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Set the updater ID when a record is updated.

This method is automatically called before_update if the model has an updater column. It sets the updater_id to the current user from Whodunit::Current. Does not run on new records (creation) or during soft-delete operations.

Since:

  • 0.1.0



181
182
183
184
185
186
187
188
189
# File 'lib/whodunit/stampable.rb', line 181

def set_whodunit_updater
  return unless Whodunit::Current.user_id

  return if new_record? # Don't set updater on creation

  return if being_soft_deleted? # Don't set updater during soft-delete

  self[self.class.whodunit_setting(:updater_column)] = Whodunit::Current.user_id
end

#updater_column?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Check if the model has an updater column and it’s enabled.

Returns:

  • (Boolean)

    true if the updater column exists and is enabled

Since:

  • 0.1.0



224
225
226
227
228
229
# File 'lib/whodunit/stampable.rb', line 224

def updater_column?
  return false unless self.class.model_updater_enabled?

  column_name = self.class.whodunit_setting(:updater_column).to_s
  self.class.column_names.include?(column_name)
end