unleash 4.4.3 → 4.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: 910ddad70479cae68918b7400b4ddc6d0c71f9448d8524072c318d6c353c4f52
4
- data.tar.gz: 1d75ed31736627ea30c2bbc9be7903c9dfa19dc4691c07de885dd87a8eb94c85
3
+ metadata.gz: b823e18f3a0768928856433b11674d37ea62cc7f2b8b15786920488aba5c063c
4
+ data.tar.gz: 6c8ec27517214b441c711d2f0069c211492ce3185bf4f1741c7b07077a28b088
5
5
  SHA512:
6
- metadata.gz: aec46ef764fcb138d34baa7c6832ff1865be4dac1f6f004ab928e8447ecde701ec82bf2069c69e62b2404706bf029c1743a3dc2322b5e7409135aba498cdf80e
7
- data.tar.gz: d1a529a53cbd934c4aae24919352fc670b27b8cb0a9ccb2bde20deafa3eb3adf679e7c49ced246258547f68a400a10a1da214bc07a19e153ffb0af60fcea8961
6
+ metadata.gz: a324e1902ae4e3885a206531e25d6138db8e39fb50b946471d0dcce3aec1ce595c2743b79ad3519af7cb80e83a284428d16bb2f3385313f889938f2ded80feef
7
+ data.tar.gz: b89d72bb1b7a0fdd6ba65fe92ab1909ec5b02742d6e2d2447d047ed0d3325efe29e0e0f033ad4ff0ad12e80041a2a8697b64e366dd185077953be59073c8e801
@@ -50,7 +50,7 @@ jobs:
50
50
  - name: Install dependencies
51
51
  run: bundle install
52
52
  - name: Download test cases
53
- run: git clone --depth 5 --branch v4.2.2 https://github.com/Unleash/client-specification.git client-specification
53
+ run: git clone --depth 5 --branch v4.3.1 https://github.com/Unleash/client-specification.git client-specification
54
54
  - name: Run tests
55
55
  run: bundle exec rake
56
56
  env:
data/CHANGELOG.md ADDED
@@ -0,0 +1,116 @@
1
+
2
+ # Changelog
3
+
4
+ All notable changes to this project will be documented in this file.
5
+
6
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
7
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
8
+
9
+ Note: These changes are not considered notable:
10
+ - build
11
+ - documentation
12
+ - dependencies
13
+
14
+ ## [Unreleased]
15
+
16
+ ## [4.5.0] - 2023-07-05
17
+ ### Added
18
+ - variants in strategies (#148)
19
+ - issue described here (#147)
20
+
21
+ ### Fixed
22
+ - groupId override for variants
23
+
24
+ ## [4.4.4] - 2023-07-05
25
+ ### Fixed
26
+ - flexible rollout strategy without context (#146)
27
+ - The flexible rollout strategy should evaluate default and random stickiness even if context is not provided.
28
+
29
+ ## [4.4.3] - 2023-06-14
30
+ ### Added
31
+ - Add Context#to_h method (#136)
32
+
33
+ ### Fixed
34
+ - Bootstrapped variants now work when client is disabled (#138)
35
+ - Variant metrics are now sent correctly to Unleash. Fixed a typo in the payload name. (#145)
36
+
37
+ ### Changed
38
+ - Automatically disable metrics/MetricsReporter when client is disabled (#139) (#140)
39
+
40
+ ## [4.4.2] - 2023-01-05
41
+ ### Added
42
+ - Add Client#disabled? method (#130)
43
+
44
+ ## [4.4.1] - 2022-12-07
45
+ ### Fixed
46
+ - exception no longer bubbles up in constraints when context is nil (#127)
47
+ - variants metrics did count toggles correctly (#126)
48
+ - prevent race condition when manipulating metrics data (#122)
49
+ - allow passing user_id as integer (#119)
50
+
51
+ ## [4.4.0] - 2022-09-19
52
+ ### Added
53
+ - Allow custom strategies (#96)
54
+ - Global segments (#114)
55
+
56
+ ### Fixed
57
+ - Initializing client configuration from constructor (#117)
58
+ - Support int context in set comparison (#115)
59
+
60
+ ## [4.3.0] - 2023-07-14
61
+ ### Added
62
+ - dynamic http headers via Proc or Lambda (#107)
63
+
64
+ ### Fixed
65
+ - Fixed ToggleFetcher#save! to close opened files on failure. (#97)
66
+
67
+ ### Changed
68
+ - Refactored ToggleFetcher#read! (#106)
69
+
70
+ ## [4.2.1] - 2022-03-29
71
+ ### Fixed
72
+ - correct logic for default values on feature toggles so toggle value respected when toggle exists and default is true (#93)
73
+
74
+ ## [4.2.0] - 2022-03-18
75
+ ### Added
76
+ - Advanced constraints operators (#92)
77
+
78
+ ### Changed
79
+ - Default to the client never giving up trying to reach the server even after repeated failures (#91)
80
+
81
+ ## [4.1.0] - 2022-02-11
82
+ ### Added
83
+ - feat: Implement custom bootstrapping on startup (#88)
84
+ - feat: add support for cidr in `RemoteAddress` strategy (#77)
85
+
86
+ ### Changed
87
+ - default values for `metrics_interval` to `60s` and `retry_limit` to `5` (#78)
88
+
89
+ ## [4.0.0] - 2021-12-16
90
+ ### Added
91
+ - Support for projects query (requires unleash 4.x) (#38)
92
+ - Allow passing blocks to is_enabled? to determine default_result (#33)
93
+ - Implement custom stickiness (#69)
94
+ - Allow using custom_http_headers from the CLI utility (#75)
95
+
96
+ ### Fixed
97
+ - Allow context to correctly resolve camelCase property values (#74)
98
+ - Avoid unlikely situation of config changing under the read operation due to backup path file being incorrectly set (#63)
99
+
100
+ ### Changed
101
+ - change how we handle the server api url (avoid double slashes in urls used for API calls.)
102
+ - default values: refresh_interval => 10, metrics_interval=> 30 (#59)
103
+ - changed metrics reporting behavior (#66)
104
+ - only send metrics if there is data to send. (#58)
105
+ - in Client#get_variant() allow context and fallback_variant as nil (#51)
106
+
107
+ [unreleased]: https://git1hub.com/unleash/unleash-client-ruby/compare/v4.4.3...HEAD
108
+ [4.4.3]: https://github.com/unleash/unleash-client-ruby/compare/v4.4.2...v4.4.3
109
+ [4.4.2]: https://github.com/unleash/unleash-client-ruby/compare/v4.4.1...v4.4.2
110
+ [4.4.1]: https://github.com/unleash/unleash-client-ruby/compare/v4.4.0...v4.4.1
111
+ [4.4.0]: https://github.com/unleash/unleash-client-ruby/compare/v4.3.0...v4.4.0
112
+ [4.3.0]: https://github.com/unleash/unleash-client-ruby/compare/v4.2.1...v4.3.0
113
+ [4.2.1]: https://github.com/unleash/unleash-client-ruby/compare/v4.2.0...v4.2.1
114
+ [4.2.0]: https://github.com/unleash/unleash-client-ruby/compare/v4.1.0...v4.2.0
115
+ [4.1.0]: https://github.com/unleash/unleash-client-ruby/compare/v4.0.0...v4.1.0
116
+ [4.0.0]: https://github.com/unleash/unleash-client-ruby/compare/v3.2.5...v4.0.0
data/README.md CHANGED
@@ -25,7 +25,7 @@ Leverage the [Unleash Server](https://github.com/Unleash/unleash) for powerful f
25
25
  Add this line to your application's Gemfile:
26
26
 
27
27
  ```ruby
28
- gem 'unleash', '~> 4.4.0'
28
+ gem 'unleash', '~> 4.5.0'
29
29
  ```
30
30
 
31
31
  And then execute:
@@ -528,16 +528,30 @@ You can also run `bin/console` for an interactive prompt that will allow you to
528
528
  This SDK is also built against the Unleash Client Specification tests.
529
529
  To run the Ruby SDK against this test suite, you'll need to have a copy on your machine, you can clone the repository directly using:
530
530
 
531
- `git clone --depth 5 --branch v4.2.2 https://github.com/Unleash/client-specification.git client-specification`
531
+ `git clone --depth 5 --branch v4.3.1 https://github.com/Unleash/client-specification.git client-specification`
532
532
 
533
533
  After doing this, `rake spec` will also run the client specification tests.
534
534
 
535
535
  To install this gem onto your local machine, run `bundle exec rake install`.
536
- To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
536
+
537
+ ## Releasing
538
+
539
+ Choose a new version number following [Semantic Versioning](https://semver.org/spec/v2.0.0.html) semantics and then:
540
+
541
+ - update the version number in [./lib/unleash/version.rb](./lib/unleash/version.rb),
542
+ - if a major or minor version bump, update the [Installation section](#Installation) in [README.md](README.md)
543
+ - update [CHANGELOG.md](CHANGELOG.md) following the format on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
544
+ - commit with message `chore: bump version to x.y.z`
545
+ - then run `bundle exec rake release`
546
+ - This will create a git tag for the version on the current commit,
547
+ - push git commits and tags to origin and
548
+ - push the `.gem` file to [rubygems.org](https://rubygems.org)
537
549
 
538
550
 
539
551
  ## Contributing
540
552
 
541
553
  Bug reports and pull requests are welcome on GitHub at https://github.com/unleash/unleash-client-ruby.
542
554
 
555
+ Be sure to run both `bundle exec rspec` and `bundle exec rubocop` in your branch before creating a pull request.
556
+
543
557
  Please include tests with any pull requests, to avoid regressions.
@@ -1,8 +1,8 @@
1
1
  module Unleash
2
2
  class ActivationStrategy
3
- attr_accessor :name, :params, :constraints, :disabled
3
+ attr_accessor :name, :params, :constraints, :disabled, :variant_definitions
4
4
 
5
- def initialize(name, params, constraints = [])
5
+ def initialize(name, params, constraints = [], variant_definitions = [])
6
6
  self.name = name
7
7
  self.disabled = false
8
8
 
@@ -15,17 +15,30 @@ module Unleash
15
15
  self.params = {}
16
16
  end
17
17
 
18
- if constraints.is_a?(Array) && constraints.each{ |c| c.is_a?(Constraint) }
18
+ if constraints.is_a?(Array) && constraints.all?{ |c| c.is_a?(Constraint) }
19
19
  self.constraints = constraints
20
20
  else
21
- Unleash.logger.warn "Invalid constraints provided for ActivationStrategy (contraints: #{constraints})"
21
+ Unleash.logger.warn "Invalid constraints provided for ActivationStrategy (constraints: #{constraints})"
22
22
  self.disabled = true
23
23
  self.constraints = []
24
24
  end
25
+
26
+ self.variant_definitions = valid_variant_definitions(variant_definitions)
25
27
  end
26
28
 
27
29
  def matches_context?(context)
28
30
  self.constraints.any?{ |c| c.matches_context? context }
29
31
  end
32
+
33
+ private
34
+
35
+ def valid_variant_definitions(variant_definitions)
36
+ if variant_definitions.is_a?(Array) && variant_definitions.all?{ |variant_definition| variant_definition.is_a?(VariantDefinition) }
37
+ variant_definitions
38
+ else
39
+ Unleash.logger.warn "Invalid variant_definitions provided for ActivationStrategy (variant_definitions: #{variant_definitions})"
40
+ []
41
+ end
42
+ end
30
43
  end
31
44
  end
@@ -9,6 +9,8 @@ module Unleash
9
9
  class FeatureToggle
10
10
  attr_accessor :name, :enabled, :strategies, :variant_definitions
11
11
 
12
+ FeatureEvaluationResult = Struct.new(:enabled?, :strategy)
13
+
12
14
  def initialize(params = {}, segment_map = {})
13
15
  params = {} if params.nil?
14
16
 
@@ -37,10 +39,13 @@ module Unleash
37
39
 
38
40
  context = ensure_valid_context(context)
39
41
 
40
- toggle_enabled = am_enabled?(context)
41
- variant = resolve_variant(context, toggle_enabled)
42
+ evaluation_result = evaluate(context)
43
+
44
+ group_id = evaluation_result[:strategy]&.params.to_h['groupId'] || self.name
45
+
46
+ variant = resolve_variant(context, evaluation_result, group_id)
42
47
 
43
- choice = toggle_enabled ? :yes : :no
48
+ choice = evaluation_result[:enabled?] ? :yes : :no
44
49
  Unleash.toggle_metrics.increment_variant(self.name, choice, variant.name) unless Unleash.configuration.disable_metrics
45
50
  variant
46
51
  end
@@ -51,33 +56,40 @@ module Unleash
51
56
 
52
57
  private
53
58
 
54
- def resolve_variant(context, toggle_enabled)
55
- return Unleash::FeatureToggle.disabled_variant unless toggle_enabled
56
- return Unleash::FeatureToggle.disabled_variant if sum_variant_defs_weights <= 0
59
+ def resolve_variant(context, evaluation_result, group_id)
60
+ variant_definitions = evaluation_result[:strategy]&.variant_definitions
61
+ variant_definitions = self.variant_definitions if variant_definitions.nil? || variant_definitions.empty?
62
+ return Unleash::FeatureToggle.disabled_variant unless evaluation_result[:enabled?]
63
+ return Unleash::FeatureToggle.disabled_variant if sum_variant_defs_weights(variant_definitions) <= 0
57
64
 
58
- variant_from_override_match(context) || variant_from_weights(context, resolve_stickiness)
65
+ variant_from_override_match(context, variant_definitions) ||
66
+ variant_from_weights(context, resolve_stickiness(variant_definitions), variant_definitions, group_id)
59
67
  end
60
68
 
61
- def resolve_stickiness
62
- self.variant_definitions&.map(&:stickiness)&.compact&.first || "default"
69
+ def resolve_stickiness(variant_definitions)
70
+ variant_definitions&.map(&:stickiness)&.compact&.first || "default"
63
71
  end
64
72
 
65
73
  # only check if it is enabled, do not do metrics
66
74
  def am_enabled?(context)
67
- result =
68
- if self.enabled
69
- self.strategies.empty? ||
70
- self.strategies.any? do |s|
71
- strategy_enabled?(s, context) && strategy_constraint_matches?(s, context)
72
- end
75
+ evaluate(context)[:enabled?]
76
+ end
77
+
78
+ def evaluate(context)
79
+ evaluation_result =
80
+ if !self.enabled
81
+ FeatureEvaluationResult.new(false, nil)
82
+ elsif self.strategies.empty?
83
+ FeatureEvaluationResult.new(true, nil)
73
84
  else
74
- false
85
+ strategy = self.strategies.find{ |s| strategy_enabled?(s, context) && strategy_constraint_matches?(s, context) }
86
+ FeatureEvaluationResult.new(!strategy.nil?, strategy)
75
87
  end
76
88
 
77
89
  Unleash.logger.debug "Unleash::FeatureToggle (enabled:#{self.enabled} " \
78
- "and Strategies combined with contraints returned #{result})"
90
+ "and Strategies combined with contraints returned #{evaluation_result})"
79
91
 
80
- result
92
+ evaluation_result
81
93
  end
82
94
 
83
95
  def strategy_enabled?(strategy, context)
@@ -92,8 +104,8 @@ module Unleash
92
104
  strategy.constraints.empty? || strategy.constraints.all?{ |c| c.matches_context?(context) }
93
105
  end
94
106
 
95
- def sum_variant_defs_weights
96
- self.variant_definitions.map(&:weight).reduce(0, :+)
107
+ def sum_variant_defs_weights(variant_definitions)
108
+ variant_definitions.map(&:weight).reduce(0, :+)
97
109
  end
98
110
 
99
111
  def variant_salt(context, stickiness = "default")
@@ -110,18 +122,22 @@ module Unleash
110
122
  SecureRandom.random_number
111
123
  end
112
124
 
113
- def variant_from_override_match(context)
114
- variant = self.variant_definitions.find{ |vd| vd.override_matches_context?(context) }
115
- return nil if variant.nil?
125
+ def variant_from_override_match(context, variant_definitions)
126
+ variant_definition = variant_definitions.find{ |vd| vd.override_matches_context?(context) }
127
+ return nil if variant_definition.nil?
116
128
 
117
- Unleash::Variant.new(name: variant.name, enabled: true, payload: variant.payload)
129
+ Unleash::Variant.new(name: variant_definition.name, enabled: true, payload: variant_definition.payload)
118
130
  end
119
131
 
120
- def variant_from_weights(context, stickiness)
121
- variant_weight = Unleash::Strategy::Util.get_normalized_number(variant_salt(context, stickiness), self.name, sum_variant_defs_weights)
132
+ def variant_from_weights(context, stickiness, variant_definitions, group_id)
133
+ variant_weight = Unleash::Strategy::Util.get_normalized_number(
134
+ variant_salt(context, stickiness),
135
+ group_id,
136
+ sum_variant_defs_weights(variant_definitions)
137
+ )
122
138
  prev_weights = 0
123
139
 
124
- variant_definition = self.variant_definitions
140
+ variant_definition = variant_definitions
125
141
  .find do |v|
126
142
  res = (prev_weights + v.weight >= variant_weight)
127
143
  prev_weights += v.weight
@@ -148,11 +164,26 @@ module Unleash
148
164
  ActivationStrategy.new(
149
165
  s['name'],
150
166
  s['parameters'],
151
- resolve_constraints(s, segment_map)
167
+ resolve_constraints(s, segment_map),
168
+ resolve_variants(s)
152
169
  )
153
170
  end || []
154
171
  end
155
172
 
173
+ def resolve_variants(strategy)
174
+ strategy.fetch("variants", [])
175
+ .select{ |variant| variant.is_a?(Hash) && variant.has_key?("name") }
176
+ .map do |variant|
177
+ VariantDefinition.new(
178
+ variant.fetch("name", ""),
179
+ variant.fetch("weight", 0),
180
+ variant.fetch("payload", nil),
181
+ variant.fetch("stickiness", nil),
182
+ variant.fetch("overrides", [])
183
+ )
184
+ end
185
+ end
186
+
156
187
  def resolve_constraints(strategy, segment_map)
157
188
  segment_constraints = (strategy["segments"] || []).map do |segment_id|
158
189
  segment_map[segment_id]&.fetch("constraints")
@@ -10,9 +10,10 @@ module Unleash
10
10
  # need: params['percentage']
11
11
  def is_enabled?(params = {}, context = nil)
12
12
  return false unless params.is_a?(Hash)
13
- return false unless context.instance_of?(Unleash::Context)
14
13
 
15
14
  stickiness = params.fetch('stickiness', 'default')
15
+ return false if context_invalid?(stickiness, context)
16
+
16
17
  stickiness_id = resolve_stickiness(stickiness, context)
17
18
 
18
19
  begin
@@ -32,6 +33,12 @@ module Unleash
32
33
 
33
34
  private
34
35
 
36
+ def context_invalid?(stickiness, context)
37
+ return false if ['random', 'default'].include?(stickiness)
38
+
39
+ !context.instance_of?(Unleash::Context)
40
+ end
41
+
35
42
  def random
36
43
  Random.rand(0..100)
37
44
  end
@@ -41,7 +48,9 @@ module Unleash
41
48
  when 'random'
42
49
  random
43
50
  when 'default'
44
- context.user_id || context.session_id || random
51
+ return random unless context.instance_of?(Unleash::Context)
52
+
53
+ context&.user_id || context&.session_id || random
45
54
  else
46
55
  begin
47
56
  context.get_by_name(stickiness)
@@ -1,3 +1,3 @@
1
1
  module Unleash
2
- VERSION = "4.4.3".freeze
2
+ VERSION = "4.5.0".freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unleash
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.4.3
4
+ version: 4.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Renato Arruda
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-14 00:00:00.000000000 Z
11
+ date: 2023-08-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: murmurhash3
@@ -152,6 +152,7 @@ files:
152
152
  - ".gitignore"
153
153
  - ".rspec"
154
154
  - ".rubocop.yml"
155
+ - CHANGELOG.md
155
156
  - Gemfile
156
157
  - LICENSE
157
158
  - README.md
@@ -199,7 +200,7 @@ homepage: https://github.com/unleash/unleash-client-ruby
199
200
  licenses:
200
201
  - Apache-2.0
201
202
  metadata: {}
202
- post_install_message:
203
+ post_install_message:
203
204
  rdoc_options: []
204
205
  require_paths:
205
206
  - lib
@@ -214,8 +215,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
214
215
  - !ruby/object:Gem::Version
215
216
  version: '0'
216
217
  requirements: []
217
- rubygems_version: 3.4.10
218
- signing_key:
218
+ rubygems_version: 3.3.15
219
+ signing_key:
219
220
  specification_version: 4
220
221
  summary: Unleash feature toggle client.
221
222
  test_files: []