determinator 2.5.1 → 2.7.0
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/.github/workflows/gem-push.yml +33 -0
- data/CHANGELOG.md +29 -0
- data/determinator.gemspec +4 -4
- data/examples/determinator-rails/Gemfile +1 -2
- data/examples/determinator-rails/Gemfile.lock +93 -89
- data/examples/determinator-rails/app/controllers/index_controller.rb +6 -2
- data/examples/determinator-rails/config/initializers/determinator.rb +4 -2
- data/examples/determinator-rails/config/initializers/new_framework_defaults.rb +0 -1
- data/examples/determinator-rails/example_features/colloquial_welcome +14 -0
- data/examples/determinator-rails/example_features/welcome_emoji +19 -0
- data/lib/determinator.rb +3 -3
- data/lib/determinator/control.rb +22 -6
- data/lib/determinator/feature.rb +10 -10
- data/lib/determinator/serializers/json.rb +1 -0
- data/lib/determinator/version.rb +1 -1
- data/lib/rspec/determinator.rb +5 -2
- metadata +17 -15
- data/examples/determinator-rails/.env +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dffc736e777d38ceafbe8ce45d0a01f960341a542a2b93b6ba13b77f1479d291
|
4
|
+
data.tar.gz: 7be7ecbd850faac8009e031d41eea1d9679d5f2c43923cbc8ad688c6c1411f3a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b959c8460720151fd7455f1bbc556af6f61f327c7092bad148d52bc9141c3e5144c17a7c6cb45c483322f9dc65ee047ceccc1a048eea15e46544f6aa4fb06e1
|
7
|
+
data.tar.gz: b8b08c82db627c37d8630dd56111d0361b3c5a5874286d447015ac98d0d25a2421631f589f4438b26d71bc9ed58f5141d8084563dd37c24c7f5ca71faac62156
|
@@ -0,0 +1,33 @@
|
|
1
|
+
name: Ruby Gem
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ master ]
|
6
|
+
|
7
|
+
jobs:
|
8
|
+
build:
|
9
|
+
name: Build + Publish
|
10
|
+
runs-on: ubuntu-latest
|
11
|
+
|
12
|
+
steps:
|
13
|
+
- uses: actions/checkout@v2
|
14
|
+
- name: Set git user
|
15
|
+
run: |
|
16
|
+
git config user.email "rooci@deliveroo.co.uk"
|
17
|
+
git config user.name "Determinator Release Github Action"
|
18
|
+
- name: Set up Ruby 2.6
|
19
|
+
uses: actions/setup-ruby@v1
|
20
|
+
with:
|
21
|
+
ruby-version: 2.6.x
|
22
|
+
|
23
|
+
- name: Publish to RubyGems
|
24
|
+
run: |
|
25
|
+
mkdir -p $HOME/.gem
|
26
|
+
touch $HOME/.gem/credentials
|
27
|
+
chmod 0600 $HOME/.gem/credentials
|
28
|
+
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
29
|
+
gem install bundler
|
30
|
+
bundle install --jobs 4 --retry 3
|
31
|
+
rake release
|
32
|
+
env:
|
33
|
+
GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,32 @@
|
|
1
|
+
# 2.7.0
|
2
|
+
|
3
|
+
⚠️ This release includes breaking changes ⚠️
|
4
|
+
|
5
|
+
Interface change:
|
6
|
+
- Constraints which are not arrays of strings are no longer accepted; if present, the library returns false and logs an error.
|
7
|
+
|
8
|
+
# 2.6.0
|
9
|
+
|
10
|
+
Interface change:
|
11
|
+
- A `feature_cache` is now required to use Determinator. See the `examples/determinator-rails/config/initializers/determinator.rb` for a quick start.
|
12
|
+
|
13
|
+
# 2.5.4
|
14
|
+
|
15
|
+
Bug fix:
|
16
|
+
- Apply app_version logic to structured request.app_version too
|
17
|
+
|
18
|
+
# 2.5.3
|
19
|
+
|
20
|
+
Bug fix:
|
21
|
+
- Avoid errors when updating the gem and using persistent cache resulting in null fixed_determinations
|
22
|
+
|
23
|
+
# 2.5.2
|
24
|
+
|
25
|
+
Feature:
|
26
|
+
- Add structured_bucket to Feature
|
27
|
+
- Add `#retrieve` method to the Control
|
28
|
+
- Add optional `feature` argument to `feature_flag_on?` and `which_variant`, to reuse an existing feature
|
29
|
+
|
1
30
|
# 2.5.1
|
2
31
|
|
3
32
|
Feature:
|
data/determinator.gemspec
CHANGED
@@ -23,12 +23,12 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.add_runtime_dependency "faraday"
|
24
24
|
spec.add_runtime_dependency "semantic", "~> 1.6"
|
25
25
|
|
26
|
-
spec.add_development_dependency "bundler"
|
27
|
-
spec.add_development_dependency "rake", "~>
|
26
|
+
spec.add_development_dependency "bundler"
|
27
|
+
spec.add_development_dependency "rake", "~> 12.0"
|
28
28
|
spec.add_development_dependency "rspec", "~> 3.0"
|
29
29
|
spec.add_development_dependency "rspec-its", "~> 1.2"
|
30
30
|
spec.add_development_dependency "guard-rspec", "~> 4.7"
|
31
|
-
spec.add_development_dependency "
|
31
|
+
spec.add_development_dependency "factory_bot", "~> 4.8"
|
32
32
|
spec.add_development_dependency 'webmock'
|
33
|
-
spec.add_development_dependency
|
33
|
+
spec.add_development_dependency 'sidekiq'
|
34
34
|
end
|
@@ -1,140 +1,144 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../..
|
3
3
|
specs:
|
4
|
-
determinator (
|
4
|
+
determinator (2.6.0)
|
5
5
|
faraday
|
6
|
+
semantic (~> 1.6)
|
6
7
|
|
7
8
|
GEM
|
8
9
|
remote: https://rubygems.org/
|
9
10
|
specs:
|
10
|
-
actioncable (5.
|
11
|
-
actionpack (= 5.
|
12
|
-
nio4r (
|
13
|
-
websocket-driver (
|
14
|
-
actionmailer (5.
|
15
|
-
actionpack (= 5.
|
16
|
-
actionview (= 5.
|
17
|
-
activejob (= 5.
|
11
|
+
actioncable (5.2.4.4)
|
12
|
+
actionpack (= 5.2.4.4)
|
13
|
+
nio4r (~> 2.0)
|
14
|
+
websocket-driver (>= 0.6.1)
|
15
|
+
actionmailer (5.2.4.4)
|
16
|
+
actionpack (= 5.2.4.4)
|
17
|
+
actionview (= 5.2.4.4)
|
18
|
+
activejob (= 5.2.4.4)
|
18
19
|
mail (~> 2.5, >= 2.5.4)
|
19
20
|
rails-dom-testing (~> 2.0)
|
20
|
-
actionpack (5.
|
21
|
-
actionview (= 5.
|
22
|
-
activesupport (= 5.
|
23
|
-
rack (~> 2.0)
|
24
|
-
rack-test (
|
21
|
+
actionpack (5.2.4.4)
|
22
|
+
actionview (= 5.2.4.4)
|
23
|
+
activesupport (= 5.2.4.4)
|
24
|
+
rack (~> 2.0, >= 2.0.8)
|
25
|
+
rack-test (>= 0.6.3)
|
25
26
|
rails-dom-testing (~> 2.0)
|
26
27
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
27
|
-
actionview (5.
|
28
|
-
activesupport (= 5.
|
28
|
+
actionview (5.2.4.4)
|
29
|
+
activesupport (= 5.2.4.4)
|
29
30
|
builder (~> 3.1)
|
30
|
-
|
31
|
+
erubi (~> 1.4)
|
31
32
|
rails-dom-testing (~> 2.0)
|
32
33
|
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
33
|
-
activejob (5.
|
34
|
-
activesupport (= 5.
|
34
|
+
activejob (5.2.4.4)
|
35
|
+
activesupport (= 5.2.4.4)
|
35
36
|
globalid (>= 0.3.6)
|
36
|
-
activemodel (5.
|
37
|
-
activesupport (= 5.
|
38
|
-
activerecord (5.
|
39
|
-
activemodel (= 5.
|
40
|
-
activesupport (= 5.
|
41
|
-
arel (
|
42
|
-
|
37
|
+
activemodel (5.2.4.4)
|
38
|
+
activesupport (= 5.2.4.4)
|
39
|
+
activerecord (5.2.4.4)
|
40
|
+
activemodel (= 5.2.4.4)
|
41
|
+
activesupport (= 5.2.4.4)
|
42
|
+
arel (>= 9.0)
|
43
|
+
activestorage (5.2.4.4)
|
44
|
+
actionpack (= 5.2.4.4)
|
45
|
+
activerecord (= 5.2.4.4)
|
46
|
+
marcel (~> 0.3.1)
|
47
|
+
activesupport (5.2.4.4)
|
43
48
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
44
49
|
i18n (>= 0.7, < 2)
|
45
50
|
minitest (~> 5.1)
|
46
51
|
tzinfo (~> 1.1)
|
47
|
-
arel (
|
48
|
-
builder (3.2.
|
49
|
-
byebug (
|
50
|
-
concurrent-ruby (1.
|
51
|
-
|
52
|
-
|
53
|
-
dotenv (2.
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
52
|
+
arel (9.0.0)
|
53
|
+
builder (3.2.4)
|
54
|
+
byebug (11.1.3)
|
55
|
+
concurrent-ruby (1.1.8)
|
56
|
+
crass (1.0.6)
|
57
|
+
dotenv (2.7.6)
|
58
|
+
dotenv-rails (2.7.6)
|
59
|
+
dotenv (= 2.7.6)
|
60
|
+
railties (>= 3.2)
|
61
|
+
erubi (1.10.0)
|
62
|
+
faraday (1.3.0)
|
63
|
+
faraday-net_http (~> 1.0)
|
59
64
|
multipart-post (>= 1.2, < 3)
|
60
|
-
|
65
|
+
ruby2_keywords
|
66
|
+
faraday-net_http (1.0.1)
|
67
|
+
globalid (0.4.2)
|
61
68
|
activesupport (>= 4.2.0)
|
62
|
-
i18n (1.
|
69
|
+
i18n (1.8.7)
|
63
70
|
concurrent-ruby (~> 1.0)
|
64
|
-
loofah (2.
|
71
|
+
loofah (2.9.0)
|
65
72
|
crass (~> 1.0.2)
|
66
73
|
nokogiri (>= 1.5.9)
|
67
|
-
mail (2.7.
|
74
|
+
mail (2.7.1)
|
68
75
|
mini_mime (>= 0.1.1)
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
rack
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
76
|
+
marcel (0.3.3)
|
77
|
+
mimemagic (~> 0.3.2)
|
78
|
+
method_source (1.0.0)
|
79
|
+
mimemagic (0.3.5)
|
80
|
+
mini_mime (1.0.2)
|
81
|
+
minitest (5.14.3)
|
82
|
+
multipart-post (2.1.1)
|
83
|
+
nio4r (2.5.4)
|
84
|
+
nokogiri (1.11.1-x86_64-darwin)
|
85
|
+
racc (~> 1.4)
|
86
|
+
puma (3.12.6)
|
87
|
+
racc (1.5.2)
|
88
|
+
rack (2.2.3)
|
89
|
+
rack-test (1.1.0)
|
90
|
+
rack (>= 1.0, < 3)
|
91
|
+
rails (5.2.4.4)
|
92
|
+
actioncable (= 5.2.4.4)
|
93
|
+
actionmailer (= 5.2.4.4)
|
94
|
+
actionpack (= 5.2.4.4)
|
95
|
+
actionview (= 5.2.4.4)
|
96
|
+
activejob (= 5.2.4.4)
|
97
|
+
activemodel (= 5.2.4.4)
|
98
|
+
activerecord (= 5.2.4.4)
|
99
|
+
activestorage (= 5.2.4.4)
|
100
|
+
activesupport (= 5.2.4.4)
|
92
101
|
bundler (>= 1.3.0)
|
93
|
-
railties (= 5.
|
102
|
+
railties (= 5.2.4.4)
|
94
103
|
sprockets-rails (>= 2.0.0)
|
95
104
|
rails-dom-testing (2.0.3)
|
96
105
|
activesupport (>= 4.2.0)
|
97
106
|
nokogiri (>= 1.6)
|
98
|
-
rails-html-sanitizer (1.0
|
99
|
-
loofah (~> 2.
|
100
|
-
railties (5.
|
101
|
-
actionpack (= 5.
|
102
|
-
activesupport (= 5.
|
107
|
+
rails-html-sanitizer (1.3.0)
|
108
|
+
loofah (~> 2.3)
|
109
|
+
railties (5.2.4.4)
|
110
|
+
actionpack (= 5.2.4.4)
|
111
|
+
activesupport (= 5.2.4.4)
|
103
112
|
method_source
|
104
113
|
rake (>= 0.8.7)
|
105
|
-
thor (>= 0.
|
106
|
-
rake (
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
connection_pool (~> 2.2, >= 2.2.0)
|
111
|
-
rack-protection (>= 1.5.0)
|
112
|
-
redis (>= 3.3.5, < 5)
|
113
|
-
sprockets (3.7.1)
|
114
|
+
thor (>= 0.19.0, < 2.0)
|
115
|
+
rake (13.0.3)
|
116
|
+
ruby2_keywords (0.0.4)
|
117
|
+
semantic (1.6.1)
|
118
|
+
sprockets (4.0.2)
|
114
119
|
concurrent-ruby (~> 1.0)
|
115
120
|
rack (> 1, < 3)
|
116
|
-
sprockets-rails (3.2.
|
121
|
+
sprockets-rails (3.2.2)
|
117
122
|
actionpack (>= 4.0)
|
118
123
|
activesupport (>= 4.0)
|
119
124
|
sprockets (>= 3.0.0)
|
120
|
-
thor (
|
125
|
+
thor (1.1.0)
|
121
126
|
thread_safe (0.3.6)
|
122
|
-
tzinfo (1.2.
|
127
|
+
tzinfo (1.2.9)
|
123
128
|
thread_safe (~> 0.1)
|
124
|
-
websocket-driver (0.
|
129
|
+
websocket-driver (0.7.3)
|
125
130
|
websocket-extensions (>= 0.1.0)
|
126
|
-
websocket-extensions (0.1.
|
131
|
+
websocket-extensions (0.1.5)
|
127
132
|
|
128
133
|
PLATFORMS
|
129
|
-
|
134
|
+
x86_64-darwin-19
|
130
135
|
|
131
136
|
DEPENDENCIES
|
132
137
|
byebug
|
133
138
|
determinator!
|
134
139
|
dotenv-rails
|
135
140
|
puma (~> 3.0)
|
136
|
-
rails (~> 5.
|
137
|
-
sidekiq
|
141
|
+
rails (~> 5.1)
|
138
142
|
|
139
143
|
BUNDLED WITH
|
140
|
-
|
144
|
+
2.2.7
|
@@ -5,9 +5,13 @@ class IndexController < ApplicationController
|
|
5
5
|
|
6
6
|
message = [
|
7
7
|
is_colloquial ? "hi world" : "hello world",
|
8
|
-
emoji
|
8
|
+
(emoji if emoji)
|
9
9
|
].compact.join(" ")
|
10
10
|
|
11
|
-
|
11
|
+
explain = "An experiment and a feature flag are being checked for the user with guid #{guid}. "
|
12
|
+
explain += "The feature flag (colloquial_welcome) is #{is_colloquial ? 'on' : 'off'}. "
|
13
|
+
explain += "The experiment (welcome_emoji) returned #{emoji}#{", so is omitted" unless emoji}."
|
14
|
+
|
15
|
+
render json: { welcome: message, explanation: explain }
|
12
16
|
end
|
13
17
|
end
|
@@ -1,7 +1,9 @@
|
|
1
|
-
require 'determinator/retrieve/
|
1
|
+
require 'determinator/retrieve/file'
|
2
2
|
require 'active_support/cache'
|
3
3
|
|
4
|
-
|
4
|
+
# File retriever just for example; use a Dynaconf retriever in your app
|
5
|
+
# retrieval = Determinator::Retrieve::Dynaconf.new(base_url: ENV['DYNACONF_URL'], service_name: 'determinator-rails')
|
6
|
+
retrieval = Determinator::Retrieve::File.new(root: File.join(__dir__, "../../example_features"))
|
5
7
|
feature_cache = Determinator::Cache::FetchWrapper.new(
|
6
8
|
ActiveSupport::Cache::MemoryStore.new(expires_in: 1.minute)
|
7
9
|
)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
{
|
2
|
+
"id": "colloquial_welcome",
|
3
|
+
"identifier": "colloquial_welcome",
|
4
|
+
"name": "Whether a colloquial welcome should be used",
|
5
|
+
"active": true,
|
6
|
+
"bucket_type": "guid",
|
7
|
+
"target_groups": [
|
8
|
+
{
|
9
|
+
"name": "50% of users",
|
10
|
+
"rollout": 32768,
|
11
|
+
"constraints": {}
|
12
|
+
}
|
13
|
+
]
|
14
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
{
|
2
|
+
"id": "welcome_emoji",
|
3
|
+
"identifier": "welcome_emoji",
|
4
|
+
"name": "What emoji should be used for the example hello",
|
5
|
+
"active": true,
|
6
|
+
"bucket_type": "guid",
|
7
|
+
"variants": {
|
8
|
+
"👋": 1,
|
9
|
+
"🎉": 1
|
10
|
+
},
|
11
|
+
"winning_variant": null,
|
12
|
+
"target_groups": [
|
13
|
+
{
|
14
|
+
"name": "50% of users",
|
15
|
+
"rollout": 32768,
|
16
|
+
"constraints": {}
|
17
|
+
}
|
18
|
+
]
|
19
|
+
}
|
data/lib/determinator.rb
CHANGED
@@ -15,13 +15,13 @@ module Determinator
|
|
15
15
|
class << self
|
16
16
|
attr_reader :feature_cache, :retrieval
|
17
17
|
# @param :retrieval [Determinator::Retrieve::Routemaster] A retrieval instance for Features
|
18
|
+
# @param :feature_cache [#call] a caching proc, accepting a feature name, which will return the named feature or yield (and store) if not available
|
18
19
|
# @param :errors [#call, nil] a proc, accepting an error, which will be called with any errors which occur while determinating
|
19
20
|
# @param :missing_feature [#call, nil] a proc, accepting a feature name, which will be called any time a feature is requested but isn't available
|
20
|
-
|
21
|
-
def configure(retrieval:, errors: nil, missing_feature: nil, feature_cache: nil)
|
21
|
+
def configure(retrieval:, feature_cache:, errors: nil, missing_feature: nil)
|
22
22
|
self.on_error(&errors) if errors
|
23
23
|
self.on_missing_feature(&missing_feature) if missing_feature
|
24
|
-
@feature_cache = feature_cache
|
24
|
+
@feature_cache = feature_cache
|
25
25
|
@retrieval = retrieval
|
26
26
|
@instance = Control.new(retrieval: retrieval)
|
27
27
|
end
|
data/lib/determinator/control.rb
CHANGED
@@ -30,10 +30,11 @@ module Determinator
|
|
30
30
|
# @param :id [#to_s] The id of the actor being determinated for
|
31
31
|
# @param :guid [#to_s] The Anonymous id of the actor being determinated for
|
32
32
|
# @param :properties [Hash<Symbol,String>] The properties of this actor which will be used for including this actor or not
|
33
|
+
# @param :feature [Feature] The feature to use instead of retrieving one
|
33
34
|
# @raise [ArgumentError] When the arguments given to this method aren't ever going to produce a useful response
|
34
35
|
# @return [true,false] Whether the feature is on (true) or off (false) for this actor
|
35
|
-
def feature_flag_on?(name, id: nil, guid: nil, properties: {})
|
36
|
-
determinate_and_notice(name, id: id, guid: guid, properties: properties) do |feature|
|
36
|
+
def feature_flag_on?(name, id: nil, guid: nil, properties: {}, feature: nil)
|
37
|
+
determinate_and_notice(name, id: id, guid: guid, properties: properties, feature: feature) do |feature|
|
37
38
|
feature.feature_flag?
|
38
39
|
end
|
39
40
|
end
|
@@ -44,10 +45,11 @@ module Determinator
|
|
44
45
|
# @param :id [#to_s] The id of the actor being determinated for
|
45
46
|
# @param :guid [#to_s] The Anonymous id of the actor being determinated for
|
46
47
|
# @param :properties [Hash<Symbol,String>] The properties of this actor which will be used for including this actor or not
|
48
|
+
# @param :feature [Feature] The feature to use instead of retrieving one
|
47
49
|
# @raise [ArgumentError] When the arguments given to this method aren't ever going to produce a useful response
|
48
50
|
# @return [false,String] Returns false, if the actor is not in this experiment, or otherwise the variant name.
|
49
|
-
def which_variant(name, id: nil, guid: nil, properties: {})
|
50
|
-
determinate_and_notice(name, id: id, guid: guid, properties: properties) do |feature|
|
51
|
+
def which_variant(name, id: nil, guid: nil, properties: {}, feature: nil)
|
52
|
+
determinate_and_notice(name, id: id, guid: guid, properties: properties, feature: feature) do |feature|
|
51
53
|
feature.experiment?
|
52
54
|
end
|
53
55
|
end
|
@@ -58,6 +60,14 @@ module Determinator
|
|
58
60
|
end
|
59
61
|
end
|
60
62
|
|
63
|
+
# Uses the retrieval (and a cache if set on the Determinator config) to fetch a feature definition.
|
64
|
+
#
|
65
|
+
# @param name [#to_s] The name of the experiment being checked
|
66
|
+
# @return [Feature, MissingResponse] Returns the Feature object, or MissingResponse if the feature is not found.
|
67
|
+
def retrieve(name)
|
68
|
+
Determinator.with_retrieval_cache(name) { retrieval.retrieve(name) }
|
69
|
+
end
|
70
|
+
|
61
71
|
def inspect
|
62
72
|
'#<Determinator::Control>'
|
63
73
|
end
|
@@ -66,8 +76,8 @@ module Determinator
|
|
66
76
|
|
67
77
|
Indicators = Struct.new(:rollout, :variant)
|
68
78
|
|
69
|
-
def determinate_and_notice(name, id:, guid:, properties:)
|
70
|
-
feature
|
79
|
+
def determinate_and_notice(name, id:, guid:, properties:, feature: nil)
|
80
|
+
feature ||= retrieve(name)
|
71
81
|
|
72
82
|
if feature.nil? || feature.is_a?(ErrorResponse) || feature.is_a?(MissingResponse)
|
73
83
|
Determinator.notice_missing_feature(name)
|
@@ -164,6 +174,8 @@ module Determinator
|
|
164
174
|
end
|
165
175
|
|
166
176
|
def choose_fixed_determination(feature, properties)
|
177
|
+
return unless feature.fixed_determinations
|
178
|
+
|
167
179
|
# Keys and values must be strings
|
168
180
|
normalised_properties = normalise_properties(properties)
|
169
181
|
|
@@ -207,6 +219,9 @@ module Determinator
|
|
207
219
|
end
|
208
220
|
|
209
221
|
def matches_constraints(normalised_properties, constraints)
|
222
|
+
unless constraints.all?{ |k, v| k.is_a?(String) && v.all?{ |vv| vv.is_a?(String) } }
|
223
|
+
raise "Constraints must by arrays of strings"
|
224
|
+
end
|
210
225
|
constraints.reduce(true) do |fit, (scope, *required)|
|
211
226
|
present = [*normalised_properties[scope]]
|
212
227
|
fit && matches_requirements?(scope, required, present)
|
@@ -216,6 +231,7 @@ module Determinator
|
|
216
231
|
def matches_requirements?(scope, required, present)
|
217
232
|
case scope
|
218
233
|
when "app_version" then has_any_app_version?(required, present)
|
234
|
+
when "request.app_version" then has_any_app_version?(required, present)
|
219
235
|
else has_any?(required, present)
|
220
236
|
end
|
221
237
|
end
|
data/lib/determinator/feature.rb
CHANGED
@@ -3,9 +3,9 @@ module Determinator
|
|
3
3
|
#
|
4
4
|
# @attr_reader [nil,Hash<String,Integer>] variants The variants for this experiment, with the name of the variant as the key and the weight as the value. Will be nil for non-experiments.
|
5
5
|
class Feature
|
6
|
-
attr_reader :name, :identifier, :bucket_type, :variants, :target_groups, :fixed_determinations, :active, :winning_variant
|
6
|
+
attr_reader :name, :identifier, :bucket_type, :structured_bucket, :variants, :target_groups, :fixed_determinations, :active, :winning_variant
|
7
7
|
|
8
|
-
def initialize(name:, identifier:, bucket_type:, target_groups:, fixed_determinations: [], variants: {}, overrides: {}, active: false, winning_variant: nil)
|
8
|
+
def initialize(name:, identifier:, bucket_type:, target_groups:, structured_bucket: nil, fixed_determinations: [], variants: {}, overrides: {}, active: false, winning_variant: nil)
|
9
9
|
@name = name.to_s
|
10
10
|
@identifier = identifier.to_s
|
11
11
|
@variants = variants
|
@@ -14,6 +14,7 @@ module Determinator
|
|
14
14
|
@winning_variant = parse_outcome(winning_variant, allow_exclusion: false)
|
15
15
|
@active = active
|
16
16
|
@bucket_type = bucket_type.to_sym
|
17
|
+
@structured_bucket = structured_bucket
|
17
18
|
|
18
19
|
# To prevent confusion between actor id data types
|
19
20
|
@overrides = overrides.each_with_object({}) do |(identifier, outcome), hash|
|
@@ -36,6 +37,11 @@ module Determinator
|
|
36
37
|
variants.empty?
|
37
38
|
end
|
38
39
|
|
40
|
+
# @return [true,false] Is this feature using structured identification?
|
41
|
+
def structured?
|
42
|
+
!!structured_bucket && !structured_bucket.empty?
|
43
|
+
end
|
44
|
+
|
39
45
|
# Is this feature overridden for the given actor id?
|
40
46
|
#
|
41
47
|
# @return [true,false] Whether this feature is overridden for this actor
|
@@ -78,7 +84,7 @@ module Determinator
|
|
78
84
|
TargetGroup.new(
|
79
85
|
name: target_group['name'],
|
80
86
|
rollout: target_group['rollout'].to_i,
|
81
|
-
constraints:
|
87
|
+
constraints: constraints
|
82
88
|
)
|
83
89
|
|
84
90
|
# Invalid target groups are ignored
|
@@ -105,17 +111,11 @@ module Determinator
|
|
105
111
|
name: fixed_determination['name'],
|
106
112
|
feature_on: fixed_determination['feature_on'],
|
107
113
|
variant: variant,
|
108
|
-
constraints:
|
114
|
+
constraints: constraints
|
109
115
|
)
|
110
116
|
# Invalid fixed determinations are ignored
|
111
117
|
rescue
|
112
118
|
nil
|
113
119
|
end
|
114
|
-
|
115
|
-
def parse_constraints(constraints)
|
116
|
-
constraints.each_with_object({}) do |(key, value), hash|
|
117
|
-
hash[key.to_s] = [*value].map(&:to_s)
|
118
|
-
end
|
119
|
-
end
|
120
120
|
end
|
121
121
|
end
|
@@ -15,6 +15,7 @@ module Determinator
|
|
15
15
|
name: obj['name'],
|
16
16
|
identifier: obj['identifier'],
|
17
17
|
bucket_type: obj['bucket_type'],
|
18
|
+
structured_bucket: obj['structured_bucket'],
|
18
19
|
active: (obj['active'] === true),
|
19
20
|
target_groups: obj['target_groups'],
|
20
21
|
fixed_determinations: obj['fixed_determinations'].to_a,
|
data/lib/determinator/version.rb
CHANGED
data/lib/rspec/determinator.rb
CHANGED
@@ -3,6 +3,9 @@ require_relative '../determinator/retrieve/in_memory_retriever'
|
|
3
3
|
|
4
4
|
module RSpec
|
5
5
|
module Determinator
|
6
|
+
|
7
|
+
DO_NOT_USE_IN_PRODUCTION_CODE_NULL_FEATURE_CACHE = -> (name, &block) { block.call(name) }
|
8
|
+
|
6
9
|
def self.included(by)
|
7
10
|
by.extend(DSL)
|
8
11
|
|
@@ -12,10 +15,10 @@ module RSpec
|
|
12
15
|
old_retriever = ::Determinator.instance.retrieval
|
13
16
|
begin
|
14
17
|
fake_retriever.clear!
|
15
|
-
::Determinator.configure(retrieval: fake_retriever)
|
18
|
+
::Determinator.configure(retrieval: fake_retriever, feature_cache: DO_NOT_USE_IN_PRODUCTION_CODE_NULL_FEATURE_CACHE)
|
16
19
|
example.run
|
17
20
|
ensure
|
18
|
-
::Determinator.configure(retrieval: old_retriever)
|
21
|
+
::Determinator.configure(retrieval: old_retriever, feature_cache: DO_NOT_USE_IN_PRODUCTION_CODE_NULL_FEATURE_CACHE)
|
19
22
|
end
|
20
23
|
end
|
21
24
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: determinator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- JP Hastings-Spital
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-04-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -42,30 +42,30 @@ dependencies:
|
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: '0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '12.0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '12.0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rspec
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -109,7 +109,7 @@ dependencies:
|
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '4.7'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
112
|
+
name: factory_bot
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
115
|
- - "~>"
|
@@ -150,7 +150,7 @@ dependencies:
|
|
150
150
|
- - ">="
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: '0'
|
153
|
-
description:
|
153
|
+
description:
|
154
154
|
email:
|
155
155
|
- jp@deliveroo.co.uk
|
156
156
|
executables: []
|
@@ -159,6 +159,7 @@ extra_rdoc_files: []
|
|
159
159
|
files:
|
160
160
|
- ".circleci/config.yml"
|
161
161
|
- ".circleci/config.yml.erb"
|
162
|
+
- ".github/workflows/gem-push.yml"
|
162
163
|
- ".gitignore"
|
163
164
|
- ".gitmodules"
|
164
165
|
- ".rspec"
|
@@ -175,7 +176,6 @@ files:
|
|
175
176
|
- docs/background.md
|
176
177
|
- docs/img/determinator.jpg
|
177
178
|
- docs/local_development.md
|
178
|
-
- examples/determinator-rails/.env
|
179
179
|
- examples/determinator-rails/.gitignore
|
180
180
|
- examples/determinator-rails/Gemfile
|
181
181
|
- examples/determinator-rails/Gemfile.lock
|
@@ -202,6 +202,8 @@ files:
|
|
202
202
|
- examples/determinator-rails/config/puma.rb
|
203
203
|
- examples/determinator-rails/config/routes.rb
|
204
204
|
- examples/determinator-rails/config/secrets.yml
|
205
|
+
- examples/determinator-rails/example_features/colloquial_welcome
|
206
|
+
- examples/determinator-rails/example_features/welcome_emoji
|
205
207
|
- examples/determinator-rails/public/favicon.ico
|
206
208
|
- examples/determinator-rails/public/robots.txt
|
207
209
|
- lib/determinator.rb
|
@@ -234,7 +236,7 @@ homepage: https://github.com/deliveroo/determinator
|
|
234
236
|
licenses:
|
235
237
|
- MIT
|
236
238
|
metadata: {}
|
237
|
-
post_install_message:
|
239
|
+
post_install_message:
|
238
240
|
rdoc_options: []
|
239
241
|
require_paths:
|
240
242
|
- lib
|
@@ -249,8 +251,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
249
251
|
- !ruby/object:Gem::Version
|
250
252
|
version: '0'
|
251
253
|
requirements: []
|
252
|
-
rubygems_version: 3.0.
|
253
|
-
signing_key:
|
254
|
+
rubygems_version: 3.0.3
|
255
|
+
signing_key:
|
254
256
|
specification_version: 4
|
255
257
|
summary: Determine which experiments and features a specific actor should see.
|
256
258
|
test_files: []
|
@@ -1,7 +0,0 @@
|
|
1
|
-
ROUTEMASTER_DRAIN_TOKENS=demo
|
2
|
-
ROUTEMASTER_DRAIN_REDIS=redis://localhost/0/routemaster/drain
|
3
|
-
ROUTEMASTER_CACHE_REDIS=redis://localhost/0/routemaster/cache
|
4
|
-
ROUTEMASTER_CACHE_AUTH=
|
5
|
-
ROUTEMASTER_QUEUE_ADAPTER=sidekiq
|
6
|
-
ROUTEMASTER_QUEUE_NAME=routemaster
|
7
|
-
ROUTEMASTER_CALLBACK_URL=https://determinator-example.dev/events
|