whodunit 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c68bbab9b951c3ed257a22769b665e0525f8b41ecb23c3f7375488cc099f9f65
4
- data.tar.gz: 3e23380c8ba1502e359c768fe4a99fe3af499598801de896cc030075b8db7de5
3
+ metadata.gz: 1ce926c88b69914bd703491e1bf11f890f64a4c4f5679929d1b898306ea18fcd
4
+ data.tar.gz: 3b277cf8bbf1f36e494f90f959941c38b3829f541b5b36b1168b98a276d830f7
5
5
  SHA512:
6
- metadata.gz: 14e70828520db567bb75dbc472c961fc292281183f630d66d2543e47680947668cd17fc03db2491fa1b0cd52fc423941ca0e73b83e2bca65cbf811a5fdbfc55c
7
- data.tar.gz: 6fe6fb76793405dac3ff6f7ad799aa0736e4a5cdfdd4c878b435210606570ce130a73eb570498efb8fc41e1ff1790c44496aae307dba0af689f5ad3fb416d7e2
6
+ metadata.gz: 68b739ee97b145af1451991f589dfd9353da7ff579592e9f45071c532ed095e2b83e166b30b987bdbd6a22f1f47b421d1dc76fce7818491889376a0249ecdd21
7
+ data.tar.gz: 3796e3c88a21640063c504d27f3fa34a16f077eac5653826cd7a0a4ad513288dec0aa88383afa633daac57d25719711e2325cc33e01079f00725f76d2d22270d
data/.rubocop.yml CHANGED
@@ -6,13 +6,14 @@ plugins:
6
6
 
7
7
  AllCops:
8
8
  NewCops: enable
9
- TargetRubyVersion: 3.0
9
+ TargetRubyVersion: 3.1
10
10
  Exclude:
11
11
  - 'vendor/**/*'
12
12
  - 'tmp/**/*'
13
13
  - 'coverage/**/*'
14
14
  - 'bin/**/*'
15
15
  - 'sig/**/*'
16
+ - 'example/**/*'
16
17
 
17
18
  # Layout & Formatting
18
19
  Layout/LineLength:
data/CHANGELOG.md CHANGED
@@ -7,6 +7,60 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.1] - 2025-01-21
11
+
12
+ ### Added
13
+
14
+ - **ApplicationRecord Integration**: `whodunit install` now prompts to automatically add `Whodunit::Stampable` to `ApplicationRecord` for convenient all-model stamping
15
+ - **Enhanced Configuration Template**: Detailed explanations and examples for each configuration option in generated initializer
16
+ - **Post-install Message**: Helpful instructions displayed after gem installation via `bundle add whodunit`
17
+ - **Comprehensive Test Coverage**: Full test suite for ApplicationRecord integration with edge case handling
18
+
19
+ ### Changed
20
+
21
+ - **Improved CLI Experience**: More user-friendly prompts and messages throughout the installation process
22
+ - **Better Documentation**: Updated README with corrected installation commands and new ApplicationRecord feature
23
+ - **Code Organization**: Extracted ApplicationRecord integration logic into separate module for better maintainability
24
+
25
+ ### Fixed
26
+
27
+ - **Gemspec Consistency**: Corrected post-install message to show `whodunit install` instead of incorrect Rails generator command
28
+ - **RuboCop Compliance**: Fixed all style issues and reduced complexity across codebase
29
+
30
+ ## [0.2.0] - 2025-01-20
31
+
32
+ ### Added
33
+
34
+ - New `whodunit install` CLI command to generate configuration initializer
35
+ - Per-model configuration override capability via `whodunit_config` block
36
+ - Column enabling/disabling - set individual columns to `nil` to disable them
37
+ - Configuration validation to prevent disabling both creator and updater columns
38
+ - Comprehensive generator with Rails app detection and safety prompts
39
+ - Enhanced YARD documentation reflecting simplified architecture
40
+
41
+ ### Changed
42
+
43
+ - **BREAKING**: `soft_delete_column` now defaults to `nil` instead of `:deleted_at`
44
+ - **BREAKING**: Removed automatic soft-delete detection - now purely configuration-based
45
+ - **BREAKING**: Simplified `being_soft_deleted?` logic to check only configured column
46
+ - Migration helpers now respect column enabling/disabling configuration
47
+ - Updated all documentation to reflect simplified, configuration-based approach
48
+ - Improved test infrastructure with better configuration isolation
49
+
50
+ ### Removed
51
+
52
+ - **BREAKING**: `SoftDeleteDetector` class and all auto-detection logic
53
+ - **BREAKING**: `SOFT_DELETE_COLUMNS` constant and pattern-matching detection
54
+ - Complex database schema introspection for soft-delete detection
55
+
56
+ ### Performance
57
+
58
+ - Eliminated expensive auto-detection queries during model initialization
59
+ - Reduced computational overhead by trusting user configuration
60
+ - Simplified callback and association setup based on explicit configuration
61
+
62
+ ## [0.1.0] - 2025-01-15
63
+
10
64
  ### Added
11
65
 
12
66
  - Initial release of Whodunit gem
@@ -36,9 +90,3 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
36
90
 
37
91
  - Rails 7.2+
38
92
  - Ruby 3.1+
39
-
40
- ## [0.1.0] - TBD
41
-
42
- ### Added
43
-
44
- - Initial gem structure and basic functionality
data/README.md CHANGED
@@ -34,6 +34,23 @@ And then execute:
34
34
 
35
35
  $ bundle install
36
36
 
37
+ ### What's Next?
38
+
39
+ After installation, you have a few options:
40
+
41
+ 1. **Generate Configuration & Setup** (Recommended):
42
+ ```bash
43
+ whodunit install
44
+ ```
45
+ This will:
46
+ - Create `config/initializers/whodunit.rb` with all available configuration options
47
+ - Optionally add `Whodunit::Stampable` to your `ApplicationRecord` for automatic stamping on all models
48
+ - Provide clear next steps for adding stamp columns to your database
49
+
50
+ 2. **Quick Setup**: Jump directly to adding stamp columns to your models (see Quick Start below)
51
+
52
+ 3. **Learn More**: Check the [Complete Documentation](https://kanutocd.github.io/whodunit) for advanced configuration
53
+
37
54
  ## Quick Start
38
55
 
39
56
  ### 1. Add Stamp Columns
@@ -106,14 +123,25 @@ user.updater # => User who last updated this record
106
123
  user.deleter # => User who deleted this record (if soft-delete enabled)
107
124
  ```
108
125
 
109
- ## Smart Soft-Delete Detection
126
+ ## Soft-Delete Support
110
127
 
111
- Whodunit automatically detects popular soft-delete solutions:
128
+ Whodunit automatically tracks who deleted records when using soft-delete. Simply configure your soft-delete column:
129
+
130
+ ```ruby
131
+ # Most common soft-delete column (default)
132
+ config.soft_delete_column = :deleted_at
112
133
 
113
- - **Discard** (`gem 'discard'`)
114
- - **Paranoia** (`gem 'paranoia'`)
115
- - **ActsAsParanoid** (`gem 'acts_as_paranoid'`)
116
- - **Custom implementations** with timestamp columns like `deleted_at`, `discarded_at`, etc.
134
+ # For Discard gem users
135
+ config.soft_delete_column = :discarded_at
136
+
137
+ # For custom implementations
138
+ config.soft_delete_column = :archived_at
139
+
140
+ # Disable soft-delete support
141
+ config.soft_delete_column = nil
142
+ ```
143
+
144
+ When configured, Whodunit will automatically add the `deleter_id` column to migrations when the soft-delete column is detected in your table.
117
145
 
118
146
  ## Configuration
119
147
 
@@ -124,7 +152,8 @@ Whodunit.configure do |config|
124
152
  config.creator_column = :created_by_id # Default: :creator_id
125
153
  config.updater_column = :updated_by_id # Default: :updater_id
126
154
  config.deleter_column = :deleted_by_id # Default: :deleter_id
127
- config.auto_detect_soft_delete = false # Default: true
155
+ config.soft_delete_column = :discarded_at # Default: nil
156
+ config.auto_inject_whodunit_stamps = false # Default: true
128
157
 
129
158
  # Column data type configuration
130
159
  config.column_data_type = :integer # Default: :bigint (applies to all columns)
@@ -142,6 +171,46 @@ By default, all stamp columns use `:bigint` data type. You can customize this in
142
171
  - **Individual**: Set specific column types to override the global default
143
172
  - **Per-migration**: Override types on a per-migration basis (see Migration Helpers)
144
173
 
174
+ ### Automatic Injection (Rails Integration)
175
+
176
+ By default, Whodunit automatically adds stamp columns to your migrations, just like how Rails automatically handles `timestamps`:
177
+
178
+ ```ruby
179
+ # Automatic injection is enabled by default!
180
+ # Your migrations automatically get whodunit stamps:
181
+ class CreatePosts < ActiveRecord::Migration[8.0]
182
+ def change
183
+ create_table :posts do |t|
184
+ t.string :title
185
+ t.text :body
186
+ t.timestamps
187
+ # t.whodunit_stamps automatically added after t.timestamps!
188
+ end
189
+ end
190
+ end
191
+
192
+ # Disable automatic injection globally:
193
+ Whodunit.configure do |config|
194
+ config.auto_inject_whodunit_stamps = false
195
+ end
196
+
197
+ # Skip auto-injection for specific tables:
198
+ create_table :system_logs do |t|
199
+ t.string :message
200
+ t.timestamps skip_whodunit_stamps: true
201
+ end
202
+
203
+ # Or add manually if you want specific options:
204
+ create_table :posts do |t|
205
+ t.string :title
206
+ t.whodunit_stamps include_deleter: true # Manual override
207
+ t.timestamps
208
+ # No auto-injection since already added manually
209
+ end
210
+ ```
211
+
212
+ This feature respects soft-delete auto-detection and includes the deleter column when appropriate.
213
+
145
214
  ## Manual User Setting
146
215
 
147
216
  For background jobs, tests, or special scenarios:
data/exe/whodunit ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/whodunit/generator"
5
+
6
+ # Parse command line arguments
7
+ command = ARGV[0]
8
+
9
+ case command
10
+ when "install"
11
+ Whodunit::Generator.install_initializer
12
+ when "help", "--help", "-h", nil
13
+ puts Whodunit::Generator.help_message
14
+ else
15
+ puts "Unknown command: #{command}"
16
+ puts Whodunit::Generator.help_message
17
+ exit 1
18
+ end
@@ -147,8 +147,8 @@ module Whodunit
147
147
  # without_whodunit_user do
148
148
  # Post.bulk_import(data) # No creator_id will be set
149
149
  # end
150
- def without_whodunit_user(&block)
151
- with_whodunit_user(nil, &block)
150
+ def without_whodunit_user(&)
151
+ with_whodunit_user(nil, &)
152
152
  end
153
153
 
154
154
  class_methods do
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Whodunit
4
+ class Generator
5
+ # Handles ApplicationRecord integration functionality
6
+ module ApplicationRecordIntegration
7
+ def self.handle_application_record_integration!
8
+ application_record_file = "app/models/application_record.rb"
9
+
10
+ return unless application_record_exists?(application_record_file)
11
+
12
+ content = File.read(application_record_file)
13
+ return if stampable_already_included?(content)
14
+
15
+ return unless user_wants_application_record_integration?
16
+
17
+ add_stampable_to_application_record!(application_record_file, content)
18
+ end
19
+
20
+ private_class_method def self.application_record_exists?(file_path)
21
+ return true if File.exist?(file_path)
22
+
23
+ puts "⚠️ ApplicationRecord not found at #{file_path}"
24
+ false
25
+ end
26
+
27
+ private_class_method def self.stampable_already_included?(content)
28
+ return false unless content.include?("Whodunit::Stampable")
29
+
30
+ puts "✅ Whodunit::Stampable already included in ApplicationRecord"
31
+ true
32
+ end
33
+
34
+ private_class_method def self.user_wants_application_record_integration?
35
+ puts ""
36
+ puts "🤔 Do you want to include Whodunit::Stampable in ApplicationRecord?"
37
+ puts " This will automatically enable stamping for ALL your models."
38
+ puts " (You can always include it manually in specific models instead)"
39
+ print " Add to ApplicationRecord? (Y/n): "
40
+
41
+ response = $stdin.gets.chomp.downcase
42
+ !%w[n no].include?(response)
43
+ end
44
+
45
+ private_class_method def self.add_stampable_to_application_record!(file_path, content)
46
+ updated_content = try_primary_pattern(content) || try_fallback_pattern(content)
47
+
48
+ if updated_content
49
+ write_updated_application_record!(file_path, updated_content)
50
+ else
51
+ show_manual_integration_message
52
+ end
53
+ end
54
+
55
+ private_class_method def self.try_primary_pattern(content)
56
+ return unless content.match?(/^(\s*class ApplicationRecord < ActiveRecord::Base\s*)$/)
57
+
58
+ content.gsub(
59
+ /^(\s*class ApplicationRecord < ActiveRecord::Base\s*)$/,
60
+ "\\1\n include Whodunit::Stampable"
61
+ )
62
+ end
63
+
64
+ private_class_method def self.try_fallback_pattern(content)
65
+ return unless content.match?(/^(\s*class ApplicationRecord.*\n)/)
66
+
67
+ content.gsub(
68
+ /^(\s*class ApplicationRecord.*\n)/,
69
+ "\\1 include Whodunit::Stampable\n"
70
+ )
71
+ end
72
+
73
+ private_class_method def self.write_updated_application_record!(file_path, content)
74
+ File.write(file_path, content)
75
+ puts "✅ Added Whodunit::Stampable to ApplicationRecord"
76
+ puts " All your models will now automatically include stamping!"
77
+ end
78
+
79
+ private_class_method def self.show_manual_integration_message
80
+ puts "⚠️ Could not automatically modify ApplicationRecord"
81
+ puts " Please manually add: include Whodunit::Stampable"
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require_relative "generator/application_record_integration"
5
+
6
+ module Whodunit
7
+ # Generator for creating Whodunit configuration files
8
+ class Generator
9
+ INITIALIZER_CONTENT = <<~RUBY
10
+ # frozen_string_literal: true
11
+
12
+ # Whodunit configuration
13
+ # This file was generated by `whodunit install` command.
14
+ # Uncomment and modify the options you want to customize.
15
+
16
+ # Whodunit.configure do |config|
17
+ # # User model configuration
18
+ # # Specify which model represents users in your application
19
+ # config.user_class = 'Account' # Default: 'User'
20
+ # # Use 'Account', 'Admin', etc. if your user model has a different name
21
+ #
22
+ # # Column name configuration
23
+ # # Customize the names of the tracking columns in your database
24
+ # config.creator_column = :created_by_id # Default: :creator_id
25
+ # # Column that stores who created the record
26
+ # config.updater_column = :updated_by_id # Default: :updater_id
27
+ # # Column that stores who last updated the record
28
+ # config.deleter_column = :deleted_by_id # Default: :deleter_id
29
+ # # Column that stores who deleted the record (soft-delete only)
30
+ #
31
+ # # Soft-delete integration
32
+ # # Enable tracking of who deleted records when using soft-delete gems
33
+ # config.soft_delete_column = :discarded_at # Default: nil (disabled)
34
+ # # Set to :deleted_at for Paranoia gem
35
+ # # Set to :discarded_at for Discard gem
36
+ # # Set to your custom soft-delete column name
37
+ # # Set to nil to disable soft-delete tracking
38
+ #
39
+ # # Migration auto-injection
40
+ # # Control whether whodunit stamps are automatically added to new migrations
41
+ # config.auto_inject_whodunit_stamps = false # Default: true
42
+ # # When true, automatically adds t.whodunit_stamps to create_table migrations
43
+ # # When false, you must manually add t.whodunit_stamps to your migrations
44
+ #
45
+ # # Column data type configuration
46
+ # # Configure what data types to use for the tracking columns
47
+ # config.column_data_type = :integer # Default: :bigint
48
+ # # Global default for all stamp columns
49
+ # # Common options: :bigint, :integer, :string, :uuid
50
+ #
51
+ # # Individual column type overrides
52
+ # # Override the data type for specific columns (takes precedence over column_data_type)
53
+ # config.creator_column_type = :string # Default: nil (uses column_data_type)
54
+ # # Useful if your user IDs are strings or UUIDs
55
+ # config.updater_column_type = :uuid # Default: nil (uses column_data_type)
56
+ # # Set to match your user model's primary key type
57
+ # config.deleter_column_type = :integer # Default: nil (uses column_data_type)
58
+ # # Only used when soft_delete_column is configured
59
+ # end
60
+ RUBY
61
+
62
+ def self.install_initializer
63
+ config_dir = "config/initializers"
64
+ config_file = File.join(config_dir, "whodunit.rb")
65
+
66
+ validate_rails_application!
67
+ ensure_config_directory_exists!(config_dir)
68
+ handle_existing_file!(config_file)
69
+ create_initializer_file!(config_file)
70
+ ApplicationRecordIntegration.handle_application_record_integration!
71
+ show_success_message(config_file)
72
+ end
73
+
74
+ private_class_method def self.validate_rails_application!
75
+ return if File.exist?("config/application.rb")
76
+
77
+ puts "❌ Error: This doesn't appear to be a Rails application."
78
+ puts " Make sure you're in the root directory of your Rails app."
79
+ exit 1
80
+ end
81
+
82
+ private_class_method def self.ensure_config_directory_exists!(config_dir)
83
+ return if Dir.exist?(config_dir)
84
+
85
+ puts "📁 Creating #{config_dir} directory..."
86
+ FileUtils.mkdir_p(config_dir)
87
+ end
88
+
89
+ private_class_method def self.handle_existing_file!(config_file)
90
+ return unless File.exist?(config_file)
91
+
92
+ puts "⚠️ #{config_file} already exists!"
93
+ print " Do you want to overwrite it? (y/N): "
94
+ response = $stdin.gets.chomp.downcase
95
+ return if %w[y yes].include?(response)
96
+
97
+ puts " Cancelled."
98
+ exit 0
99
+ end
100
+
101
+ private_class_method def self.create_initializer_file!(config_file)
102
+ File.write(config_file, INITIALIZER_CONTENT)
103
+ puts "✅ Generated #{config_file}"
104
+ end
105
+
106
+ private_class_method def self.show_success_message(config_file)
107
+ puts ""
108
+ puts "📝 Next steps:"
109
+ puts " 1. Edit #{config_file} to customize your configuration"
110
+ puts " 2. Uncomment the options you want to change"
111
+ puts " 3. Add stamp columns to your models with migrations:"
112
+ puts " rails generate migration AddStampsToUsers"
113
+ puts " # Then add: add_whodunit_stamps :users"
114
+ puts " 4. Restart your Rails server to apply changes"
115
+ puts ""
116
+ puts "📖 For more information, see: https://github.com/kanutocd/whodunit?tab=readme-ov-file#configuration"
117
+ end
118
+
119
+ def self.help_message
120
+ <<~HELP
121
+ Whodunit - Lightweight creator/updater/deleter tracking for ActiveRecord
122
+
123
+ Usage:
124
+ whodunit install Generate config/initializers/whodunit.rb
125
+ whodunit help Show this help message
126
+
127
+ Examples:
128
+ whodunit install # Creates config/initializers/whodunit.rb with sample configuration
129
+
130
+ For more information, visit: https://github.com/kanutocd/whodunit
131
+ HELP
132
+ end
133
+ end
134
+ end