magick-feature-flags 1.3.0 → 1.3.1
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 +4 -4
- data/README.md +26 -0
- data/app/controllers/magick/adminui/features_controller.rb +5 -2
- data/lib/magick/feature.rb +14 -19
- data/lib/magick/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a34f03026e8631bc2d29c0935cb98e472e7ed387fc4628edc357776423bfc2c5
|
|
4
|
+
data.tar.gz: d87af21843efb22d5dba60d342f3beb5fd807eb42f1b2948c82ae8dc3d6b67a2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '0296397245455dbecd448a2dab448b2209579f4c9c39a1e93b424e8f6982fe8a3f498140ac7af5f1127c70fc2cdaa3378194135afe2e55fe9254646a4c761e64'
|
|
7
|
+
data.tar.gz: 43e1ce6dcf51815f83ccf4440845d415f8d351e6a6038c8f7bcd0a294dfc0def6531a04a118119d66e71a64d7cfcc1607273eda0c181dfdf951c9ee61d4cfae7
|
data/README.md
CHANGED
|
@@ -467,6 +467,32 @@ class CheckoutController < ApplicationController
|
|
|
467
467
|
end
|
|
468
468
|
```
|
|
469
469
|
|
|
470
|
+
**Experiments without a user (anonymous visitors):**
|
|
471
|
+
|
|
472
|
+
For flows where there's no authenticated user yet (e.g., registration, landing pages), use any stable identifier as `user_id` — a session ID or a tracking cookie:
|
|
473
|
+
|
|
474
|
+
```ruby
|
|
475
|
+
class RegistrationController < ApplicationController
|
|
476
|
+
def new
|
|
477
|
+
cookies[:visitor_id] ||= SecureRandom.uuid
|
|
478
|
+
@variant = Magick.variant(:registration_flow, user_id: cookies[:visitor_id])
|
|
479
|
+
end
|
|
480
|
+
end
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
The hashing just needs a consistent string. As long as the same visitor sends the same identifier, they get the same variant every time.
|
|
484
|
+
|
|
485
|
+
**Safe to call on non-existent experiments:**
|
|
486
|
+
|
|
487
|
+
```ruby
|
|
488
|
+
Magick.variant(:nonexistent, user_id: 123) # => nil
|
|
489
|
+
Magick.variant_value(:nonexistent, user_id: 123) # => nil
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
**Important — changing weights may shift users:**
|
|
493
|
+
|
|
494
|
+
You can change variant weights at any time via the Admin UI or code, and changes take effect immediately across all adapters. However, changing weights alters the bucket boundaries, which means some users may be reassigned to a different variant after the update. Magick does not persist individual user-to-variant assignments — assignment is computed on the fly from the hash. If your experiment requires that users never shift variants mid-experiment, you should persist the assignment externally (e.g., store `user_id → variant` in a database table on first exposure).
|
|
495
|
+
|
|
470
496
|
**How it works:**
|
|
471
497
|
- Variants are assigned using a deterministic MD5 hash of `feature_name + user_id`
|
|
472
498
|
- The same user always gets the same variant across sessions and requests
|
|
@@ -80,8 +80,11 @@ module Magick
|
|
|
80
80
|
end
|
|
81
81
|
|
|
82
82
|
def enable
|
|
83
|
-
@feature.enable
|
|
84
|
-
|
|
83
|
+
if @feature.enable
|
|
84
|
+
redirect_to magick_admin_ui.features_path, notice: 'Feature enabled'
|
|
85
|
+
else
|
|
86
|
+
redirect_to magick_admin_ui.feature_path(@feature.name), alert: 'Cannot enable feature — its dependencies must be enabled first'
|
|
87
|
+
end
|
|
85
88
|
end
|
|
86
89
|
|
|
87
90
|
def disable
|
data/lib/magick/feature.rb
CHANGED
|
@@ -516,20 +516,16 @@ module Magick
|
|
|
516
516
|
end
|
|
517
517
|
|
|
518
518
|
def enable(user_id: nil)
|
|
519
|
-
# Check
|
|
520
|
-
#
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
end
|
|
519
|
+
# Check that all of this feature's own dependencies are enabled
|
|
520
|
+
# e.g. if checkout depends on payments, checkout can't be enabled until payments is
|
|
521
|
+
deps = @dependencies || []
|
|
522
|
+
unless deps.empty?
|
|
523
|
+
disabled_deps = deps.select do |dep_name|
|
|
524
|
+
dep_feature = Magick.features[dep_name.to_s] || Magick[dep_name]
|
|
525
|
+
dep_feature && !dep_feature.enabled?
|
|
526
|
+
end
|
|
528
527
|
|
|
529
|
-
|
|
530
|
-
# Return false if any main feature that depends on this feature is disabled
|
|
531
|
-
# This prevents enabling a dependency when the main feature is disabled
|
|
532
|
-
return false
|
|
528
|
+
return false unless disabled_deps.empty?
|
|
533
529
|
end
|
|
534
530
|
|
|
535
531
|
# Clear all targeting to enable globally
|
|
@@ -988,13 +984,12 @@ module Magick
|
|
|
988
984
|
end
|
|
989
985
|
|
|
990
986
|
def disable_dependent_features(user_id: nil)
|
|
991
|
-
# Cascade-disable
|
|
992
|
-
# e.g. if
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
return if deps.empty?
|
|
987
|
+
# Cascade-disable features that depend ON this feature.
|
|
988
|
+
# e.g. if checkout depends on payments, disabling payments also disables checkout.
|
|
989
|
+
dependents = find_dependent_features
|
|
990
|
+
return if dependents.empty?
|
|
996
991
|
|
|
997
|
-
|
|
992
|
+
dependents.each do |dep_name|
|
|
998
993
|
dep_feature = Magick.features[dep_name.to_s] || Magick[dep_name]
|
|
999
994
|
next unless dep_feature
|
|
1000
995
|
|
data/lib/magick/version.rb
CHANGED