determinator 2.4.4 → 2.5.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: 1b5c2494d530c31a915321c8dbf47713e4ddaed50a0d5858a285e020a87bccf1
4
- data.tar.gz: 4e9cad995c047a7c8d12aa7dc8228cddfc4a84891c2fc9283b4d41eb26b3bbdf
3
+ metadata.gz: b505247b6902fb3768389da7b5a20e625f95e5ccaec778ae7fb52e1808286b73
4
+ data.tar.gz: 659b93e86df258535f904bd90d91768767c5c1644cd578d3efe3f45a9c2aba6f
5
5
  SHA512:
6
- metadata.gz: 0727e1e39289a3f89b0d6e20da1139130ca329cdd786af73b7190c3df3f3e958f31d2f92eb09593e00c2503fdb09adf499ac81006baac28094b377e59f7be452
7
- data.tar.gz: ce5085f303357a3d3e70e082b99f6ef986ee1f5c0a27fee7c0b89d7e6196c6702955e2c70db1897f003316cfcbf3934d99a9379cfa1b11a9659321832141a091
6
+ metadata.gz: 753968e66ac00b3f0606eed33b5d1873ad23a89cce901b69e261f38263ff14f18b975a0f03e55146e8ea78dc397b1a48878f10c3b1fc6eadd4f278a78f2a7703
7
+ data.tar.gz: 46d183bc1e87d5031b43115bb63573503725f441a44603577274fef9817cd20722471bcba1b46d018beab4828bbcbf738c4de742aff0de26578178330edd2885
@@ -1,3 +1,8 @@
1
+ # 2.5.0
2
+
3
+ Feature:
4
+ - Add fixed determinations
5
+
1
6
  # 2.4.4
2
7
 
3
8
  Bug fix:
@@ -2,6 +2,7 @@ require 'determinator/version'
2
2
  require 'determinator/control'
3
3
  require 'determinator/feature'
4
4
  require 'determinator/target_group'
5
+ require 'determinator/fixed_determination'
5
6
  require 'determinator/cache/fetch_wrapper'
6
7
  require 'determinator/serializers/json'
7
8
  require 'determinator/missing_response'
@@ -81,6 +81,14 @@ module Determinator
81
81
 
82
82
  return feature.override_value_for(id) if feature.overridden_for?(id)
83
83
 
84
+ fixed_determination = choose_fixed_determination(feature, properties)
85
+ # Given constraints have specified that this actor's determination should be fixed
86
+ if fixed_determination
87
+ return false unless fixed_determination.feature_on
88
+ return true unless feature.experiment?
89
+ return fixed_determination.variant
90
+ end
91
+
84
92
  target_group = choose_target_group(feature, properties)
85
93
  # Given constraints have excluded this actor from this experiment
86
94
  return false unless target_group
@@ -106,23 +114,34 @@ module Determinator
106
114
  false
107
115
  end
108
116
 
117
+ def choose_fixed_determination(feature, properties)
118
+ # Keys and values must be strings
119
+ normalised_properties = normalise_properties(properties)
120
+
121
+ feature.fixed_determinations.find { |fd|
122
+ matches_constraints(normalised_properties, fd.constraints)
123
+ }
124
+ end
125
+
109
126
  def choose_target_group(feature, properties)
110
127
  # Keys and values must be strings
111
- normalised_properties = properties.each_with_object({}) do |(name, values), hash|
112
- hash[name.to_s] = [*values].map(&:to_s)
113
- end
128
+ normalised_properties = normalise_properties(properties)
114
129
 
115
130
  feature.target_groups.select { |tg|
116
131
  next false unless tg.rollout.between?(1, 65_536)
117
132
 
118
- tg.constraints.reduce(true) do |fit, (scope, *required)|
119
- present = [*normalised_properties[scope]]
120
- fit && matches_requirements?(scope, required, present)
121
- end
133
+ matches_constraints(normalised_properties, tg.constraints)
122
134
  # Must choose target group deterministically, if more than one match
123
135
  }.sort_by { |tg| tg.rollout }.last
124
136
  end
125
137
 
138
+ def matches_constraints(normalised_properties, constraints)
139
+ constraints.reduce(true) do |fit, (scope, *required)|
140
+ present = [*normalised_properties[scope]]
141
+ fit && matches_requirements?(scope, required, present)
142
+ end
143
+ end
144
+
126
145
  def matches_requirements?(scope, required, present)
127
146
  case scope
128
147
  when "app_version" then has_any_app_version?(required, present)
@@ -205,5 +224,13 @@ module Determinator
205
224
 
206
225
  raise ArgumentError, "A variant should have been found by this point, there is a bug in the code."
207
226
  end
227
+
228
+ private
229
+
230
+ def normalise_properties(properties)
231
+ properties.each_with_object({}) do |(name, values), hash|
232
+ hash[name.to_s] = [*values].map(&:to_s)
233
+ end
234
+ end
208
235
  end
209
236
  end
@@ -3,13 +3,14 @@ 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, :active, :winning_variant
6
+ attr_reader :name, :identifier, :bucket_type, :variants, :target_groups, :fixed_determinations, :active, :winning_variant
7
7
 
8
- def initialize(name:, identifier:, bucket_type:, target_groups:, variants: {}, overrides: {}, active: false, winning_variant: nil)
8
+ def initialize(name:, identifier:, bucket_type:, target_groups:, fixed_determinations: [], variants: {}, overrides: {}, active: false, winning_variant: nil)
9
9
  @name = name.to_s
10
- @identifier = (identifier || name).to_s
10
+ @identifier = identifier.to_s
11
11
  @variants = variants
12
12
  @target_groups = parse_target_groups(target_groups)
13
+ @fixed_determinations = parse_fixed_determinations(fixed_determinations)
13
14
  @winning_variant = parse_outcome(winning_variant, allow_exclusion: false)
14
15
  @active = active
15
16
  @bucket_type = bucket_type.to_sym
@@ -72,14 +73,43 @@ module Determinator
72
73
 
73
74
  TargetGroup.new(
74
75
  rollout: target_group['rollout'].to_i,
75
- constraints: constraints.each_with_object({}) do |(key, value), hash|
76
- hash[key.to_s] = [*value].map(&:to_s)
77
- end
76
+ constraints: parse_constraints(constraints)
78
77
  )
79
78
 
80
79
  # Invalid target groups are ignored
81
80
  rescue
82
81
  nil
83
82
  end
83
+
84
+ def parse_fixed_determinations(fixed_determinations)
85
+ fixed_determinations.map(&method(:parse_fixed_determination)).compact
86
+ end
87
+
88
+ def parse_fixed_determination(fixed_determination)
89
+ return fixed_determination if fixed_determination.is_a? FixedDetermination
90
+
91
+ variant = fixed_determination['variant']
92
+ return nil if variant && !variants.keys.include?(variant)
93
+
94
+ # if a variant is present the fixed determination should always be on
95
+ return nil if variant && !fixed_determination['feature_on']
96
+
97
+ constraints = fixed_determination['constraints'].to_h
98
+
99
+ FixedDetermination.new(
100
+ feature_on: fixed_determination['feature_on'],
101
+ variant: variant,
102
+ constraints: parse_constraints(constraints)
103
+ )
104
+ # Invalid fixed determinations are ignored
105
+ rescue
106
+ nil
107
+ end
108
+
109
+ def parse_constraints(constraints)
110
+ constraints.each_with_object({}) do |(key, value), hash|
111
+ hash[key.to_s] = [*value].map(&:to_s)
112
+ end
113
+ end
84
114
  end
85
115
  end
@@ -0,0 +1,20 @@
1
+ module Determinator
2
+ class FixedDetermination
3
+ attr_reader :feature_on, :variant, :constraints
4
+
5
+ def initialize(feature_on:, variant:, constraints: {})
6
+ @feature_on = feature_on
7
+ @variant = variant
8
+ @constraints = constraints
9
+ end
10
+
11
+ def inspect
12
+ "<feature_on: #{feature_on}, variant: #{variant}, constraints: #{constraints}"
13
+ end
14
+
15
+ def ==(other)
16
+ return false unless other.is_a?(self.class)
17
+ other.feature_on == feature_on && other.variant == variant && other.constraints == constraints
18
+ end
19
+ end
20
+ end
@@ -12,14 +12,15 @@ module Determinator
12
12
  obj = string_or_hash.is_a?(Hash) ? string_or_hash : ::JSON.parse(string_or_hash)
13
13
 
14
14
  Determinator::Feature.new(
15
- name: obj['name'],
16
- identifier: obj['identifier'],
17
- bucket_type: obj['bucket_type'],
18
- active: (obj['active'] === true),
19
- target_groups: obj['target_groups'],
20
- variants: obj['variants'].to_h,
21
- overrides: obj['overrides'].to_h,
22
- winning_variant: obj['winning_variant'].to_s,
15
+ name: obj['name'],
16
+ identifier: obj['identifier'],
17
+ bucket_type: obj['bucket_type'],
18
+ active: (obj['active'] === true),
19
+ target_groups: obj['target_groups'],
20
+ fixed_determinations: obj['fixed_determinations'].to_a,
21
+ variants: obj['variants'].to_h,
22
+ overrides: obj['overrides'].to_h,
23
+ winning_variant: obj['winning_variant'].to_s,
23
24
  )
24
25
  end
25
26
  end
@@ -1,3 +1,3 @@
1
1
  module Determinator
2
- VERSION = '2.4.4'
2
+ VERSION = '2.5.0'
3
3
  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.4
4
+ version: 2.5.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-05-20 00:00:00.000000000 Z
11
+ date: 2020-06-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -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: []
@@ -210,6 +210,7 @@ files:
210
210
  - lib/determinator/control.rb
211
211
  - lib/determinator/error_response.rb
212
212
  - lib/determinator/feature.rb
213
+ - lib/determinator/fixed_determination.rb
213
214
  - lib/determinator/missing_response.rb
214
215
  - lib/determinator/retrieve/dynaconf.rb
215
216
  - lib/determinator/retrieve/file.rb
@@ -231,7 +232,7 @@ homepage: https://github.com/deliveroo/determinator
231
232
  licenses:
232
233
  - MIT
233
234
  metadata: {}
234
- post_install_message:
235
+ post_install_message:
235
236
  rdoc_options: []
236
237
  require_paths:
237
238
  - lib
@@ -246,8 +247,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
246
247
  - !ruby/object:Gem::Version
247
248
  version: '0'
248
249
  requirements: []
249
- rubygems_version: 3.0.3
250
- signing_key:
250
+ rubygems_version: 3.0.6
251
+ signing_key:
251
252
  specification_version: 4
252
253
  summary: Determine which experiments and features a specific actor should see.
253
254
  test_files: []