determinator 2.4.4 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
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: []