whodunit 0.4.1 → 0.5.0

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: ec3f89bd20ea577eb0a740137c86a7927500ccd189e9d9375d18b21c0b36ffdc
4
- data.tar.gz: 20586d959bc0d25a47319f615f333021d1ebbec331d865c6a18400987f24ef39
3
+ metadata.gz: 3c21f2da7abe6b4a2b1440a155e75f28e8e73fea5bcd5cc4169a8c07c49a02c4
4
+ data.tar.gz: f1e0a0978d93eeff03293f1fe92b1aabcc536bd043e2972b28d360dd4c9e4259
5
5
  SHA512:
6
- metadata.gz: 5cca573bee0c6d0313d5adf4079e18996d4bc5ecf354b7de39f77e9537b078c67d6122d4a2a7c8c3314ec9da44f40a5ec262a573c06e12b092f86935a951b327
7
- data.tar.gz: 810bd48aaf36a9605802c62d8f6616d0d68eb63b78a2f92c955c0f06b0cc609f0f6c729274bfc33d5caf42e8263ce8a49d4aa253480db7103dd0c0ea756ce5e1
6
+ metadata.gz: 8a42ff533dd671e32a8a409a8bdd90ddb74b3dd34ab015cdfccabaee192761e8b1f629ff145cc22bf9db08d131e63009c1698a730710177434c2b46337f2a893
7
+ data.tar.gz: 9d5663c7629baa96a6076c6e3b8e4b4f201ced618653e5a27e3a8273db0bfe3ae7a99b6451518361af04e2793a4f0d17564f0959c33dc3028cdd34fe82d0ce9d
data/.rubocop.yml CHANGED
@@ -43,6 +43,11 @@ Metrics/MethodLength:
43
43
  Metrics/ModuleLength:
44
44
  Exclude:
45
45
  - 'lib/whodunit.rb'
46
+ - 'lib/whodunit/migration_helpers.rb'
47
+
48
+ Metrics/ParameterLists:
49
+ Exclude:
50
+ - 'lib/whodunit/migration_helpers.rb'
46
51
 
47
52
  # RSpec specific configurations
48
53
  RSpec/ExampleLength:
@@ -113,6 +118,7 @@ Metrics/AbcSize:
113
118
  Max: 20
114
119
  Exclude:
115
120
  - 'spec/**/*'
121
+ - 'lib/whodunit/migration_helpers.rb'
116
122
 
117
123
  Metrics/CyclomaticComplexity:
118
124
  Max: 10
data/CHANGELOG.md CHANGED
@@ -1,9 +1,23 @@
1
1
  # Changelog
2
2
 
3
- All notable changes to this project will be documented in this file.
3
+ ## Unreleased
4
4
 
5
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
- and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
+ ## [0.5.0] - 2026-08-20
6
+
7
+ ### Added
8
+
9
+ - Added `auto_create_user_fk_constraints` configuration for automatically creating foreign-key constraints from creator, updater, and deleter stamp columns to the configured user table.
10
+ - Added `user_class_table_name` for explicitly overriding the inferred user table name.
11
+ - Added per-migration overrides for automatic user foreign-key creation.
12
+ - Added automatic injection of the configured soft-delete timestamp alongside the deleter stamp column.
13
+
14
+ ### Fixed
15
+
16
+ - Made stamp auto-injection idempotent when migrations already declare stamp columns or matching foreign keys.
17
+ - Removed stamp foreign-key constraints before removing stamp columns in migration helpers.
18
+ - Avoided loading the configured user model while migrations infer its table name, preventing failures when the user table is being created in the same migration.
19
+ - Skipped automatic stamp and user-FK injection for Rails internal metadata tables during database setup.
20
+ - Made `require "whodunit"` safe before Active Record loads by deferring migration integration until Active Record is available.
7
21
 
8
22
  ## [0.4.1] - 2026-06-01
9
23
 
data/README.md CHANGED
@@ -208,6 +208,8 @@ Whodunit.configure do |config|
208
208
  config.deleter_column = :deleted_by_id # Default: :deleter_id
209
209
  config.soft_delete_column = :discarded_at # Default: nil
210
210
  config.auto_inject_whodunit_stamps = false # Default: true
211
+ config.auto_create_user_fk_constraints = false # Default: false
212
+ # config.user_class_table_name = :accounts # Optional explicit table name
211
213
 
212
214
  # Reverse association configuration
213
215
  config.auto_setup_reverse_associations = false # Default: true
@@ -222,6 +224,40 @@ Whodunit.configure do |config|
222
224
  end
223
225
  ```
224
226
 
227
+ When `auto_create_user_fk_constraints` is enabled, migration helpers add
228
+ foreign-key constraints from the configured creator, updater, and deleter
229
+ columns to the configured user table. The user table is inferred from
230
+ `user_class` unless `user_class_table_name` is set explicitly.
231
+ For the standard `User` model, Whodunit infers `users`; setting
232
+ `user_class_table_name` is not required. Use it only when the model maps to a
233
+ nonstandard table name or when you want to make that mapping explicit.
234
+
235
+ **Caution:** the referenced user table must be created before migrations that
236
+ create these foreign keys. The user model is not loaded while resolving the
237
+ table name, so a user table can safely use automatic stamps in its own
238
+ migration. Use `auto_create_user_fk_constraints: false` for migrations that
239
+ must run before the user table exists.
240
+
241
+ Automatic injection intentionally skips Rails internal metadata tables such as
242
+ `ar_internal_metadata` and `schema_migrations`.
243
+
244
+ When `soft_delete_column` is configured, `include_deleter: :auto` also adds
245
+ the configured soft-delete timestamp and deleter columns to new tables. The
246
+ injection is idempotent when either column was declared explicitly. Pass
247
+ `include_deleter: false` to omit only the deleter audit column for a specific
248
+ table.
249
+
250
+ The setting can be overridden for an individual migration:
251
+
252
+ ```ruby
253
+ create_table :posts do |t|
254
+ t.timestamps
255
+ t.whodunit_stamps auto_create_user_fk_constraints: false
256
+ end
257
+
258
+ add_whodunit_stamps :legacy_posts, auto_create_user_fk_constraints: true
259
+ ```
260
+
225
261
  ### Data Type Configuration
226
262
 
227
263
  By default, all stamp columns use `:bigint` data type. You can customize this in several ways:
@@ -26,7 +26,7 @@ module Whodunit
26
26
  # config.updater_column = :updated_by_id # Default: :updater_id
27
27
  # # Column that stores who last updated the record
28
28
  # config.deleter_column = :deleted_by_id # Default: :deleter_id
29
- # # Column that stores who deleted the record (soft-delete only)
29
+ # # Column that stores who performed the delete operation
30
30
  #
31
31
  # # Soft-delete integration
32
32
  # # Enable tracking of who deleted records when using soft-delete gems
@@ -39,8 +39,11 @@ module Whodunit
39
39
  # # Migration auto-injection
40
40
  # # Control whether whodunit stamps are automatically added to new migrations
41
41
  # config.auto_inject_whodunit_stamps = false # Default: true
42
- # # When true, automatically adds t.whodunit_stamps to create_table migrations
42
+ # # When true, automatically adds stamps to create_table definitions using timestamps
43
43
  # # When false, you must manually add t.whodunit_stamps to your migrations
44
+ # config.auto_create_user_fk_constraints = false # Default: false
45
+ # # When true, adds foreign keys from stamp columns to the user table
46
+ # # config.user_class_table_name = :accounts # Optional explicit user table name
44
47
  #
45
48
  # # Column data type configuration
46
49
  # # Configure what data types to use for the tracking columns
@@ -55,7 +58,7 @@ module Whodunit
55
58
  # config.updater_column_type = :uuid # Default: nil (uses column_data_type)
56
59
  # # Set to match your user model's primary key type
57
60
  # config.deleter_column_type = :integer # Default: nil (uses column_data_type)
58
- # # Only used when soft_delete_column is configured
61
+ # # Used whenever the deleter column is injected
59
62
  # end
60
63
  RUBY
61
64
 
@@ -74,7 +74,12 @@ module Whodunit
74
74
  # add_whodunit_stamps :posts, include_deleter: true
75
75
  # @example Exclude deleter column
76
76
  # add_whodunit_stamps :posts, include_deleter: false
77
- def add_whodunit_stamps(table_name, include_deleter: :auto, creator_type: nil, updater_type: nil, deleter_type: nil)
77
+ def add_whodunit_stamps(table_name, include_deleter: :auto, creator_type: nil, updater_type: nil, deleter_type: nil,
78
+ auto_create_user_fk_constraints: nil)
79
+ if Whodunit.soft_delete_enabled? && !column_exists?(table_name, Whodunit.soft_delete_column)
80
+ add_column table_name, Whodunit.soft_delete_column, :datetime, null: true
81
+ end
82
+
78
83
  if Whodunit.creator_enabled?
79
84
  add_column table_name, Whodunit.creator_column, creator_type || Whodunit.creator_data_type, null: true
80
85
  end
@@ -88,6 +93,7 @@ module Whodunit
88
93
  end
89
94
 
90
95
  add_whodunit_indexes(table_name, include_deleter)
96
+ add_whodunit_foreign_keys(table_name, include_deleter, auto_create_user_fk_constraints)
91
97
  end
92
98
 
93
99
  # Remove stamp columns from an existing table.
@@ -107,7 +113,9 @@ module Whodunit
107
113
  # remove_whodunit_stamps :posts, include_deleter: true
108
114
  # @example Exclude deleter removal
109
115
  # remove_whodunit_stamps :posts, include_deleter: false
110
- def remove_whodunit_stamps(table_name, include_deleter: :auto, **_options)
116
+ def remove_whodunit_stamps(table_name, include_deleter: :auto, auto_create_user_fk_constraints: nil, **_options)
117
+ remove_whodunit_foreign_keys(table_name, include_deleter, auto_create_user_fk_constraints)
118
+
111
119
  if Whodunit.creator_enabled? && column_exists?(table_name, Whodunit.creator_column)
112
120
  remove_column table_name, Whodunit.creator_column
113
121
  end
@@ -120,6 +128,10 @@ module Whodunit
120
128
  column_exists?(table_name, Whodunit.deleter_column)
121
129
  remove_column table_name, Whodunit.deleter_column
122
130
  end
131
+
132
+ return unless Whodunit.soft_delete_enabled? && column_exists?(table_name, Whodunit.soft_delete_column)
133
+
134
+ remove_column table_name, Whodunit.soft_delete_column
123
135
  end
124
136
 
125
137
  # Add stamp columns to a table definition or existing table.
@@ -129,8 +141,7 @@ module Whodunit
129
141
  # 2. As a standalone method in migrations (attempts to infer table name)
130
142
  #
131
143
  # Only adds columns that are enabled in configuration. For new tables,
132
- # deleter column is only added when explicitly requested (include_deleter: true)
133
- # to be conservative.
144
+ # include_deleter: :auto follows the configured soft-delete column.
134
145
  #
135
146
  # @param table_def [ActiveRecord::ConnectionAdapters::TableDefinition, nil]
136
147
  # the table definition (when used in create_table block) or nil
@@ -154,40 +165,52 @@ module Whodunit
154
165
  # t.whodunit_stamps include_deleter: true
155
166
  # end
156
167
  def whodunit_stamps(table_def = nil, include_deleter: :auto, creator_type: nil, updater_type: nil,
157
- deleter_type: nil)
168
+ deleter_type: nil, auto_create_user_fk_constraints: nil)
158
169
  if table_def.nil?
159
- handle_migration_stamps(include_deleter, creator_type, updater_type, deleter_type)
170
+ handle_migration_stamps(include_deleter, creator_type, updater_type, deleter_type,
171
+ auto_create_user_fk_constraints)
160
172
  else
161
- handle_table_definition_stamps(table_def, include_deleter, creator_type, updater_type, deleter_type)
173
+ handle_table_definition_stamps(table_def, include_deleter, creator_type, updater_type, deleter_type,
174
+ auto_create_user_fk_constraints)
162
175
  end
163
176
  end
164
177
 
165
178
  private
166
179
 
167
180
  # Handle stamps when called as migration method
168
- def handle_migration_stamps(include_deleter, creator_type, updater_type, deleter_type)
181
+ def handle_migration_stamps(include_deleter, creator_type, updater_type, deleter_type,
182
+ auto_create_user_fk_constraints)
169
183
  table_name = infer_table_name_from_migration
170
184
  return unless table_name
171
185
 
172
186
  add_whodunit_stamps(table_name, include_deleter: include_deleter, creator_type: creator_type,
173
- updater_type: updater_type, deleter_type: deleter_type)
187
+ updater_type: updater_type, deleter_type: deleter_type,
188
+ auto_create_user_fk_constraints: auto_create_user_fk_constraints)
174
189
  end
175
190
 
176
191
  # Handle stamps when called within create_table block
177
- def handle_table_definition_stamps(table_def, include_deleter, creator_type, updater_type, deleter_type)
178
- if Whodunit.creator_enabled?
179
- table_def.column Whodunit.creator_column, creator_type || Whodunit.creator_data_type, null: true
180
- end
192
+ def handle_table_definition_stamps(table_def, include_deleter, creator_type, updater_type, deleter_type,
193
+ auto_create_user_fk_constraints)
194
+ add_whodunit_columns_to_table_definition(table_def, include_deleter, creator_type, updater_type, deleter_type)
195
+ add_whodunit_indexes_for_create_table(table_def, include_deleter)
196
+ add_whodunit_foreign_keys_for_create_table(table_def, include_deleter, auto_create_user_fk_constraints)
197
+ end
181
198
 
182
- if Whodunit.updater_enabled?
183
- table_def.column Whodunit.updater_column, updater_type || Whodunit.updater_data_type, null: true
184
- end
199
+ def add_whodunit_columns_to_table_definition(table_def, include_deleter, creator_type, updater_type, deleter_type)
200
+ add_table_definition_column(table_def, Whodunit.creator_column, creator_type || Whodunit.creator_data_type,
201
+ Whodunit.creator_enabled?)
202
+ add_table_definition_column(table_def, Whodunit.updater_column, updater_type || Whodunit.updater_data_type,
203
+ Whodunit.updater_enabled?)
204
+ add_table_definition_column(table_def, Whodunit.soft_delete_column, :datetime,
205
+ Whodunit.soft_delete_enabled?)
206
+ add_table_definition_column(table_def, Whodunit.deleter_column, deleter_type || Whodunit.deleter_data_type,
207
+ should_include_deleter_for_new_table?(include_deleter))
208
+ end
185
209
 
186
- if should_include_deleter_for_new_table?(include_deleter)
187
- table_def.column Whodunit.deleter_column, deleter_type || Whodunit.deleter_data_type, null: true
188
- end
210
+ def add_table_definition_column(table_def, column, type, enabled)
211
+ return unless enabled && !stamp_column_defined?(table_def, column)
189
212
 
190
- add_whodunit_indexes_for_create_table(table_def, include_deleter)
213
+ table_def.column column, type, null: true
191
214
  end
192
215
 
193
216
  # Determine if deleter column should be included based on configuration.
@@ -198,12 +221,11 @@ module Whodunit
198
221
  include_deleter == :auto ? soft_delete_enabled? : include_deleter.eql?(true)
199
222
  end
200
223
 
201
- # For new tables, be more conservative - only include deleter when explicitly requested.
202
- # This prevents adding deleter columns to tables that may not need them.
224
+ # Determine whether a new table should include the deleter column.
203
225
  # @param include_deleter [Symbol, Boolean] the inclusion preference
204
- # @return [Boolean] true only when explicitly requested (true)
226
+ # @return [Boolean] true when explicitly requested or soft-delete is configured
205
227
  def should_include_deleter_for_new_table?(include_deleter)
206
- include_deleter.eql?(true)
228
+ should_include_deleter?(include_deleter)
207
229
  end
208
230
 
209
231
  # Check if soft-delete is enabled based on configuration.
@@ -241,6 +263,60 @@ module Whodunit
241
263
  table_def.index Whodunit.deleter_column, name: "index_#{table_name}_on_deleter"
242
264
  end
243
265
 
266
+ def add_whodunit_foreign_keys(table_name, include_deleter, override)
267
+ return unless create_user_fk_constraints?(override)
268
+
269
+ add_foreign_key table_name, Whodunit.user_table_name, column: Whodunit.creator_column if Whodunit.creator_enabled?
270
+ add_foreign_key table_name, Whodunit.user_table_name, column: Whodunit.updater_column if Whodunit.updater_enabled?
271
+ return unless should_include_deleter?(include_deleter)
272
+
273
+ add_foreign_key table_name, Whodunit.user_table_name, column: Whodunit.deleter_column
274
+ end
275
+
276
+ def add_whodunit_foreign_keys_for_create_table(table_def, include_deleter, override)
277
+ return unless create_user_fk_constraints?(override)
278
+
279
+ add_table_definition_foreign_key(table_def, Whodunit.creator_column) if Whodunit.creator_enabled?
280
+ add_table_definition_foreign_key(table_def, Whodunit.updater_column) if Whodunit.updater_enabled?
281
+ return unless should_include_deleter_for_new_table?(include_deleter)
282
+
283
+ add_table_definition_foreign_key(table_def,
284
+ Whodunit.deleter_column)
285
+ end
286
+
287
+ def stamp_column_defined?(table_def, column_name)
288
+ table_def.columns.any? { |column| column.name.to_s == column_name.to_s }
289
+ end
290
+
291
+ def add_table_definition_foreign_key(table_def, column_name)
292
+ return if table_def.foreign_keys.any? do |foreign_key|
293
+ foreign_key.to_table.to_s == Whodunit.user_table_name.to_s &&
294
+ foreign_key.options[:column].to_s == column_name.to_s
295
+ end
296
+
297
+ table_def.foreign_key Whodunit.user_table_name, column: column_name
298
+ end
299
+
300
+ def create_user_fk_constraints?(override)
301
+ override.nil? ? Whodunit.auto_create_user_fk_constraints : override
302
+ end
303
+
304
+ def remove_whodunit_foreign_keys(table_name, include_deleter, override)
305
+ return unless create_user_fk_constraints?(override)
306
+
307
+ if Whodunit.creator_enabled?
308
+ remove_foreign_key table_name, Whodunit.user_table_name,
309
+ column: Whodunit.creator_column
310
+ end
311
+ if Whodunit.updater_enabled?
312
+ remove_foreign_key table_name, Whodunit.user_table_name,
313
+ column: Whodunit.updater_column
314
+ end
315
+ return unless should_include_deleter?(include_deleter)
316
+
317
+ remove_foreign_key table_name, Whodunit.user_table_name, column: Whodunit.deleter_column
318
+ end
319
+
244
320
  # Attempt to infer table name from migration class name
245
321
  def infer_table_name_from_migration
246
322
  return nil unless respond_to?(:migration_name)
@@ -249,10 +325,10 @@ module Whodunit
249
325
  case migration_name
250
326
  when /^Create(\w+)$/
251
327
  table_name = ::Regexp.last_match(1)
252
- table_name.respond_to?(:underscore) ? table_name.underscore.pluralize : "#{table_name.downcase}s"
328
+ table_name.underscore.pluralize
253
329
  when /^Add\w*To(\w+)$/
254
330
  table_name = ::Regexp.last_match(1)
255
- table_name.respond_to?(:underscore) ? table_name.underscore : table_name.downcase
331
+ table_name.underscore
256
332
  end
257
333
  end
258
334
 
@@ -263,5 +339,13 @@ module Whodunit
263
339
  end
264
340
  end
265
341
 
266
- # Extend ActiveRecord::Migration when available
267
- ActiveRecord::Migration.include(Whodunit::MigrationHelpers) if defined?(ActiveRecord::Migration)
342
+ # Integrate immediately when Active Record is already loaded, or defer until it is.
343
+ # :nocov:
344
+ if defined?(ActiveRecord::Migration)
345
+ ActiveRecord::Migration.include(Whodunit::MigrationHelpers)
346
+ elsif defined?(ActiveSupport)
347
+ ActiveSupport.on_load(:active_record) do
348
+ ActiveRecord::Migration.include(Whodunit::MigrationHelpers)
349
+ end
350
+ end
351
+ # :nocov:
@@ -186,7 +186,7 @@ module Whodunit
186
186
  # @return [void]
187
187
  # @api private
188
188
  def setup_whodunit_associations
189
- return if abstract_class?
189
+ return if abstract_class? || !table_exists?
190
190
 
191
191
  setup_creator_association if creator_column_exists? && model_creator_enabled?
192
192
  setup_updater_association if updater_column_exists? && model_updater_enabled?
@@ -42,7 +42,7 @@ module Whodunit
42
42
  skip = options.delete(:skip_whodunit_stamps)
43
43
  result = super
44
44
 
45
- if Whodunit.auto_inject_whodunit_stamps && !_whodunit_stamps_added && !skip
45
+ if Whodunit.auto_inject_whodunit_stamps && !_whodunit_stamps_added && !skip && !whodunit_internal_table?
46
46
  whodunit_stamps(include_deleter: :auto)
47
47
  end
48
48
 
@@ -52,9 +52,17 @@ module Whodunit
52
52
  # assign true tp the tracker `@_whodunit_stamps_added` before the
53
53
  # reuse/call of the `whodunit_stamps` flow from the Whodunit::MigrationHelpers module (see railtie.rb)
54
54
  #
55
- def whodunit_stamps(include_deleter: :auto, creator_type: nil, updater_type: nil, deleter_type: nil)
55
+ def whodunit_stamps(include_deleter: :auto, creator_type: nil, updater_type: nil, deleter_type: nil,
56
+ auto_create_user_fk_constraints: nil)
56
57
  self._whodunit_stamps_added = true
57
- self.class.whodunit_stamps(self, include_deleter:, creator_type:, updater_type:, deleter_type:)
58
+ self.class.whodunit_stamps(self, include_deleter:, creator_type:, updater_type:, deleter_type:,
59
+ auto_create_user_fk_constraints:)
60
+ end
61
+
62
+ private
63
+
64
+ def whodunit_internal_table?
65
+ %w[ar_internal_metadata schema_migrations].include?(name.to_s)
58
66
  end
59
67
  end
60
68
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Whodunit
4
- VERSION = "0.4.1"
4
+ VERSION = "0.5.0"
5
5
  end
data/lib/whodunit.rb CHANGED
@@ -43,6 +43,11 @@ module Whodunit
43
43
  # @return [String] the user class name
44
44
  mattr_accessor :user_class, default: "User"
45
45
 
46
+ # Explicit user table name override. When nil, the table name is inferred
47
+ # from the configured user class.
48
+ # @return [String, Symbol, nil] the user table name override
49
+ mattr_accessor :user_class_table_name, default: nil
50
+
46
51
  # The column name for tracking who created the record (default: :creator_id)
47
52
  # @return [Symbol] the creator column name
48
53
  mattr_accessor :creator_column, default: :creator_id
@@ -65,6 +70,10 @@ module Whodunit
65
70
  # @return [Boolean] auto-injection setting
66
71
  mattr_accessor :auto_inject_whodunit_stamps, default: true
67
72
 
73
+ # Whether migration helpers should create foreign keys to the user table.
74
+ # @return [Boolean] automatic user foreign-key constraint setting
75
+ mattr_accessor :auto_create_user_fk_constraints, default: false
76
+
68
77
  # @!group Data Type Configuration
69
78
 
70
79
  # The default data type for stamp columns (default: :bigint)
@@ -132,6 +141,21 @@ module Whodunit
132
141
  user_class.to_s
133
142
  end
134
143
 
144
+ # Resolve the configured user table name without loading the model. Migration
145
+ # execution can happen before the user table exists, so constantizing the
146
+ # user class here may cause Active Record to query a missing relation.
147
+ # Explicit configuration takes precedence; class objects may still provide
148
+ # their custom Active Record table name directly.
149
+ #
150
+ # @return [String] the user table name
151
+ def self.user_table_name
152
+ return user_class_table_name.to_s if user_class_table_name
153
+
154
+ return user_class.table_name.to_s if user_class.respond_to?(:table_name)
155
+
156
+ user_class_name.underscore.pluralize
157
+ end
158
+
135
159
  # @!group Data Type Helpers
136
160
 
137
161
  # Get the data type for the creator column
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: whodunit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ken C. Demanawa
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-05-31 00:00:00.000000000 Z
11
+ date: 2026-08-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord