model_timeline 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7406b06646ef46c0b8d2541835ebeaa5e0f71638c600d0b7de45903bb36b2c81
4
+ data.tar.gz: dad655ebda19102a5e09eb3a51d07ac15cfbca051527ee3c3a71d979ab2a4543
5
+ SHA512:
6
+ metadata.gz: 4f618bea0cc20328d5d2a61352c753ec39fbe04f90b7f909ba93df236ab28a4e239515c3b0b70913381149e99996c189ebb41afe5e2a2d6649e3fdb0fabf24b4
7
+ data.tar.gz: d145d2798077f4bbb13191f966d8b73433330e5360b808862f14ddc3f1e4d7e6d97d231ad9e0828a5f63f4f759453550eb5cb132781a9a5dfa15b21b816ec36b
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Alexandre Stapenhorst
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,453 @@
1
+ # ModelTimeline
2
+
3
+ ModelTimeline is a flexible audit logging gem for Rails applications that allows you to track changes to your models with comprehensive attribution and flexible configuration options.
4
+
5
+ ## How this gem is different than paper_trail and audited?
6
+
7
+ ModelTimeline was designed with several unique features that differentiate it from other auditing gems:
8
+
9
+ - *Multiple configurations per model:* Unlike paper_trail and audited, ModelTimeline allows you to define multiple timeline configurations on the same model. This means you can track different sets of attributes for different purposes.
10
+ - *Targeted tracking:* Configure separate timelines for different aspects of your model (e.g., one for security events, another for content changes).
11
+ - PostgreSQL optimization: Built to leverage PostgreSQL's JSONB capabilities for efficient storage and advanced querying.
12
+ - *IP address tracking:* Automatically captures the client IP address for each change.
13
+ - *Rich metadata support:* Add custom metadata to timeline entries via configuration or at runtime.
14
+ - *Flexible user attribution:* Works with any authentication system by using a configurable method to retrieve the current user.
15
+ - *Comprehensive RSpec support:* Built-in matchers for testing timeline recording.
16
+
17
+
18
+ ## Installation
19
+
20
+ Add this line to your application's Gemfile:
21
+
22
+ ```sh
23
+ gem 'model_timeline'
24
+ ```
25
+
26
+ And then execute:
27
+
28
+ ```sh
29
+ $ bundle install
30
+ ```
31
+
32
+ Or install it yourself as:
33
+
34
+ ```sh
35
+ $ gem install model_timeline
36
+ ```
37
+
38
+ Run the generator to create the necessary migration:
39
+
40
+ ```sh
41
+ $ rails generate model_timeline:install
42
+ $ rails db:migrate
43
+ ```
44
+
45
+ For a custom table name:
46
+
47
+ ```sh
48
+ $ rails generate model_timeline:install --table_name=custom_timeline_entries
49
+ $ rails db:migrate
50
+ ```
51
+ ## Configuration
52
+
53
+ ### Initializer
54
+
55
+ Configure the gem in an initializer:
56
+
57
+ ```ruby
58
+ # config/initializers/model_timeline.rb
59
+ ModelTimeline.configure do |config|
60
+ # Method to retrieve the current user in controllers (default: :current_user)
61
+ config.current_user_method = :current_user
62
+
63
+ # Method to retrieve the client IP address in controllers (default: :remote_ip)
64
+ config.current_ip_method = :remote_ip
65
+
66
+ # Enable/disable timeline tracking globally
67
+ # config.enabled = true # Enabled by default
68
+ end
69
+ ```
70
+
71
+ ### Model Configuration
72
+ Include the Timelineable module in your models (this happens automatically with Rails):
73
+
74
+ > **Important**: When defining multiple timelines on the same model, each must use a unique `class_name` option. Otherwise, the associations will conflict and an error will be raised.
75
+
76
+ ```ruby
77
+ # Basic usage with default settings
78
+ class User < ApplicationRecord
79
+ has_timeline
80
+ end
81
+
82
+ # Using a custom association name with class_name along default
83
+ class User < ApplicationRecord
84
+ has_timeline
85
+
86
+ has_timeline :security_events, class_name: 'SecurityTimelineEntry'
87
+ end
88
+
89
+ # Tracking only specific attributes
90
+ class User < ApplicationRecord
91
+ has_timeline only: [:last_login_at, :login_count, :status],
92
+ class_name: 'LoginTimelineEntry'
93
+ end
94
+
95
+ # Ignoring specific attributes
96
+ class User < ApplicationRecord
97
+ has_timeline :profile_changes,
98
+ ignore: [:password, :remember_token, :login_count],
99
+ class_name: 'ProfileTimelineEntry'
100
+ end
101
+
102
+ # Tracking only specific events
103
+ class User < ApplicationRecord
104
+ has_timeline :content_changes,
105
+ on: [:update, :destroy],
106
+ class_name: 'ContentTimelineEntry'
107
+ end
108
+
109
+ # Using a custom timeline entry class and table
110
+ class User < ApplicationRecord
111
+ has_timeline :custom_timeline_entries,
112
+ class_name: 'CustomTimelineEntry'
113
+ end
114
+
115
+ # Adding additional metadata to each entry
116
+ class Order < ApplicationRecord
117
+ has_timeline :admin_changes,
118
+ class_name: 'AdminTimelineEntry',
119
+ meta: {
120
+ app_version: "1.0",
121
+ # Dynamic values using methods or procs
122
+ section: :section_name,
123
+ category_id: ->(record) { record.category_id }
124
+ }
125
+ end
126
+ ```
127
+
128
+ ### Using Metadata
129
+
130
+ <!-- ! THIS IS WRONG, NEEDS TO BE UPDATED. -->
131
+ ModelTimeline allows you to include custom metadata with your timeline entries, which is especially useful for tracking changes across related entities or adding domain-specific context.
132
+
133
+ #### Adding Metadata Through Configuration
134
+
135
+ When defining a timeline, any fields you include in the `meta` option will be evaluated and stored in the timeline entry:
136
+
137
+ ```ruby
138
+ class Comment < ApplicationRecord
139
+ belongs_to :post
140
+
141
+ has_timeline :comment_changes,
142
+ class_name: 'ContentTimelineEntry',
143
+ meta: {
144
+ post_id: ->(record) { record.post_id },
145
+ }
146
+ end
147
+ ```
148
+
149
+ If your timeline table has columns that match the keys in your `meta` hash, these values will be stored in
150
+ those dedicated columns. Otherwise they will be ignored. (This might change in the future and a metadata column might be
151
+ added to put additional metadata that doesn't have a dedicated column.)
152
+
153
+ #### Adding Metadata at Runtime
154
+
155
+ ```ruby
156
+ # Add metadata for a specific operation
157
+ ModelTimeline.with_metadata(post_id: '123456') do
158
+ comment.update(body: 'Updated comment')
159
+ end
160
+
161
+ # Add metadata for the current thread/request
162
+ ModelTimeline.metadata = { post_id: '123456' }
163
+ comment.update(status: 'approved')
164
+ ```
165
+
166
+ #### Custom Timeline Tables with Domain-specific Columns
167
+
168
+ For tracking related entities more effectively, you can create a custom timeline table with additional columns:
169
+
170
+ ```ruby
171
+ # Migration to create a product-specific timeline table
172
+ class CreatePostTimelineEntries < ActiveRecord::Migration[6.1]
173
+ def change
174
+ create_table :post_timeline_entries do |t|
175
+ # Standard ModelTimeline columns
176
+ t.string :timelineable_type
177
+ t.integer :timelineable_id
178
+ t.string :action
179
+ t.jsonb :object_changes
180
+ t.integer :user_id
181
+ t.string :ip_address
182
+
183
+ # Custom columns that can be populated via the meta option
184
+ t.integer :post_id
185
+
186
+ t.timestamps
187
+ end
188
+
189
+ add_index :post_timeline_entries, [:timelineable_type, :timelineable_id]
190
+ add_index :post_timeline_entries, :post_id
191
+ add_index :post_timeline_entries, :user_id
192
+ end
193
+ end
194
+ ```
195
+
196
+ Then, use this table with your models:
197
+
198
+ ```ruby
199
+ class Comment < ApplicationRecord
200
+ belongs_to :post
201
+
202
+ has_timeline :product_changes,
203
+ class_name: 'PostTimelineEntry',
204
+ meta: {
205
+ post_id: ->(record) { record.post_id },
206
+ # OR
207
+ # post_id: :post_id
208
+ # OR
209
+ # post_id: :my_custom_post_id_method
210
+ }
211
+ end
212
+ ```
213
+
214
+ With this approach, you can easily query all changes related to a specific post or product:
215
+
216
+ ```ruby
217
+ # Find all timeline entries for a specific post
218
+ PostTimelineEntry.where(post_id: post.id)
219
+ ```
220
+
221
+ This makes it significantly easier to track and analyze changes across related models within a specific domain context.
222
+
223
+ ### Controller Integration
224
+
225
+ Define the current user and ip_address for the current request
226
+
227
+ ```ruby
228
+ class ApplicationController < ActionController::Base
229
+ private
230
+
231
+ # ModelTimeline will look for the methods set in the initializer.
232
+ # Given
233
+ # ModelTimeline.configure do |config|
234
+ # config.current_user_method = :my_current_user
235
+ # config.current_ip_method = :remote_ip
236
+ # end
237
+ #
238
+ def my_current_user
239
+ my_current_user_instance
240
+ end
241
+
242
+ def remote_ip
243
+ request.remote_ip
244
+ end
245
+ end
246
+ ```
247
+
248
+ ## Usage
249
+
250
+ ### Basic Usage
251
+
252
+ Once configured, ModelTimeline automatically tracks changes to your models:
253
+
254
+ ```ruby
255
+ user = User.create(username: 'johndoe', email: 'john@example.com')
256
+ # Creates a timeline entry with action: 'create'
257
+
258
+ user.update(email: 'new@example.com')
259
+ # Creates a timeline entry with action: 'update' and the changed attributes
260
+
261
+ user.destroy
262
+ # Creates a timeline entry with action: 'destroy'
263
+ ```
264
+
265
+ ### Accessing Timeline Entries
266
+
267
+
268
+ ```ruby
269
+ # Get all timeline entries for a model
270
+ user.timeline_entries
271
+
272
+ # Get timeline entries with a specific action
273
+ user.timeline_entries.where(action: 'update')
274
+
275
+ # Find entries for a specific user
276
+ ModelTimeline::TimelineEntry.for_user(admin)
277
+
278
+ # Find entries from a specific IP
279
+ ModelTimeline::TimelineEntry.for_ip_address('192.168.1.1')
280
+ ```
281
+
282
+ ### Custom Tables and Models
283
+
284
+ ```ruby
285
+ # Create a custom timeline entry class
286
+ class SecurityTimelineEntry < ModelTimeline::TimelineEntry
287
+ self.table_name = 'security_timeline_entries'
288
+
289
+ # Add custom scopes or methods
290
+ scope :critical, -> { where("object_changes::text ILIKE '%password%'") }
291
+ end
292
+
293
+ # Use it in your model
294
+ class User < ApplicationRecord
295
+ has_timeline :security_timelines,
296
+ class_name: 'SecurityTimelineEntry',
297
+ only: [:sign_in_count, :last_sign_in_at, :role]
298
+ end
299
+
300
+ # Access the custom timeline
301
+ user.security_timelines
302
+ ```
303
+
304
+ ### Controlling Timeline Recording
305
+
306
+ Temporarily enable or disable timeline recording:
307
+
308
+ ```ruby
309
+ # Disable timeline recording for a block of code
310
+ ModelTimeline.without_timeline do
311
+ # Changes made here won't be recorded
312
+ user.update(name: 'New Name')
313
+ post.destroy
314
+ end
315
+
316
+ # Set custom context for timeline entries
317
+ ModelTimeline.with_timeline(current_user: admin_user, current_ip: '10.0.0.1', metadata: { reason: 'Admin action' }) do
318
+ # Changes made here will be attributed to admin_user from 10.0.0.1
319
+ # with the additional metadata
320
+ user.update(status: 'suspended')
321
+ end
322
+ ```
323
+
324
+ Add additional contextual information to timeline entries:
325
+
326
+ ```ruby
327
+ # Set metadata for all timeline entries in the current request
328
+ ModelTimeline.metadata = { import_batch: 'daily_sync_2023_01_01' }
329
+
330
+ # Temporarily add or override metadata for a block
331
+ ModelTimeline.with_metadata(source: 'api') do
332
+ # All timeline entries created here will include this metadata
333
+ user.update(status: 'active')
334
+ end
335
+ ```
336
+
337
+ ### Timeline Entry Scopes
338
+
339
+ ModelTimeline provides several useful scopes for querying timeline entries:
340
+
341
+ ```ruby
342
+ # Find entries for a specific model
343
+ ModelTimeline::TimelineEntry.for_timelineable(user)
344
+
345
+ # Find entries created by a specific user
346
+ ModelTimeline::TimelineEntry.for_user(admin)
347
+
348
+ # Find entries from a specific IP address
349
+ ModelTimeline::TimelineEntry.for_ip_address('192.168.1.1')
350
+
351
+ # Find entries where a specific attribute was changed
352
+ ModelTimeline::TimelineEntry.with_changed_attribute('email')
353
+
354
+ # Find entries where an attribute was changed to a specific value
355
+ ModelTimeline::TimelineEntry.with_changed_value('status', 'active')
356
+ ```
357
+
358
+ ### PostgreSQL-Specific Features
359
+
360
+ ModelTimeline leverages PostgreSQL's JSONB capabilities for efficient querying:
361
+
362
+ ```ruby
363
+ # Find timeline entries containing specific changes using JSONB containment
364
+ TimelineEntry.where("object_changes @> ?", {email: ["old@example.com", "new@example.com"]}.to_json)
365
+
366
+ # Search for any value in the changes
367
+ TimelineEntry.where("object_changes::text LIKE ?", "%specific_value%")
368
+ ```
369
+
370
+ The gem creates GIN indexes on the JSONB columns for optimized performance with large audit logs.
371
+
372
+
373
+ ## RSpec Integration
374
+
375
+ ### Configuration
376
+
377
+ Configure RSpec to work with ModelTimeline:
378
+
379
+ ```ruby
380
+ # spec/support/model_timeline.rb
381
+ require 'model_timeline/rspec'
382
+
383
+ RSpec.configure do |config|
384
+ # This disables ModelTimeline by default in tests for better performance
385
+ config.before(:suite) do
386
+ ModelTimeline.disable!
387
+ end
388
+
389
+ # Include the RSpec helpers and matchers
390
+ config.include ModelTimeline::RSpec
391
+ end
392
+ ```
393
+
394
+ #### Enabling Timeline in Tests
395
+
396
+ ModelTimeline is disabled by default in tests for performance. Enable it selectively:
397
+
398
+ ```ruby
399
+ # Enable timeline for a single test with metadata
400
+ it 'tracks changes', :with_timeline do
401
+ # ModelTimeline is enabled here
402
+ user = create(:user)
403
+ expect(user.timeline_entries).to exist
404
+ end
405
+
406
+ # Enable timeline for a group of tests
407
+ describe 'tracked actions', :with_timeline do
408
+ it 'tracks creation' do
409
+ post = create(:post)
410
+ expect(post.timeline_entries.count).to eq(1)
411
+ end
412
+
413
+ it 'tracks updates' do
414
+ post = create(:post)
415
+ post.update(title: 'New Title')
416
+ expect(post.timeline_entries.count).to eq(2)
417
+ end
418
+ end
419
+
420
+ # Tests without the metadata will have timeline disabled
421
+ it 'does not track changes' do
422
+ user = create(:user)
423
+ expect(ModelTimeline::TimelineEntry.count).to eq(0)
424
+ end
425
+ ```
426
+
427
+ #### RSpec Matchers
428
+
429
+ ModelTimeline provides several matchers for testing timeline entries:
430
+
431
+ ```ruby
432
+ # Check for any timeline entries
433
+ expect(user).to have_timeline_entries
434
+
435
+ # Check for a specific number of entries
436
+ expect(user).to have_timeline_entries(3)
437
+
438
+ # Check for entries with a specific action
439
+ expect(user).to have_timelined_action(:update)
440
+
441
+ # Check if a specific attribute was changed
442
+ expect(user).to have_timelined_change(:email)
443
+
444
+ # Check if an attribute was changed to a specific value
445
+ expect(user).to have_timelined_entry(:status, 'active')
446
+ ```
447
+
448
+ These matchers make it easy to test that your application is correctly tracking model changes.
449
+
450
+
451
+ ## License
452
+
453
+ The gem is available as open source under the terms of the MIT License.
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ModelTimeline
4
+ # Error raised when there's an issue with ModelTimeline configuration.
5
+ # This error is typically raised when attempting to define multiple timeline
6
+ # configurations for the same model and timeline entry class combination.
7
+ #
8
+ # @example Raising the error
9
+ # raise ModelTimeline::ConfigurationError.new
10
+ #
11
+ # @example With custom message
12
+ # raise ModelTimeline::ConfigurationError.new("Custom error message")
13
+ #
14
+ class ConfigurationError < StandardError
15
+ # Initialize a new ConfigurationError
16
+ #
17
+ # @param message [String] Custom error message
18
+ # @return [ModelTimeline::ConfigurationError] A new instance of ConfigurationError
19
+ def initialize(message = 'Multiple definitions of the same configuration found. ' \
20
+ 'Please ensure that each configuration is unique.')
21
+ super
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ModelTimeline
4
+ # Provides controller functionality for automatically tracking model timeline information.
5
+ # When included in a controller, this module will capture the current user and IP address
6
+ # for each request and make them available for timeline entries.
7
+ #
8
+ # @example Adding to a specific controller
9
+ # class ApplicationController < ActionController::Base
10
+ # include ModelTimeline::ControllerAdditions
11
+ # end
12
+ #
13
+ # @example Using the class method
14
+ # class ApplicationController < ActionController::Base
15
+ # track_actions_with_model_timeline
16
+ # end
17
+ #
18
+ module ControllerAdditions
19
+ extend ActiveSupport::Concern
20
+
21
+ included do
22
+ before_action :set_model_timeline_info
23
+ after_action :clear_model_timeline_info
24
+ end
25
+
26
+ # Sets the current user and IP address in the request store.
27
+ # Called automatically as a before_action.
28
+ #
29
+ # @return [void]
30
+ def set_model_timeline_info
31
+ user = (send(ModelTimeline.current_user_method) if respond_to?(ModelTimeline.current_user_method, true))
32
+
33
+ ip = begin
34
+ if request.respond_to?(ModelTimeline.current_ip_method)
35
+ request.send(ModelTimeline.current_ip_method)
36
+ else
37
+ request.remote_ip
38
+ end
39
+ rescue StandardError
40
+ nil
41
+ end
42
+
43
+ ModelTimeline.store_user_and_ip(user, ip)
44
+ end
45
+
46
+ # Clears the request store after the request is complete.
47
+ # Called automatically as an after_action.
48
+ #
49
+ # @return [void]
50
+ def clear_model_timeline_info
51
+ ModelTimeline.clear_request_store
52
+ end
53
+
54
+ # Class methods added to the including controller.
55
+ module ClassMethods
56
+ # Convenience method to include ModelTimeline::ControllerAdditions in a controller.
57
+ #
58
+ # @return [void]
59
+ def track_actions_with_model_timeline
60
+ include ModelTimeline::ControllerAdditions
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'rails/generators/active_record'
5
+
6
+ module ModelTimeline
7
+ # Contains generators for setting up ModelTimeline in a Rails application.
8
+ # These generators help with creating necessary database tables and configurations.
9
+ module Generators
10
+ # Rails generator that creates the necessary migration file for ModelTimeline.
11
+ # his generator creates a migration to set up the timeline entries table.
12
+ #
13
+ # @example
14
+ # $ rails generate model_timeline:install
15
+ #
16
+ # @example With custom table name
17
+ # $ rails generate model_timeline:install --table_name=custom_timeline_entries
18
+ class InstallGenerator < Rails::Generators::Base
19
+ include Rails::Generators::Migration
20
+
21
+ source_root File.expand_path('templates', __dir__)
22
+
23
+ # @option options [String] :table_name ('model_timeline_timeline_entries')
24
+ # The name to use for the timeline entries database table
25
+ class_option :table_name, type: :string, desc: 'Name for the timeline entries table'
26
+
27
+ # Returns the next migration number to be used in the migration filename
28
+ #
29
+ # @param [String] dirname The directory where migrations are stored
30
+ # @return [String] The next migration number
31
+ def self.next_migration_number(dirname)
32
+ ActiveRecord::Generators::Base.next_migration_number(dirname)
33
+ end
34
+
35
+ # Creates the migration file for ModelTimeline tables
36
+ #
37
+ # @return [void]
38
+ def create_migration_file
39
+ @table_name = options[:table_name] || 'model_timeline_timeline_entries'
40
+ migration_template 'migration.rb.tt', 'db/migrate/create_model_timeline_tables.rb'
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,27 @@
1
+ class CreateModelTimelineTables < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
+ def change
3
+ create_table :<%= @table_name %> do |t|
4
+ t.string :timelineable_type
5
+ t.bigint :timelineable_id
6
+ t.string :action, null: false
7
+
8
+ # Use PostgreSQL's JSONB type for better performance
9
+ t.jsonb :object_changes, default: {}, null: false
10
+
11
+ # Polymorphic user association
12
+ t.string :user_type
13
+ t.bigint :user_id
14
+ t.string :username
15
+
16
+ # IP address tracking
17
+ t.inet :ip_address
18
+
19
+ t.timestamps
20
+ end
21
+
22
+ add_index :<%= @table_name %>, [:timelineable_type, :timelineable_id], name: 'idx_timeline_on_timelineable'
23
+ add_index :<%= @table_name %>, [:user_type, :user_id], name: 'idx_timeline_on_user'
24
+ add_index :<%= @table_name %>, :object_changes, using: :gin, name: 'idx_timeline_on_changes'
25
+ add_index :<%= @table_name %>, :ip_address, name: 'idx_timeline_on_ip'
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ModelTimeline
4
+ # Rails integration for ModelTimeline.
5
+ # This Railtie automatically integrates ModelTimeline with Rails by:
6
+ # - Including the Timelineable module in all ActiveRecord models
7
+ # - Making controller helper methods available in all ActionControllers
8
+ #
9
+ # @example
10
+ # # This class is automatically loaded by Rails, no manual inclusion required
11
+ # # Rails.application.initialize!
12
+ #
13
+ class Railtie < Rails::Railtie
14
+ # @!method initializer(name, &block)
15
+ # Initializes ModelTimeline by including necessary modules in Rails components.
16
+ # Called automatically when Rails loads.
17
+ #
18
+ # @param name [String] The name of the initializer
19
+ # @param block [Proc] The initialization code to run
20
+ # @return [void]
21
+ initializer 'model_timeline.initialize' do
22
+ ActiveSupport.on_load(:active_record) do
23
+ include Timelineable
24
+ end
25
+
26
+ ActiveSupport.on_load(:action_controller) do
27
+ include ControllerAdditions::ClassMethods
28
+ end
29
+ end
30
+ end
31
+ end