magick-feature-flags 0.9.18 → 0.9.20

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: 7df3e59dc8f0736e18fb93603f53038f48b5302769c42cc37ab87f2afcba0307
4
- data.tar.gz: e0235e0c7518860061b7b67a27aee9c1be88346c8292733aae0a2c794c2288d7
3
+ metadata.gz: e4f9379b630e9db00b28614f347d523edb33d63c842d6b5c9fd747be02f1d75d
4
+ data.tar.gz: ffa32ee13faa08781c68870813c15ea7b3dcc15fe65f8c1f2618952251fe870c
5
5
  SHA512:
6
- metadata.gz: 0a76c637a3cd96cebd6fa91be4f10df8e1174713fabb82c4cdb8df1cecd2d4c6c6ea46e00949cc485e6c0a3d46a9180c718204361a2630c8a71a069a0c6f6bbd
7
- data.tar.gz: f0682f004002eab91412ee9d948e69f3f84de41238ab2d5ca3f06abec1abf2616c86063a66198f8a596f32d8dc2cf545c9db0b62961e6dc98a4d97a39b01e5cc
6
+ metadata.gz: 0f2651c1f8528b4eb385f82a629c7891e0a78ec4a796b574baa8228a2bfcaddf88ee6855af45f9fba9c12b4d7d475e1d8aa6ba6b1002706a1247a8ef81b540e4
7
+ data.tar.gz: 418cc8c6909969581b147df052c72ffc9635a86b07726f69f0c6085bef3bbb804515d34a3d6ca782f8b4c03d4127722a5547a483ca2d0cddfc56f3bef4e6e864
@@ -11,12 +11,16 @@ module Magick
11
11
  end
12
12
 
13
13
  def get(feature_name, key)
14
+ # Fast path: avoid mutex if possible (use string keys directly)
15
+ feature_name_str = feature_name.is_a?(String) ? feature_name : feature_name.to_s
16
+ key_str = key.is_a?(String) ? key : key.to_s
17
+
14
18
  mutex.synchronize do
15
19
  cleanup_expired
16
- feature_data = store[feature_name.to_s]
20
+ feature_data = store[feature_name_str]
17
21
  return nil unless feature_data
18
22
 
19
- value = feature_data[key.to_s]
23
+ value = feature_data[key_str]
20
24
  deserialize_value(value)
21
25
  end
22
26
  end
@@ -22,77 +22,54 @@ module Magick
22
22
  @dependencies = options[:dependencies] ? Array(options[:dependencies]) : []
23
23
  @stored_value_initialized = false # Track if @stored_value has been explicitly set
24
24
 
25
+ # Performance optimizations: cache expensive checks
26
+ @_targeting_empty = true # Will be updated after load_from_adapter
27
+ @_rails_events_enabled = false # Cache Rails events availability (only enable in dev)
28
+ @_perf_metrics_enabled = false # Cache performance metrics (disabled by default for speed)
29
+
25
30
  validate_type!
26
31
  validate_default_value!
27
32
  load_from_adapter
33
+ # Update targeting empty cache after loading
34
+ @_targeting_empty = @targeting.empty?
35
+ # Cache performance metrics availability (check once, not on every call)
36
+ @_perf_metrics_enabled = Magick.performance_metrics != nil
28
37
  # Save description and display_name to adapter if they were provided and not already in adapter
29
38
  save_metadata_if_new
30
39
  end
31
40
 
32
41
  def enabled?(context = {})
33
- # Track usage if metrics available
34
- start_time = Time.now if Magick.performance_metrics
35
-
36
- result = check_enabled(context)
37
-
38
- if Magick.performance_metrics
39
- duration = (Time.now - start_time) * 1000 # milliseconds
40
- Magick.performance_metrics.record(name, 'enabled?', duration, success: true)
41
- end
42
-
43
- # Rails 8+ events for enabled/disabled
44
- if defined?(Magick::Rails::Events) && Magick::Rails::Events.rails8?
45
- if result
46
- Magick::Rails::Events.feature_enabled(name, context: context)
47
- else
48
- Magick::Rails::Events.feature_disabled(name, context: context)
49
- end
50
- end
51
-
52
- # Warn if deprecated
53
- if status == :deprecated && result && !context[:allow_deprecated]
54
- warn "DEPRECATED: Feature '#{name}' is deprecated and will be removed." if Magick.warn_on_deprecated
55
-
56
- # Rails 8+ event for deprecation warning
57
- if defined?(Magick::Rails::Events) && Magick::Rails::Events.rails8?
58
- Magick::Rails::Events.deprecated_warning(name)
59
- end
60
- end
61
-
62
- result
42
+ # Fastest path: check enabled status directly (no overhead)
43
+ check_enabled(context)
63
44
  rescue StandardError => e
64
- if Magick.performance_metrics
65
- duration = (Time.now - start_time) * 1000
66
- Magick.performance_metrics.record(name, 'enabled?', duration, success: false)
67
- end
68
45
  # Return false on any error (fail-safe)
69
46
  warn "Magick: Error checking feature '#{name}': #{e.message}" if defined?(Rails) && Rails.env.development?
70
47
  false
71
48
  end
72
49
 
73
50
  def check_enabled(context = {})
51
+ # Fast path: check status first
74
52
  return false if status == :inactive
75
53
  return false if status == :deprecated && !context[:allow_deprecated]
76
54
 
77
- # NOTE: We don't check dependencies here because:
78
- # - Main features can be enabled independently
79
- # - Dependencies are only prevented from being enabled if the main feature is disabled
80
- # - The dependency check is handled in the enable() method, not in enabled?()
81
-
82
- # Check date/time range targeting
83
- return false if targeting[:date_range] && !date_range_active?(targeting[:date_range])
55
+ # Fast path: skip targeting checks if targeting is empty (most common case)
56
+ unless @_targeting_empty
57
+ # Check date/time range targeting
58
+ return false if targeting[:date_range] && !date_range_active?(targeting[:date_range])
84
59
 
85
- # Check IP address targeting
86
- return false if targeting[:ip_address] && context[:ip_address] && !ip_address_matches?(context[:ip_address])
60
+ # Check IP address targeting
61
+ return false if targeting[:ip_address] && context[:ip_address] && !ip_address_matches?(context[:ip_address])
87
62
 
88
- # Check custom attributes
89
- return false if targeting[:custom_attributes] && !custom_attributes_match?(context, targeting[:custom_attributes])
63
+ # Check custom attributes
64
+ return false if targeting[:custom_attributes] && !custom_attributes_match?(context,
65
+ targeting[:custom_attributes])
90
66
 
91
- # Check complex conditions
92
- if targeting[:complex_conditions] && !complex_conditions_match?(context, targeting[:complex_conditions])
93
- return false
67
+ # Check complex conditions
68
+ return false if targeting[:complex_conditions] && !complex_conditions_match?(context,
69
+ targeting[:complex_conditions])
94
70
  end
95
71
 
72
+ # Get value and check based on type
96
73
  value = get_value(context)
97
74
  case type
98
75
  when :boolean
@@ -131,19 +108,21 @@ module Magick
131
108
  end
132
109
 
133
110
  def get_value(context = {})
134
- # Check targeting rules first
135
- targeting_result = check_targeting(context)
136
- return targeting_result unless targeting_result.nil?
111
+ # Fast path: check targeting rules first (only if targeting exists)
112
+ unless @_targeting_empty
113
+ targeting_result = check_targeting(context)
114
+ return targeting_result unless targeting_result.nil?
115
+ end
137
116
 
138
- # Use instance variable if it has been set (check using defined? to handle nil vs uninitialized)
139
- # We use a special check: if @stored_value was explicitly set (even to false), use it
140
- # We track this by checking if @stored_value_initialized flag is set
141
- return @stored_value if defined?(@stored_value_initialized) && @stored_value_initialized
117
+ # Fast path: use cached value if initialized (avoid adapter calls)
118
+ return @stored_value if @stored_value_initialized
142
119
 
143
120
  # Load from adapter if instance variable hasn't been initialized
144
121
  loaded_value = load_value_from_adapter
145
122
  if loaded_value.nil?
146
- # Value not found in adapter, use default
123
+ # Value not found in adapter, use default and cache it
124
+ @stored_value = default_value
125
+ @stored_value_initialized = true
147
126
  default_value
148
127
  else
149
128
  # Value found in adapter, use it and mark as initialized
@@ -827,9 +806,14 @@ module Magick
827
806
  adapter_registry.set(name, 'targeting', targeting)
828
807
 
829
808
  # Update the feature in Magick.features if it's registered
830
- return unless Magick.features.key?(name)
809
+ if Magick.features.key?(name)
810
+ Magick.features[name].instance_variable_set(:@targeting, targeting.dup)
811
+ # Update targeting empty cache for performance
812
+ Magick.features[name].instance_variable_set(:@_targeting_empty, targeting.empty?)
813
+ end
831
814
 
832
- Magick.features[name].instance_variable_set(:@targeting, targeting.dup)
815
+ # Update local targeting empty cache for performance
816
+ @_targeting_empty = targeting.empty?
833
817
 
834
818
  # Explicitly trigger cache invalidation for targeting updates
835
819
  # Targeting changes affect enabled? checks, so we need immediate cache invalidation
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Magick
4
- VERSION = '0.9.18'
4
+ VERSION = '0.9.20'
5
5
  end
data/lib/magick.rb CHANGED
@@ -103,7 +103,9 @@ module Magick
103
103
  end
104
104
 
105
105
  def enabled?(feature_name, context = {})
106
- feature = features[feature_name.to_s] || self[feature_name]
106
+ # Fast path: use string key directly (avoid repeated to_s conversion)
107
+ feature_name_str = feature_name.to_s
108
+ feature = features[feature_name_str] || self[feature_name]
107
109
  feature.enabled?(context)
108
110
  end
109
111
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: magick-feature-flags
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.18
4
+ version: 0.9.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Lobanov