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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4466edc3643125ca6b84591b4db51083d215c5c6ea8201694e99d16e403eab24
4
- data.tar.gz: 326d45e3f0ac249e5a358ee3911c1da4da01db81c14ab1b3c827a8fd5d0f4ef9
3
+ metadata.gz: dffc736e777d38ceafbe8ce45d0a01f960341a542a2b93b6ba13b77f1479d291
4
+ data.tar.gz: 7be7ecbd850faac8009e031d41eea1d9679d5f2c43923cbc8ad688c6c1411f3a
5
5
  SHA512:
6
- metadata.gz: c83ad23860f0d5565618e3cfbf1f9975763f3f64c0db49668fa2259aae06c972689e000538a734571906edbf766c8d1d46e58ad2d7c657461c76759768f37bfa
7
- data.tar.gz: 777556d4606d6a10514b5f7c6311e498117845f9bf5bc3bd1be7f8c47cee837fb17243348d11ee0f2fff3931493e97e65cb06608dcbc9b435916aabad07e69f1
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", "~> 2.1.4"
27
- spec.add_development_dependency "rake", "~> 10.0"
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 "factory_girl", "~> 4.8"
31
+ spec.add_development_dependency "factory_bot", "~> 4.8"
32
32
  spec.add_development_dependency 'webmock'
33
- spec.add_development_dependency "sidekiq"
33
+ spec.add_development_dependency 'sidekiq'
34
34
  end
@@ -1,8 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'rails', '~> 5.0.2'
3
+ gem 'rails', '~> 5.1'
4
4
  gem 'puma', '~> 3.0'
5
- gem 'sidekiq'
6
5
 
7
6
  gem 'determinator', path: '../..'
8
7
 
@@ -1,140 +1,144 @@
1
1
  PATH
2
2
  remote: ../..
3
3
  specs:
4
- determinator (1.2.0)
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.0.7)
11
- actionpack (= 5.0.7)
12
- nio4r (>= 1.2, < 3.0)
13
- websocket-driver (~> 0.6.1)
14
- actionmailer (5.0.7)
15
- actionpack (= 5.0.7)
16
- actionview (= 5.0.7)
17
- activejob (= 5.0.7)
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.0.7)
21
- actionview (= 5.0.7)
22
- activesupport (= 5.0.7)
23
- rack (~> 2.0)
24
- rack-test (~> 0.6.3)
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.0.7)
28
- activesupport (= 5.0.7)
28
+ actionview (5.2.4.4)
29
+ activesupport (= 5.2.4.4)
29
30
  builder (~> 3.1)
30
- erubis (~> 2.7.0)
31
+ erubi (~> 1.4)
31
32
  rails-dom-testing (~> 2.0)
32
33
  rails-html-sanitizer (~> 1.0, >= 1.0.3)
33
- activejob (5.0.7)
34
- activesupport (= 5.0.7)
34
+ activejob (5.2.4.4)
35
+ activesupport (= 5.2.4.4)
35
36
  globalid (>= 0.3.6)
36
- activemodel (5.0.7)
37
- activesupport (= 5.0.7)
38
- activerecord (5.0.7)
39
- activemodel (= 5.0.7)
40
- activesupport (= 5.0.7)
41
- arel (~> 7.0)
42
- activesupport (5.0.7)
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 (7.1.4)
48
- builder (3.2.3)
49
- byebug (10.0.2)
50
- concurrent-ruby (1.0.5)
51
- connection_pool (2.2.2)
52
- crass (1.0.4)
53
- dotenv (2.4.0)
54
- dotenv-rails (2.4.0)
55
- dotenv (= 2.4.0)
56
- railties (>= 3.2, < 6.0)
57
- erubis (2.7.0)
58
- faraday (0.15.4)
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
- globalid (0.4.1)
65
+ ruby2_keywords
66
+ faraday-net_http (1.0.1)
67
+ globalid (0.4.2)
61
68
  activesupport (>= 4.2.0)
62
- i18n (1.0.1)
69
+ i18n (1.8.7)
63
70
  concurrent-ruby (~> 1.0)
64
- loofah (2.2.2)
71
+ loofah (2.9.0)
65
72
  crass (~> 1.0.2)
66
73
  nokogiri (>= 1.5.9)
67
- mail (2.7.0)
74
+ mail (2.7.1)
68
75
  mini_mime (>= 0.1.1)
69
- method_source (0.9.0)
70
- mini_mime (1.0.0)
71
- mini_portile2 (2.3.0)
72
- minitest (5.11.3)
73
- multipart-post (2.0.0)
74
- nio4r (2.3.1)
75
- nokogiri (1.8.2)
76
- mini_portile2 (~> 2.3.0)
77
- puma (3.11.4)
78
- rack (2.0.5)
79
- rack-protection (2.0.3)
80
- rack
81
- rack-test (0.6.3)
82
- rack (>= 1.0)
83
- rails (5.0.7)
84
- actioncable (= 5.0.7)
85
- actionmailer (= 5.0.7)
86
- actionpack (= 5.0.7)
87
- actionview (= 5.0.7)
88
- activejob (= 5.0.7)
89
- activemodel (= 5.0.7)
90
- activerecord (= 5.0.7)
91
- activesupport (= 5.0.7)
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.0.7)
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.4)
99
- loofah (~> 2.2, >= 2.2.2)
100
- railties (5.0.7)
101
- actionpack (= 5.0.7)
102
- activesupport (= 5.0.7)
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.18.1, < 2.0)
106
- rake (12.3.1)
107
- redis (4.0.1)
108
- sidekiq (5.1.3)
109
- concurrent-ruby (~> 1.0)
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.1)
121
+ sprockets-rails (3.2.2)
117
122
  actionpack (>= 4.0)
118
123
  activesupport (>= 4.0)
119
124
  sprockets (>= 3.0.0)
120
- thor (0.20.0)
125
+ thor (1.1.0)
121
126
  thread_safe (0.3.6)
122
- tzinfo (1.2.5)
127
+ tzinfo (1.2.9)
123
128
  thread_safe (~> 0.1)
124
- websocket-driver (0.6.5)
129
+ websocket-driver (0.7.3)
125
130
  websocket-extensions (>= 0.1.0)
126
- websocket-extensions (0.1.3)
131
+ websocket-extensions (0.1.5)
127
132
 
128
133
  PLATFORMS
129
- ruby
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.0.2)
137
- sidekiq
141
+ rails (~> 5.1)
138
142
 
139
143
  BUNDLED WITH
140
- 1.17.3
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
- render json: { welcome: message }
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/dynaconf'
1
+ require 'determinator/retrieve/file'
2
2
  require 'active_support/cache'
3
3
 
4
- retrieval = Determinator::Retrieve::Dynaconf.new(base_url: 'http://localhost:2345', service_name: 'determinator-rails')
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
  )
@@ -1,3 +1,2 @@
1
1
  ActiveSupport.to_time_preserves_timezone = true
2
- ActiveSupport.halt_callback_chains_on_return_false = false
3
2
  Rails.application.config.ssl_options = { hsts: { subdomains: true } }
@@ -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
- # @param :feature_cache [#call, nil] a caching proc, accepting a feature name, which will return the named feature or yield (and store) if not available
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 if feature_cache.respond_to?(:call)
24
+ @feature_cache = feature_cache
25
25
  @retrieval = retrieval
26
26
  @instance = Control.new(retrieval: retrieval)
27
27
  end
@@ -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 = Determinator.with_retrieval_cache(name) { retrieval.retrieve(name) }
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
@@ -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: parse_constraints(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: parse_constraints(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,
@@ -1,3 +1,3 @@
1
1
  module Determinator
2
- VERSION = '2.5.1'
2
+ VERSION = '2.7.0'
3
3
  end
@@ -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.5.1
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: 2020-06-11 00:00:00.000000000 Z
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: 2.1.4
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: 2.1.4
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: '10.0'
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: '10.0'
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: factory_girl
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.6
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