Module: Magick
- Extended by:
- Rails::RequestStoreIntegration
- Defined in:
- lib/magick/rails/railtie.rb,
lib/magick.rb,
lib/magick/dsl.rb,
lib/magick/config.rb,
lib/magick/errors.rb,
lib/magick/feature.rb,
lib/magick/version.rb,
lib/magick/admin_ui.rb,
lib/magick/audit_log.rb,
lib/magick/versioning.rb,
lib/magick/rails/events.rb,
lib/magick/adapters/base.rb,
lib/magick/documentation.rb,
lib/magick/export_import.rb,
lib/magick/adapters/redis.rb,
lib/magick/targeting/base.rb,
lib/magick/targeting/role.rb,
lib/magick/targeting/user.rb,
lib/magick/adapters/memory.rb,
lib/magick/admin_ui/engine.rb,
lib/magick/circuit_breaker.rb,
lib/magick/feature_variant.rb,
lib/magick/targeting/group.rb,
lib/magick/testing_helpers.rb,
lib/magick/admin_ui/helpers.rb,
lib/magick/adapters/registry.rb,
lib/magick/targeting/complex.rb,
lib/magick/feature_dependency.rb,
lib/magick/performance_metrics.rb,
lib/magick/targeting/date_range.rb,
lib/magick/targeting/ip_address.rb,
lib/magick/targeting/percentage.rb,
lib/magick/adapters/active_record.rb,
lib/magick/rails/event_subscriber.rb,
lib/magick/targeting/custom_attribute.rb,
lib/magick/targeting/request_percentage.rb,
app/controllers/magick/adminui/stats_controller.rb,
lib/generators/magick/install/install_generator.rb,
app/controllers/magick/adminui/features_controller.rb,
lib/generators/magick/active_record/active_record_generator.rb
Overview
Admin UI engine is now loaded in magick.rb when Rails is detected
Defined Under Namespace
Modules: Adapters, AdminUI, ConfigDSL, DSL, Generators, Rails, RequestStoreIntegration, Targeting, TestingHelpers Classes: AdapterError, AuditLog, CircuitBreaker, Config, Documentation, Error, ExportImport, Feature, FeatureDependency, FeatureNotFoundError, FeatureVariant, InvalidFeatureTypeError, InvalidFeatureValueError, PerformanceMetrics, Versioning
Constant Summary collapse
- VERSION =
'1.3.2'
Class Attribute Summary collapse
-
.adapter_registry ⇒ Object
Returns the value of attribute adapter_registry.
-
.audit_log ⇒ Object
Returns the value of attribute audit_log.
-
.default_adapter ⇒ Object
Returns the value of attribute default_adapter.
-
.versioning ⇒ Object
Returns the value of attribute versioning.
-
.warn_on_deprecated ⇒ Object
Returns the value of attribute warn_on_deprecated.
Class Method Summary collapse
- .[](feature_name) ⇒ Object
- .bulk_disable(feature_names, _context = {}) ⇒ Object
- .bulk_enable(feature_names, _context = {}) ⇒ Object
- .configure(&block) ⇒ Object
-
.default_adapter_registry ⇒ Object
Get default adapter registry (public method for use by other classes).
- .disabled?(feature_name, context = {}) ⇒ Boolean
- .disabled_for?(feature_name, object, **additional_context) ⇒ Boolean
-
.enable_redis_tracking(enable: true) ⇒ Object
Manually enable Redis tracking for performance metrics Useful if Redis adapter becomes available after initial configuration.
- .enabled?(feature_name, context = {}) ⇒ Boolean
- .enabled_for?(feature_name, object, **additional_context) ⇒ Boolean
- .exists?(feature_name) ⇒ Boolean
- .export(format: :json) ⇒ Object
-
.feature_average_duration(feature_name, operation: nil) ⇒ Object
Get average duration for a feature (optionally filtered by operation).
-
.feature_stats(feature_name) ⇒ Object
Get total usage count for a feature (combines memory and Redis counts).
-
.feature_usage_count(feature_name) ⇒ Object
Get usage count for a feature.
- .features ⇒ Object
- .import(data, format: :json) ⇒ Object
-
.most_used_features(limit: 10) ⇒ Object
Get most used features.
-
.performance_metrics ⇒ Object
Getter for performance_metrics.
-
.performance_metrics=(value) ⇒ Object
Override performance_metrics setter to auto-enable Redis tracking.
-
.preload! ⇒ Object
Preload all features into memory cache in minimal queries.
- .register_feature(name, **options) ⇒ Object
-
.reload_feature(feature_name) ⇒ Object
Reload a feature from the adapter (useful when feature is changed externally).
- .reset! ⇒ Object
-
.shutdown!(timeout: 5) ⇒ Object
Gracefully terminate background threads (Redis Pub/Sub subscriber, async metrics processor) so the host process can exit promptly.
- .variant(feature_name, context = {}) ⇒ Object
- .variant_value(feature_name, context = {}) ⇒ Object
Class Attribute Details
.adapter_registry ⇒ Object
Returns the value of attribute adapter_registry.
47 48 49 |
# File 'lib/magick.rb', line 47 def adapter_registry @adapter_registry end |
.audit_log ⇒ Object
Returns the value of attribute audit_log.
47 48 49 |
# File 'lib/magick.rb', line 47 def audit_log @audit_log end |
.default_adapter ⇒ Object
Returns the value of attribute default_adapter.
47 48 49 |
# File 'lib/magick.rb', line 47 def default_adapter @default_adapter end |
.versioning ⇒ Object
Returns the value of attribute versioning.
47 48 49 |
# File 'lib/magick.rb', line 47 def versioning @versioning end |
.warn_on_deprecated ⇒ Object
Returns the value of attribute warn_on_deprecated.
47 48 49 |
# File 'lib/magick.rb', line 47 def warn_on_deprecated @warn_on_deprecated end |
Class Method Details
.[](feature_name) ⇒ Object
112 113 114 115 |
# File 'lib/magick.rb', line 112 def [](feature_name) # Return registered feature if it exists, otherwise create new instance features[feature_name.to_s] || Feature.new(feature_name, adapter_registry || default_adapter_registry) end |
.bulk_disable(feature_names, _context = {}) ⇒ Object
175 176 177 178 179 180 181 |
# File 'lib/magick.rb', line 175 def bulk_disable(feature_names, _context = {}) feature_names.map do |name| feature = features[name.to_s] || self[name] feature.set_value(false) if feature.type == :boolean feature end end |
.bulk_enable(feature_names, _context = {}) ⇒ Object
167 168 169 170 171 172 173 |
# File 'lib/magick.rb', line 167 def bulk_enable(feature_names, _context = {}) feature_names.map do |name| feature = features[name.to_s] || self[name] feature.set_value(true) if feature.type == :boolean feature end end |
.configure(&block) ⇒ Object
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/magick.rb', line 81 def configure(&block) @performance_metrics ||= PerformanceMetrics.new @audit_log ||= AuditLog.new @warn_on_deprecated ||= false # Ensure adapter_registry is set (fallback to default if not configured) @adapter_registry ||= default_adapter_registry # Support both old style and new DSL style return unless block_given? if block.arity.zero? # New DSL style - calls apply! automatically Magick::ConfigDSL.configure(&block) else # Old style - need to manually reapply Redis tracking after configuration yield self # Ensure adapter_registry is still set after configuration @adapter_registry ||= default_adapter_registry # Enable Redis tracking if adapter is available and performance_metrics exists # Only enable if not already enabled (to avoid overriding explicit false setting) if @performance_metrics && @adapter_registry.is_a?(Adapters::Registry) && @adapter_registry.redis_available? unless @performance_metrics.instance_variable_get(:@redis_enabled) @performance_metrics.enable_redis_tracking(enable: true) end end end # Final check: ensure adapter_registry is set @adapter_registry ||= default_adapter_registry end |
.default_adapter_registry ⇒ Object
Get default adapter registry (public method for use by other classes)
278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/magick.rb', line 278 def default_adapter_registry @default_adapter_registry ||= begin memory_adapter = Adapters::Memory.new redis_adapter = begin Adapters::Redis.new if defined?(Redis) rescue AdapterError nil end Adapters::Registry.new(memory_adapter, redis_adapter) end end |
.disabled?(feature_name, context = {}) ⇒ Boolean
149 150 151 |
# File 'lib/magick.rb', line 149 def disabled?(feature_name, context = {}) !enabled?(feature_name, context) end |
.disabled_for?(feature_name, object, **additional_context) ⇒ Boolean
139 140 141 |
# File 'lib/magick.rb', line 139 def disabled_for?(feature_name, object, **additional_context) !enabled_for?(feature_name, object, **additional_context) end |
.enable_redis_tracking(enable: true) ⇒ Object
Manually enable Redis tracking for performance metrics Useful if Redis adapter becomes available after initial configuration
206 207 208 209 210 |
# File 'lib/magick.rb', line 206 def enable_redis_tracking(enable: true) return unless performance_metrics performance_metrics.enable_redis_tracking(enable: enable) end |
.enabled?(feature_name, context = {}) ⇒ Boolean
127 128 129 130 131 132 |
# File 'lib/magick.rb', line 127 def enabled?(feature_name, context = {}) # Fast path: use string key directly (avoid repeated to_s conversion) feature_name_str = feature_name.to_s feature = features[feature_name_str] || self[feature_name] feature.enabled?(context) end |
.enabled_for?(feature_name, object, **additional_context) ⇒ Boolean
134 135 136 137 |
# File 'lib/magick.rb', line 134 def enabled_for?(feature_name, object, **additional_context) feature = features[feature_name.to_s] || self[feature_name] feature.enabled_for?(object, **additional_context) end |
.exists?(feature_name) ⇒ Boolean
163 164 165 |
# File 'lib/magick.rb', line 163 def exists?(feature_name) features.key?(feature_name.to_s) || (adapter_registry || default_adapter_registry).exists?(feature_name) end |
.export(format: :json) ⇒ Object
183 184 185 186 187 188 189 190 191 192 |
# File 'lib/magick.rb', line 183 def export(format: :json) case format when :json ExportImport.export_json(features) when :hash ExportImport.export(features) else ExportImport.export(features) end end |
.feature_average_duration(feature_name, operation: nil) ⇒ Object
Get average duration for a feature (optionally filtered by operation)
233 234 235 236 237 |
# File 'lib/magick.rb', line 233 def feature_average_duration(feature_name, operation: nil) return 0.0 unless performance_metrics performance_metrics.average_duration(feature_name: feature_name, operation: operation) end |
.feature_stats(feature_name) ⇒ Object
Get total usage count for a feature (combines memory and Redis counts)
213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/magick.rb', line 213 def feature_stats(feature_name) return {} unless performance_metrics { usage_count: performance_metrics.usage_count(feature_name), average_duration: performance_metrics.average_duration(feature_name: feature_name), average_duration_by_operation: { enabled: performance_metrics.average_duration(feature_name: feature_name, operation: 'enabled?'), value: performance_metrics.average_duration(feature_name: feature_name, operation: 'value'), get_value: performance_metrics.average_duration(feature_name: feature_name, operation: 'get_value') } } end |
.feature_usage_count(feature_name) ⇒ Object
Get usage count for a feature
228 229 230 |
# File 'lib/magick.rb', line 228 def feature_usage_count(feature_name) performance_metrics&.usage_count(feature_name) || 0 end |
.features ⇒ Object
117 118 119 |
# File 'lib/magick.rb', line 117 def features @features ||= {} end |
.import(data, format: :json) ⇒ Object
194 195 196 197 198 |
# File 'lib/magick.rb', line 194 def import(data, format: :json) imported = ExportImport.import(data, adapter_registry || default_adapter_registry) imported.each { |name, feature| features[name] = feature } imported end |
.most_used_features(limit: 10) ⇒ Object
Get most used features
240 241 242 |
# File 'lib/magick.rb', line 240 def most_used_features(limit: 10) performance_metrics&.most_used_features(limit: limit) || {} end |
.performance_metrics ⇒ Object
Getter for performance_metrics
77 78 79 |
# File 'lib/magick.rb', line 77 def performance_metrics @performance_metrics end |
.performance_metrics=(value) ⇒ Object
Override performance_metrics setter to auto-enable Redis tracking
51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/magick.rb', line 51 def performance_metrics=(value) @performance_metrics = value # Auto-enable Redis tracking if Redis adapter is available if value && adapter_registry.is_a?(Adapters::Registry) && adapter_registry.redis_available? value.enable_redis_tracking(enable: true) end # Update all existing features to enable performance metrics tracking if value features.each_value do |feature| feature.instance_variable_set(:@_perf_metrics_enabled, true) end end value end |
.preload! ⇒ Object
Preload all features into memory cache in minimal queries. Call after configuration and feature registration to avoid per-feature cache misses.
246 247 248 249 250 251 252 253 254 255 256 257 258 259 |
# File 'lib/magick.rb', line 246 def preload! registry = adapter_registry || default_adapter_registry return unless registry # Bulk load all feature data into memory (1-2 queries total) all_data = registry.preload! # Also preload registered features' state from the cached data features.each_value do |feature| feature.reload end all_data end |
.register_feature(name, **options) ⇒ Object
121 122 123 124 125 |
# File 'lib/magick.rb', line 121 def register_feature(name, **) feature = Feature.new(name, adapter_registry || default_adapter_registry, **) features[name.to_s] = feature feature end |
.reload_feature(feature_name) ⇒ Object
Reload a feature from the adapter (useful when feature is changed externally)
144 145 146 147 |
# File 'lib/magick.rb', line 144 def reload_feature(feature_name) feature = features[feature_name.to_s] || self[feature_name] feature.reload end |
.reset! ⇒ Object
261 262 263 264 265 266 |
# File 'lib/magick.rb', line 261 def reset! @features = {} @adapter_registry = nil @default_adapter = nil @performance_metrics&.clear! end |
.shutdown!(timeout: 5) ⇒ Object
Gracefully terminate background threads (Redis Pub/Sub subscriber, async metrics processor) so the host process can exit promptly. Intended for use in Rails shutdown hooks, ‘at_exit`, or tests.
271 272 273 274 275 |
# File 'lib/magick.rb', line 271 def shutdown!(timeout: 5) safely_shutdown(@adapter_registry) { |r| r.shutdown(timeout: timeout) } safely_shutdown(@performance_metrics, &:stop_async_processor) true end |
.variant(feature_name, context = {}) ⇒ Object
153 154 155 156 |
# File 'lib/magick.rb', line 153 def variant(feature_name, context = {}) feature = features[feature_name.to_s] || self[feature_name] feature.get_variant(context) end |
.variant_value(feature_name, context = {}) ⇒ Object
158 159 160 161 |
# File 'lib/magick.rb', line 158 def variant_value(feature_name, context = {}) feature = features[feature_name.to_s] || self[feature_name] feature.get_variant_value(context) end |