Module: Magick::Rails::Events

Defined in:
lib/magick/rails/events.rb

Constant Summary collapse

EVENT_PREFIX =

Event names (using Rails 8.1 structured event format)

'magick.feature_flag'
EVENTS =
{
  changed: "#{EVENT_PREFIX}.changed",
  enabled: "#{EVENT_PREFIX}.enabled",
  disabled: "#{EVENT_PREFIX}.disabled",
  enabled_globally: "#{EVENT_PREFIX}.enabled_globally",
  disabled_globally: "#{EVENT_PREFIX}.disabled_globally",
  dependency_added: "#{EVENT_PREFIX}.dependency_added",
  dependency_removed: "#{EVENT_PREFIX}.dependency_removed",
  variant_set: "#{EVENT_PREFIX}.variant_set",
  variant_selected: "#{EVENT_PREFIX}.variant_selected",
  targeting_added: "#{EVENT_PREFIX}.targeting_added",
  targeting_removed: "#{EVENT_PREFIX}.targeting_removed",
  version_saved: "#{EVENT_PREFIX}.version_saved",
  rollback: "#{EVENT_PREFIX}.rollback",
  exported: "#{EVENT_PREFIX}.exported",
  imported: "#{EVENT_PREFIX}.imported",
  audit_logged: "#{EVENT_PREFIX}.audit_logged",
  usage_tracked: "#{EVENT_PREFIX}.usage_tracked",
  deprecated_warning: "#{EVENT_PREFIX}.deprecated_warning",
  adapter_write_failed: "#{EVENT_PREFIX}.adapter_write_failed"
}.freeze

Class Method Summary collapse

Class Method Details

.adapter_write_failed(feature_name, backend:, operation:, error_class: nil, error_message: nil, reason: nil, **metadata) ⇒ Object

A write to a shared backend (Redis / ActiveRecord) failed or was dropped. Memory has already been updated and is never rolled back, so this process is now serving a value the rest of the fleet does not have. Subscribe to alert on backend divergence.



236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/magick/rails/events.rb', line 236

def self.adapter_write_failed(feature_name, backend:, operation:, error_class: nil, error_message: nil,
                              reason: nil, **)
  notify(:adapter_write_failed, {
           feature_name: feature_name.to_s,
           backend: backend.to_s,
           operation: operation.to_s,
           error_class: error_class,
           error_message: error_message,
           reason: reason,
           timestamp: Time.now.iso8601,
           **
         })
end

.audit_logged(feature_name, action:, user_id: nil, changes: {}, **metadata) ⇒ Object

Audit log entry created



209
210
211
212
213
214
215
216
217
218
# File 'lib/magick/rails/events.rb', line 209

def self.audit_logged(feature_name, action:, user_id: nil, changes: {}, **)
  notify(:audit_logged, {
           feature_name: feature_name.to_s,
           action: action.to_s,
           user_id: user_id,
           changes: changes,
           timestamp: Time.now.iso8601,
           **
         })
end

.dependency_added(feature_name, dependency_name, **metadata) ⇒ Object

Dependency added



105
106
107
108
109
110
111
112
# File 'lib/magick/rails/events.rb', line 105

def self.dependency_added(feature_name, dependency_name, **)
  notify(:dependency_added, {
           feature_name: feature_name.to_s,
           dependency_name: dependency_name.to_s,
           timestamp: Time.now.iso8601,
           **
         })
end

.dependency_removed(feature_name, dependency_name, **metadata) ⇒ Object

Dependency removed



115
116
117
118
119
120
121
122
# File 'lib/magick/rails/events.rb', line 115

def self.dependency_removed(feature_name, dependency_name, **)
  notify(:dependency_removed, {
           feature_name: feature_name.to_s,
           dependency_name: dependency_name.to_s,
           timestamp: Time.now.iso8601,
           **
         })
end

.deprecated_warning(feature_name, **metadata) ⇒ Object

Deprecation warning



251
252
253
254
255
256
257
# File 'lib/magick/rails/events.rb', line 251

def self.deprecated_warning(feature_name, **)
  notify(:deprecated_warning, {
           feature_name: feature_name.to_s,
           timestamp: Time.now.iso8601,
           **
         })
end

.exported(format:, feature_count:, **metadata) ⇒ Object

Export performed



189
190
191
192
193
194
195
196
# File 'lib/magick/rails/events.rb', line 189

def self.exported(format:, feature_count:, **)
  notify(:exported, {
           format: format.to_s,
           feature_count: feature_count,
           timestamp: Time.now.iso8601,
           **
         })
end

.feature_changed(feature_name, changes:, user_id: nil, **metadata) ⇒ Object

Feature flag changed (value, status, etc.)



54
55
56
57
58
59
60
61
62
# File 'lib/magick/rails/events.rb', line 54

def self.feature_changed(feature_name, changes:, user_id: nil, **)
  notify(:changed, {
           feature_name: feature_name.to_s,
           changes: changes,
           user_id: user_id,
           timestamp: Time.now.iso8601,
           **
         })
end

.feature_disabled(feature_name, context: {}, **metadata) ⇒ Object

Feature disabled



75
76
77
78
79
80
81
82
# File 'lib/magick/rails/events.rb', line 75

def self.feature_disabled(feature_name, context: {}, **)
  notify(:disabled, {
           feature_name: feature_name.to_s,
           context: context,
           timestamp: Time.now.iso8601,
           **
         })
end

.feature_disabled_globally(feature_name, user_id: nil, **metadata) ⇒ Object

Feature disabled globally (no targeting)



95
96
97
98
99
100
101
102
# File 'lib/magick/rails/events.rb', line 95

def self.feature_disabled_globally(feature_name, user_id: nil, **)
  notify(:disabled_globally, {
           feature_name: feature_name.to_s,
           user_id: user_id,
           timestamp: Time.now.iso8601,
           **
         })
end

.feature_enabled(feature_name, context: {}, **metadata) ⇒ Object

Feature enabled



65
66
67
68
69
70
71
72
# File 'lib/magick/rails/events.rb', line 65

def self.feature_enabled(feature_name, context: {}, **)
  notify(:enabled, {
           feature_name: feature_name.to_s,
           context: context,
           timestamp: Time.now.iso8601,
           **
         })
end

.feature_enabled_globally(feature_name, user_id: nil, **metadata) ⇒ Object

Feature enabled globally (no targeting)



85
86
87
88
89
90
91
92
# File 'lib/magick/rails/events.rb', line 85

def self.feature_enabled_globally(feature_name, user_id: nil, **)
  notify(:enabled_globally, {
           feature_name: feature_name.to_s,
           user_id: user_id,
           timestamp: Time.now.iso8601,
           **
         })
end

.imported(format:, feature_count:, **metadata) ⇒ Object

Import performed



199
200
201
202
203
204
205
206
# File 'lib/magick/rails/events.rb', line 199

def self.imported(format:, feature_count:, **)
  notify(:imported, {
           format: format.to_s,
           feature_count: feature_count,
           timestamp: Time.now.iso8601,
           **
         })
end

.notify(event_name, payload = {}) ⇒ Object



41
42
43
44
45
46
# File 'lib/magick/rails/events.rb', line 41

def self.notify(event_name, payload = {})
  return unless rails81?

  event_name_str = EVENTS[event_name] || event_name.to_s
  ::Rails.event.notify(event_name_str, payload)
end

.rails81? ⇒ Boolean

Check if Rails 8.1+ structured events are available.

::Rails is spelled out on purpose: this module is nested inside Magick::Rails, so a bare Rails resolves lexically to the enclosing Magick::Rails module and never to the framework — which silently made every event in this file a no-op inside a real Rails app.

Returns:

  • (Boolean)


12
13
14
# File 'lib/magick/rails/events.rb', line 12

def self.rails81?
  defined?(::Rails) && ::Rails.respond_to?(:event) && ::Rails.event.respond_to?(:notify)
end

.rails8? ⇒ Boolean

Backward compatibility alias

Returns:

  • (Boolean)


49
50
51
# File 'lib/magick/rails/events.rb', line 49

def self.rails8?
  rails81?
end

.rollback(feature_name, version:, **metadata) ⇒ Object

Rollback performed



179
180
181
182
183
184
185
186
# File 'lib/magick/rails/events.rb', line 179

def self.rollback(feature_name, version:, **)
  notify(:rollback, {
           feature_name: feature_name.to_s,
           version: version,
           timestamp: Time.now.iso8601,
           **
         })
end

.targeting_added(feature_name, targeting_type:, targeting_value:, **metadata) ⇒ Object

Targeting added



146
147
148
149
150
151
152
153
154
# File 'lib/magick/rails/events.rb', line 146

def self.targeting_added(feature_name, targeting_type:, targeting_value:, **)
  notify(:targeting_added, {
           feature_name: feature_name.to_s,
           targeting_type: targeting_type.to_s,
           targeting_value: targeting_value,
           timestamp: Time.now.iso8601,
           **
         })
end

.targeting_removed(feature_name, targeting_type:, targeting_value: nil, **metadata) ⇒ Object

Targeting removed



157
158
159
160
161
162
163
164
165
# File 'lib/magick/rails/events.rb', line 157

def self.targeting_removed(feature_name, targeting_type:, targeting_value: nil, **)
  notify(:targeting_removed, {
           feature_name: feature_name.to_s,
           targeting_type: targeting_type.to_s,
           targeting_value: targeting_value,
           timestamp: Time.now.iso8601,
           **
         })
end

.usage_tracked(feature_name, operation:, duration:, success: true, **metadata) ⇒ Object

Usage tracked



221
222
223
224
225
226
227
228
229
230
# File 'lib/magick/rails/events.rb', line 221

def self.usage_tracked(feature_name, operation:, duration:, success: true, **)
  notify(:usage_tracked, {
           feature_name: feature_name.to_s,
           operation: operation.to_s,
           duration: duration,
           success: success,
           timestamp: Time.now.iso8601,
           **
         })
end

.variant_selected(feature_name, variant_name:, context: {}, **metadata) ⇒ Object

Variant selected



135
136
137
138
139
140
141
142
143
# File 'lib/magick/rails/events.rb', line 135

def self.variant_selected(feature_name, variant_name:, context: {}, **)
  notify(:variant_selected, {
           feature_name: feature_name.to_s,
           variant_name: variant_name.to_s,
           context: context,
           timestamp: Time.now.iso8601,
           **
         })
end

.variant_set(feature_name, variants:, **metadata) ⇒ Object

Variants set



125
126
127
128
129
130
131
132
# File 'lib/magick/rails/events.rb', line 125

def self.variant_set(feature_name, variants:, **)
  notify(:variant_set, {
           feature_name: feature_name.to_s,
           variants: variants.is_a?(Array) ? variants.map(&:to_h) : variants,
           timestamp: Time.now.iso8601,
           **
         })
end

.version_saved(feature_name, version:, created_by: nil, **metadata) ⇒ Object

Version saved



168
169
170
171
172
173
174
175
176
# File 'lib/magick/rails/events.rb', line 168

def self.version_saved(feature_name, version:, created_by: nil, **)
  notify(:version_saved, {
           feature_name: feature_name.to_s,
           version: version,
           created_by: created_by,
           timestamp: Time.now.iso8601,
           **
         })
end