Module: Whodunit
- Defined in:
- lib/whodunit.rb,
lib/whodunit/current.rb,
lib/whodunit/railtie.rb,
lib/whodunit/version.rb,
lib/whodunit/generator.rb,
lib/whodunit/stampable.rb,
lib/whodunit/migration_helpers.rb,
lib/whodunit/controller_methods.rb,
lib/whodunit/table_definition_extension.rb,
lib/whodunit/generator/application_record_integration.rb
Overview
Lightweight creator/updater/deleter tracking for ActiveRecord models.
Whodunit provides simple auditing by tracking who created, updated, and deleted ActiveRecord models. It features smart soft-delete detection and zero performance overhead.
Defined Under Namespace
Modules: ControllerMethods, MigrationHelpers, Stampable, TableDefinitionExtension Classes: Current, Error, Generator, Railtie
Constant Summary collapse
- VERSION =
"0.5.0"
Configuration collapse
-
#auto_create_user_fk_constraints ⇒ Boolean
Whether migration helpers should create foreign keys to the user table.
-
#auto_inject_whodunit_stamps ⇒ Boolean
Whether to automatically add whodunit_stamps to create_table migrations (default: true).
-
#creator_column ⇒ Symbol
The column name for tracking who created the record (default: :creator_id).
-
#deleter_column ⇒ Symbol
The column name for tracking who deleted the record (default: :deleter_id).
-
#soft_delete_column ⇒ Symbol?
The column name used for soft-delete timestamps (default: nil) Set to a column name to enable soft-delete support (e.g., :deleted_at, :discarded_at) Set to nil to disable soft-delete support entirely.
-
#updater_column ⇒ Symbol
The column name for tracking who updated the record (default: :updater_id).
-
#user_class ⇒ String
The class name of the user model (default: “User”).
-
#user_class_table_name ⇒ String, ...
Explicit user table name override.
Data Type Configuration collapse
-
#column_data_type ⇒ Symbol
The default data type for stamp columns (default: :bigint).
-
#creator_column_type ⇒ Symbol?
Specific data type for creator column (overrides column_data_type if set).
-
#deleter_column_type ⇒ Symbol?
Specific data type for deleter column (overrides column_data_type if set).
-
#updater_column_type ⇒ Symbol?
Specific data type for updater column (overrides column_data_type if set).
Reverse Association Configuration collapse
-
#auto_setup_reverse_associations ⇒ Boolean
Whether to automatically set up reverse associations on the user class (default: true) When enabled, including Whodunit::Stampable in a model will automatically add has_many associations to the user class (e.g., has_many :created_posts).
-
#reverse_association_prefix ⇒ String
Prefix for reverse association names (default: “”) Used to generate association names like “created_posts”, “updated_comments”.
-
#reverse_association_suffix ⇒ String
Suffix for reverse association names (default: “”) Used to generate association names like “posts_created”, “comments_updated”.
Model Registry collapse
-
.configure {|self| ... } ⇒ void
Configure Whodunit settings.
-
.user_class_name ⇒ String
Get the user class name as a string.
-
.user_table_name ⇒ String
Resolve the configured user table name without loading the model.
-
#registered_models ⇒ Array<Class>
Registry to track models that include Whodunit::Stampable This is used to set up reverse associations on the user class.
Data Type Helpers collapse
-
.creator_data_type ⇒ Symbol
Get the data type for the creator column.
-
.creator_enabled? ⇒ Boolean
Check if creator column is enabled.
-
.deleter_data_type ⇒ Symbol
Get the data type for the deleter column.
-
.deleter_enabled? ⇒ Boolean
Check if deleter column is enabled.
-
.soft_delete_enabled? ⇒ Boolean
Check if soft-delete is enabled.
-
.updater_data_type ⇒ Symbol
Get the data type for the updater column.
-
.updater_enabled? ⇒ Boolean
Check if updater column is enabled.
Model Registration & Reverse Associations collapse
-
.generate_reverse_association_name(action, model_plural) ⇒ String
Generate a reverse association name based on action and model name.
-
.register_model(model_class) ⇒ void
Register a model class that includes Whodunit::Stampable This is called automatically when Stampable is included.
-
.resolve_foreign_key(model_class, foreign_key_column) ⇒ Symbol
Resolve the actual foreign key column name from model configuration.
-
.resolve_user_class ⇒ Class?
Resolve the user class constant.
-
.setup_all_reverse_associations ⇒ void
Set up all reverse associations for all registered models This can be called manually if needed (e.g., after configuration changes).
-
.setup_creator_reverse_association(user_class_instance, model_class, model_plural) ⇒ void
Set up creator reverse association.
-
.setup_deleter_reverse_association(user_class_instance, model_class, model_plural) ⇒ void
Set up deleter reverse association.
-
.setup_reverse_associations_for_model(model_class) ⇒ void
Set up reverse associations on the user class for a specific model.
-
.setup_updater_reverse_association(user_class_instance, model_class, model_plural) ⇒ void
Set up updater reverse association.
-
.setup_user_reverse_association(user_class_instance, association_name, model_class, foreign_key_column) ⇒ void
Set up a specific reverse association on the user class.
-
.validate_column_configuration! ⇒ Object
Validate that column configuration is valid.
Class Method Details
.configure {|self| ... } ⇒ void
This method returns an undefined value.
Configure Whodunit settings
132 133 134 135 |
# File 'lib/whodunit.rb', line 132 def self.configure yield self validate_column_configuration! end |
.creator_data_type ⇒ Symbol
Get the data type for the creator column
163 164 165 |
# File 'lib/whodunit.rb', line 163 def self.creator_data_type creator_column_type || column_data_type end |
.creator_enabled? ⇒ Boolean
Check if creator column is enabled
187 188 189 |
# File 'lib/whodunit.rb', line 187 def self.creator_enabled? !creator_column.nil? end |
.deleter_data_type ⇒ Symbol
Get the data type for the deleter column
175 176 177 |
# File 'lib/whodunit.rb', line 175 def self.deleter_data_type deleter_column_type || column_data_type end |
.deleter_enabled? ⇒ Boolean
Check if deleter column is enabled
199 200 201 |
# File 'lib/whodunit.rb', line 199 def self.deleter_enabled? !deleter_column.nil? end |
.generate_reverse_association_name(action, model_plural) ⇒ String
Generate a reverse association name based on action and model name
248 249 250 |
# File 'lib/whodunit.rb', line 248 def self.generate_reverse_association_name(action, model_plural) "#{reverse_association_prefix}#{action}_#{model_plural}#{reverse_association_suffix}" end |
.register_model(model_class) ⇒ void
This method returns an undefined value.
Register a model class that includes Whodunit::Stampable This is called automatically when Stampable is included
209 210 211 212 213 214 215 216 217 |
# File 'lib/whodunit.rb', line 209 def self.register_model(model_class) return unless auto_setup_reverse_associations return if registered_models.include?(model_class) return if model_class.respond_to?(:whodunit_reverse_associations_enabled?) && !model_class.whodunit_reverse_associations_enabled? registered_models << model_class setup_reverse_associations_for_model(model_class) end |
.resolve_foreign_key(model_class, foreign_key_column) ⇒ Symbol
Resolve the actual foreign key column name from model configuration
319 320 321 322 323 324 325 326 327 328 329 330 |
# File 'lib/whodunit.rb', line 319 def self.resolve_foreign_key(model_class, foreign_key_column) return foreign_key_column unless model_class.respond_to?(:whodunit_setting) column_mapping = { creator_id: :creator_column, updater_id: :updater_column, deleter_id: :deleter_column } setting_key = column_mapping[foreign_key_column] setting_key ? (model_class.whodunit_setting(setting_key) || foreign_key_column) : foreign_key_column end |
.resolve_user_class ⇒ Class?
Resolve the user class constant
254 255 256 257 258 |
# File 'lib/whodunit.rb', line 254 def self.resolve_user_class user_class.constantize rescue StandardError nil end |
.setup_all_reverse_associations ⇒ void
This method returns an undefined value.
Set up all reverse associations for all registered models This can be called manually if needed (e.g., after configuration changes)
238 239 240 241 242 |
# File 'lib/whodunit.rb', line 238 def self.setup_all_reverse_associations registered_models.each do |model_class| setup_reverse_associations_for_model(model_class) end end |
.setup_creator_reverse_association(user_class_instance, model_class, model_plural) ⇒ void
This method returns an undefined value.
Set up creator reverse association
265 266 267 268 269 270 |
# File 'lib/whodunit.rb', line 265 def self.setup_creator_reverse_association(user_class_instance, model_class, model_plural) return unless model_class.respond_to?(:model_creator_enabled?) && model_class.model_creator_enabled? association_name = generate_reverse_association_name("created", model_plural) setup_user_reverse_association(user_class_instance, association_name, model_class, :creator_id) end |
.setup_deleter_reverse_association(user_class_instance, model_class, model_plural) ⇒ void
This method returns an undefined value.
Set up deleter reverse association
289 290 291 292 293 294 295 |
# File 'lib/whodunit.rb', line 289 def self.setup_deleter_reverse_association(user_class_instance, model_class, model_plural) return unless model_class.respond_to?(:model_deleter_enabled?) && model_class.model_deleter_enabled? return unless model_class.respond_to?(:soft_delete_enabled?) && model_class.soft_delete_enabled? association_name = generate_reverse_association_name("deleted", model_plural) setup_user_reverse_association(user_class_instance, association_name, model_class, :deleter_id) end |
.setup_reverse_associations_for_model(model_class) ⇒ void
This method returns an undefined value.
Set up reverse associations on the user class for a specific model
222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/whodunit.rb', line 222 def self.setup_reverse_associations_for_model(model_class) return unless auto_setup_reverse_associations user_class_instance = resolve_user_class return unless user_class_instance.respond_to?(:has_many) model_plural = model_class.name.underscore.pluralize setup_creator_reverse_association(user_class_instance, model_class, model_plural) setup_updater_reverse_association(user_class_instance, model_class, model_plural) setup_deleter_reverse_association(user_class_instance, model_class, model_plural) end |
.setup_updater_reverse_association(user_class_instance, model_class, model_plural) ⇒ void
This method returns an undefined value.
Set up updater reverse association
277 278 279 280 281 282 |
# File 'lib/whodunit.rb', line 277 def self.setup_updater_reverse_association(user_class_instance, model_class, model_plural) return unless model_class.respond_to?(:model_updater_enabled?) && model_class.model_updater_enabled? association_name = generate_reverse_association_name("updated", model_plural) setup_user_reverse_association(user_class_instance, association_name, model_class, :updater_id) end |
.setup_user_reverse_association(user_class_instance, association_name, model_class, foreign_key_column) ⇒ void
This method returns an undefined value.
Set up a specific reverse association on the user class
303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/whodunit.rb', line 303 def self.setup_user_reverse_association(user_class_instance, association_name, model_class, foreign_key_column) actual_foreign_key = resolve_foreign_key(model_class, foreign_key_column) # Check if association already exists to avoid duplicates return if user_class_instance.reflect_on_association(association_name.to_sym) user_class_instance.has_many association_name.to_sym, class_name: model_class.name, foreign_key: actual_foreign_key, dependent: :nullify end |
.soft_delete_enabled? ⇒ Boolean
Check if soft-delete is enabled
181 182 183 |
# File 'lib/whodunit.rb', line 181 def self.soft_delete_enabled? !soft_delete_column.nil? end |
.updater_data_type ⇒ Symbol
Get the data type for the updater column
169 170 171 |
# File 'lib/whodunit.rb', line 169 def self.updater_data_type updater_column_type || column_data_type end |
.updater_enabled? ⇒ Boolean
Check if updater column is enabled
193 194 195 |
# File 'lib/whodunit.rb', line 193 def self.updater_enabled? !updater_column.nil? end |
.user_class_name ⇒ String
Get the user class name as a string
140 141 142 |
# File 'lib/whodunit.rb', line 140 def self.user_class_name user_class.to_s end |
.user_table_name ⇒ String
Resolve the configured user table name without loading the model. Migration execution can happen before the user table exists, so constantizing the user class here may cause Active Record to query a missing relation. Explicit configuration takes precedence; class objects may still provide their custom Active Record table name directly.
151 152 153 154 155 156 157 |
# File 'lib/whodunit.rb', line 151 def self.user_table_name return user_class_table_name.to_s if user_class_table_name return user_class.table_name.to_s if user_class.respond_to?(:table_name) user_class_name.underscore.pluralize end |
.validate_column_configuration! ⇒ Object
Validate that column configuration is valid
334 335 336 337 338 339 340 |
# File 'lib/whodunit.rb', line 334 def self.validate_column_configuration! return if creator_enabled? || updater_enabled? raise Whodunit::Error, "At least one of creator_column or updater_column must be configured (not nil). " \ "Setting both to nil would disable all stamping functionality." end |
Instance Method Details
#auto_create_user_fk_constraints ⇒ Boolean
Whether migration helpers should create foreign keys to the user table.
75 |
# File 'lib/whodunit.rb', line 75 mattr_accessor :auto_create_user_fk_constraints, default: false |
#auto_inject_whodunit_stamps ⇒ Boolean
Whether to automatically add whodunit_stamps to create_table migrations (default: true)
71 |
# File 'lib/whodunit.rb', line 71 mattr_accessor :auto_inject_whodunit_stamps, default: true |
#auto_setup_reverse_associations ⇒ Boolean
Whether to automatically set up reverse associations on the user class (default: true) When enabled, including Whodunit::Stampable in a model will automatically add has_many associations to the user class (e.g., has_many :created_posts)
101 |
# File 'lib/whodunit.rb', line 101 mattr_accessor :auto_setup_reverse_associations, default: true |
#column_data_type ⇒ Symbol
The default data type for stamp columns (default: :bigint)
81 |
# File 'lib/whodunit.rb', line 81 mattr_accessor :column_data_type, default: :bigint |
#creator_column ⇒ Symbol
The column name for tracking who created the record (default: :creator_id)
53 |
# File 'lib/whodunit.rb', line 53 mattr_accessor :creator_column, default: :creator_id |
#creator_column_type ⇒ Symbol?
Specific data type for creator column (overrides column_data_type if set)
85 |
# File 'lib/whodunit.rb', line 85 mattr_accessor :creator_column_type, default: nil |
#deleter_column ⇒ Symbol
The column name for tracking who deleted the record (default: :deleter_id)
61 |
# File 'lib/whodunit.rb', line 61 mattr_accessor :deleter_column, default: :deleter_id |
#deleter_column_type ⇒ Symbol?
Specific data type for deleter column (overrides column_data_type if set)
93 |
# File 'lib/whodunit.rb', line 93 mattr_accessor :deleter_column_type, default: nil |
#registered_models ⇒ Array<Class>
Registry to track models that include Whodunit::Stampable This is used to set up reverse associations on the user class
118 |
# File 'lib/whodunit.rb', line 118 mattr_accessor :registered_models, default: [] |
#reverse_association_prefix ⇒ String
Prefix for reverse association names (default: “”) Used to generate association names like “created_posts”, “updated_comments”
106 |
# File 'lib/whodunit.rb', line 106 mattr_accessor :reverse_association_prefix, default: "" |
#reverse_association_suffix ⇒ String
Suffix for reverse association names (default: “”) Used to generate association names like “posts_created”, “comments_updated”
111 |
# File 'lib/whodunit.rb', line 111 mattr_accessor :reverse_association_suffix, default: "" |
#soft_delete_column ⇒ Symbol?
The column name used for soft-delete timestamps (default: nil) Set to a column name to enable soft-delete support (e.g., :deleted_at, :discarded_at) Set to nil to disable soft-delete support entirely
67 |
# File 'lib/whodunit.rb', line 67 mattr_accessor :soft_delete_column, default: nil |
#updater_column ⇒ Symbol
The column name for tracking who updated the record (default: :updater_id)
57 |
# File 'lib/whodunit.rb', line 57 mattr_accessor :updater_column, default: :updater_id |
#updater_column_type ⇒ Symbol?
Specific data type for updater column (overrides column_data_type if set)
89 |
# File 'lib/whodunit.rb', line 89 mattr_accessor :updater_column_type, default: nil |
#user_class ⇒ String
The class name of the user model (default: “User”)
44 |
# File 'lib/whodunit.rb', line 44 mattr_accessor :user_class, default: "User" |
#user_class_table_name ⇒ String, ...
Explicit user table name override. When nil, the table name is inferred from the configured user class.
49 |
# File 'lib/whodunit.rb', line 49 mattr_accessor :user_class_table_name, default: nil |