Module: ModelTimeline

Defined in:
lib/model_timeline.rb,
lib/model_timeline/rspec.rb,
lib/model_timeline/railtie.rb,
lib/model_timeline/version.rb,
lib/model_timeline/timelineable.rb,
lib/model_timeline/rspec/matchers.rb,
lib/model_timeline/timeline_entry.rb,
lib/model_timeline/configuration_error.rb,
lib/model_timeline/controller_additions.rb,
lib/model_timeline/generators/install_generator.rb

Overview

A module for tracking and recording changes to ActiveRecord models.

ModelTimeline provides functionality to create and maintain a history of model changes, including user attribution, timestamps, and additional contextual metadata.

Examples:

Basic configuration

ModelTimeline.configure do |config|
  config.current_user_method = :current_admin
  config.current_ip_method = :visitor_ip
end

Temporarily disabling timeline tracking

ModelTimeline.without_timeline do
  # Changes made here won't be recorded
  user.update(name: 'New Name')
end

Using with custom user and metadata

ModelTimeline.with_timeline(current_user: admin, metadata: {reason: 'Admin action'}) do
  user.update(status: 'suspended')
end

Defined Under Namespace

Modules: ControllerAdditions, Generators, RSpec, Timelineable Classes: ConfigurationError, Railtie, TimelineEntry

Constant Summary collapse

VERSION =

Current version of the ModelTimeline gem. Follows semantic versioning (semver.org/).

Returns:

  • (String)

    The current version in the format “MAJOR.MINOR.PATCH”

'0.1.4'

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.current_ip_methodSymbol

Gets the method name used to retrieve the client IP address

Returns:

  • (Symbol)

    The method name, defaults to :remote_ip



67
68
69
# File 'lib/model_timeline.rb', line 67

def current_ip_method
  @current_ip_method || :remote_ip
end

.current_user_methodSymbol

Gets the method name used to retrieve the current user

Returns:

  • (Symbol)

    The method name, defaults to :current_user



60
61
62
# File 'lib/model_timeline.rb', line 60

def current_user_method
  @current_user_method || :current_user
end

.enabled=(value) ⇒ Object (writeonly)

Sets whether timeline recording is enabled

Parameters:

  • value (Boolean)

    true to enable, false to disable



47
48
49
# File 'lib/model_timeline.rb', line 47

def enabled=(value)
  @enabled = value
end

Class Method Details

.clear_metadata!Hash

Clears all metadata from thread-local storage

Returns:

  • (Hash)

    The empty metadata hash



193
194
195
# File 'lib/model_timeline.rb', line 193

def clear_metadata!
  Thread.current[:model_timeline_metadata] = {}
end

.clear_request_storeHash

Clears all data from the request store

Returns:

  • (Hash)

    The empty request store



158
159
160
# File 'lib/model_timeline.rb', line 158

def clear_request_store
  Thread.current[:model_timeline_request_store] = {}
end

.configure {|self| ... } ⇒ void

This method returns an undefined value.

Configures the ModelTimeline module

Yields:

  • (self)

    Yields the ModelTimeline module for configuration



53
54
55
# File 'lib/model_timeline.rb', line 53

def configure
  yield self if block_given?
end

.current_ipString?

Gets the current IP address from thread-local storage

Returns:

  • (String, nil)

    The current IP address or nil if not set



151
152
153
# File 'lib/model_timeline.rb', line 151

def current_ip
  request_store[:ip_address]
end

.current_userObject?

Gets the current user from thread-local storage

Returns:

  • (Object, nil)

    The current user or nil if not set



144
145
146
# File 'lib/model_timeline.rb', line 144

def current_user
  request_store[:current_user]
end

.disable!false

Disables timeline recording

Returns:

  • (false)

    Always returns false



88
89
90
# File 'lib/model_timeline.rb', line 88

def disable!
  self.enabled = false
end

.enable!true

Enables timeline recording

Returns:

  • (true)

    Always returns true



81
82
83
# File 'lib/model_timeline.rb', line 81

def enable!
  self.enabled = true
end

.enabled?Boolean

Checks if timeline recording is enabled

Returns:

  • (Boolean)

    true if enabled or not explicitly disabled, false otherwise



74
75
76
# File 'lib/model_timeline.rb', line 74

def enabled?
  @enabled.nil? || @enabled
end

.metadataHash

Gets the current metadata hash from thread-local storage

Returns:

  • (Hash)

    The metadata hash



165
166
167
# File 'lib/model_timeline.rb', line 165

def 
  Thread.current[:model_timeline_metadata] ||= {}
end

.metadata=(hash) ⇒ Hash

Sets the metadata hash in thread-local storage

Parameters:

  • hash (Hash)

    The metadata hash to store

Returns:

  • (Hash)

    The provided metadata hash



173
174
175
# File 'lib/model_timeline.rb', line 173

def metadata=(hash)
  Thread.current[:model_timeline_metadata] = hash
end

.request_storeHash

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.

Gets the thread-local storage hash for the current request

Returns:

  • (Hash)

    The request store hash



127
128
129
# File 'lib/model_timeline.rb', line 127

def request_store
  Thread.current[:model_timeline_request_store] ||= {}
end

.store_user_and_ip(user, ip_address) ⇒ void

This method returns an undefined value.

Stores the current user and IP address in thread-local storage

Parameters:

  • user (Object)

    The current user

  • ip_address (String)

    The current IP address



136
137
138
139
# File 'lib/model_timeline.rb', line 136

def store_user_and_ip(user, ip_address)
  request_store[:current_user] = user
  request_store[:ip_address] = ip_address
end

.with_metadata(hash) { ... } ⇒ Object

Temporarily merges additional metadata for the duration of the block

Parameters:

  • hash (Hash)

    The metadata to merge with the current metadata

Yields:

  • The block to execute with the merged metadata

Returns:

  • (Object)

    Returns the result of the block



182
183
184
185
186
187
188
# File 'lib/model_timeline.rb', line 182

def (hash)
   = .dup
  self. = .merge(hash)
  yield
ensure
  self. = 
end

.with_timeline(current_user: nil, current_ip: nil, metadata: {}) { ... } ⇒ Object

Temporarily sets custom user, IP, and metadata for timeline entries

Parameters:

  • current_user (Object, nil) (defaults to: nil)

    The user to associate with timeline entries

  • current_ip (String, nil) (defaults to: nil)

    The IP address to associate with timeline entries

  • metadata (Hash) (defaults to: {})

    Additional metadata to store with timeline entries

Yields:

  • The block to execute with the custom context

Returns:

  • (Object)

    Returns the result of the block



111
112
113
114
115
116
117
118
119
120
121
# File 'lib/model_timeline.rb', line 111

def with_timeline(current_user: nil, current_ip: nil, metadata: {}, &block)
  previous_user = ModelTimeline.current_user
  previous_ip = ModelTimeline.current_ip
   = ModelTimeline..dup

  ModelTimeline.store_user_and_ip(current_user, current_ip) if current_user || current_ip
  ModelTimeline.(, &block)
ensure
  ModelTimeline.store_user_and_ip(previous_user, previous_ip)
  ModelTimeline. = 
end

.without_timeline { ... } ⇒ Object

Temporarily disables timeline recording for the duration of the block

Yields:

  • The block to execute with timeline recording disabled

Returns:

  • (Object)

    Returns the result of the block



96
97
98
99
100
101
102
# File 'lib/model_timeline.rb', line 96

def without_timeline
  previous_state = enabled?
  disable!
  yield
ensure
  self.enabled = previous_state
end