unleash 4.3.0 → 4.4.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: f05a01bdf6013184b6948fb5c38f6b1da1cde9d18f9a0eeecd69b54cbed8ff18
4
- data.tar.gz: 6343830fd945d00decd331acade205d605e0d496ed1d4afd4736affcb2f538ca
3
+ metadata.gz: 5788566712ad3909e6707041673d359a4d9835b59c79bf0e0fac80c7c8a164ed
4
+ data.tar.gz: 707f6875e73a10ef24fdce900aa7c179b0762eedda07ce83a41ea5ed76ba494f
5
5
  SHA512:
6
- metadata.gz: 1ac4a4b89f787b5ae27acda02865def0eb68f261da10b83c01a6b34f6706b1b29443375eb82418b9834db555fbe54c1a1dfe49e9bf4c761556145edce88f76c9
7
- data.tar.gz: c7a2332c7e6ab54872b08432af4c5c2cf092b7586664eb0267e45e416fb9a72c46246dd6cdedf68b4a9421678e4cb9f9507365e40fc5c2113b9fba8e703a19dd
6
+ metadata.gz: 5d836f1c130e5db08fdac81f75cb938e93c060f997946ec80bd5819ad098a9b6d7a6d5628f88cd70144899bbe72ac1f5a1e802b4973b6ee0d24eda85e783fab3
7
+ data.tar.gz: 69280e8af54ba2613ef3ae17206f0dfbd31a958d2595162dab71dd0d4cf5815d4dc3e023160c1edc1dbcaeea77d2cfd5f2e468af28051fe41b51dd77fae0f689
@@ -46,7 +46,7 @@ jobs:
46
46
  - name: Install dependencies
47
47
  run: bundle install
48
48
  - name: Download test cases
49
- run: git clone --depth 5 --branch v4.1.0 https://github.com/Unleash/client-specification.git client-specification
49
+ run: git clone --depth 5 --branch v4.2.2 https://github.com/Unleash/client-specification.git client-specification
50
50
  - name: Run tests
51
51
  run: bundle exec rake
52
52
  env:
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
1
  --format documentation
2
2
  --color
3
+ --require 'spec_helper'
data/.rubocop.yml CHANGED
@@ -9,7 +9,7 @@ Naming/PredicateName:
9
9
 
10
10
 
11
11
  Metrics/ClassLength:
12
- Max: 125
12
+ Max: 130
13
13
  Layout/LineLength:
14
14
  Max: 140
15
15
  Metrics/MethodLength:
@@ -34,6 +34,9 @@ Style/StringLiterals:
34
34
  Style/RedundantSelf:
35
35
  Enabled: false
36
36
 
37
+ Style/OptionalBooleanParameter:
38
+ Enabled: false
39
+
37
40
  Style/SymbolArray:
38
41
  EnforcedStyle: brackets
39
42
  Style/WordArray:
data/README.md CHANGED
@@ -97,6 +97,7 @@ Argument | Description | Required? | Type | Default Value|
97
97
  `logger` | Specify a custom `Logger` class to handle logs for the Unleash client. | N | Class | `Logger.new(STDOUT)` |
98
98
  `log_level` | Change the log level for the `Logger` class. Constant from `Logger::Severity`. | N | Constant | `Logger::WARN` |
99
99
  `bootstrap_config` | Bootstrap config on how to loaded data on start-up. This is useful for loading large states on startup without (or before) hitting the network. | N | Unleash::Bootstrap::Configuration | `nil` |
100
+ `strategies` | Strategies manager that holds all strategies and allows to add custom strategies | N | Unleash::Strategies | `Unleash::Strategies.new` |
100
101
 
101
102
  For a more in-depth look, please see `lib/unleash/configuration.rb`.
102
103
 
@@ -472,6 +473,27 @@ This client comes with the all the required strategies out of the box:
472
473
  * UnknownStrategy
473
474
  * UserWithIdStrategy
474
475
 
476
+ ## Custom Strategies
477
+
478
+ Client allows to add [custom activation strategies](https://docs.getunleash.io/advanced/custom_activation_strategy) using configuration.
479
+ In order for strategy to work correctly it should support two methods `name` and `is_enabled?`
480
+
481
+ ```ruby
482
+ class MyCustomStrategy
483
+ def name
484
+ 'muCustomStrategy'
485
+ end
486
+
487
+ def is_enabled?(params = {}, context = nil)
488
+ true
489
+ end
490
+ end
491
+
492
+ Unleash.configure do |config|
493
+ config.strategies.add(MyCustomStrategy.new)
494
+ end
495
+ ```
496
+
475
497
  ## Development
476
498
 
477
499
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -1,9 +1,10 @@
1
1
  module Unleash
2
2
  class ActivationStrategy
3
- attr_accessor :name, :params, :constraints
3
+ attr_accessor :name, :params, :constraints, :disabled
4
4
 
5
5
  def initialize(name, params, constraints = [])
6
6
  self.name = name
7
+ self.disabled = false
7
8
 
8
9
  if params.is_a?(Hash)
9
10
  self.params = params
@@ -18,6 +19,7 @@ module Unleash
18
19
  self.constraints = constraints
19
20
  else
20
21
  Unleash.logger.warn "Invalid constraints provided for ActivationStrategy (contraints: #{constraints})"
22
+ self.disabled = true
21
23
  self.constraints = []
22
24
  end
23
25
  end
@@ -12,7 +12,7 @@ module Unleash
12
12
  attr_accessor :fetcher_scheduled_executor, :metrics_scheduled_executor
13
13
 
14
14
  def initialize(*opts)
15
- Unleash.configuration ||= Unleash::Configuration.new(*opts)
15
+ Unleash.configuration = Unleash::Configuration.new(*opts) unless opts.empty?
16
16
  Unleash.configuration.validate!
17
17
 
18
18
  Unleash.logger = Unleash.configuration.logger.clone
@@ -45,7 +45,7 @@ module Unleash
45
45
  return default_value
46
46
  end
47
47
 
48
- toggle = Unleash::FeatureToggle.new(toggle_as_hash)
48
+ toggle = Unleash::FeatureToggle.new(toggle_as_hash, Unleash&.segment_cache)
49
49
 
50
50
  toggle.is_enabled?(context)
51
51
  end
@@ -110,7 +110,7 @@ module Unleash
110
110
  'appName': Unleash.configuration.app_name,
111
111
  'instanceId': Unleash.configuration.instance_id,
112
112
  'sdkVersion': "unleash-client-ruby:" + Unleash::VERSION,
113
- 'strategies': Unleash::STRATEGIES.keys,
113
+ 'strategies': Unleash.strategies.keys,
114
114
  'started': Time.now.iso8601(Unleash::TIME_RESOLUTION),
115
115
  'interval': Unleash.configuration.metrics_interval_in_millis
116
116
  }
@@ -20,7 +20,8 @@ module Unleash
20
20
  :backup_file,
21
21
  :logger,
22
22
  :log_level,
23
- :bootstrap_config
23
+ :bootstrap_config,
24
+ :strategies
24
25
 
25
26
  def initialize(opts = {})
26
27
  validate_custom_http_headers!(opts[:custom_http_headers]) if opts.has_key?(:custom_http_headers)
@@ -51,7 +52,8 @@ module Unleash
51
52
  def http_headers
52
53
  {
53
54
  'UNLEASH-INSTANCEID' => self.instance_id,
54
- 'UNLEASH-APPNAME' => self.app_name
55
+ 'UNLEASH-APPNAME' => self.app_name,
56
+ 'Unleash-Client-Spec' => '4.2.2'
55
57
  }.merge!(generate_custom_http_headers)
56
58
  end
57
59
 
@@ -94,12 +96,13 @@ module Unleash
94
96
  self.backup_file = nil
95
97
  self.log_level = Logger::WARN
96
98
  self.bootstrap_config = nil
99
+ self.strategies = Unleash::Strategies.new
97
100
 
98
101
  self.custom_http_headers = {}
99
102
  end
100
103
 
101
104
  def initialize_default_logger
102
- self.logger = Logger.new(STDOUT)
105
+ self.logger = Logger.new($stdout)
103
106
 
104
107
  # on default logger, use custom formatter that includes thread_name:
105
108
  self.logger.formatter = proc do |severity, datetime, _progname, msg|
@@ -1,12 +1,11 @@
1
1
  require 'date'
2
-
3
2
  module Unleash
4
3
  class Constraint
5
4
  attr_accessor :context_name, :operator, :value, :inverted, :case_insensitive
6
5
 
7
6
  OPERATORS = {
8
- IN: ->(context_v, constraint_v){ constraint_v.include? context_v },
9
- NOT_IN: ->(context_v, constraint_v){ !constraint_v.include? context_v },
7
+ IN: ->(context_v, constraint_v){ constraint_v.include? context_v.to_s },
8
+ NOT_IN: ->(context_v, constraint_v){ !constraint_v.include? context_v.to_s },
10
9
  STR_STARTS_WITH: ->(context_v, constraint_v){ constraint_v.any?{ |v| context_v.start_with? v } },
11
10
  STR_ENDS_WITH: ->(context_v, constraint_v){ constraint_v.any?{ |v| context_v.end_with? v } },
12
11
  STR_CONTAINS: ->(context_v, constraint_v){ constraint_v.any?{ |v| context_v.include? v } },
@@ -19,16 +18,21 @@ module Unleash
19
18
  DATE_BEFORE: ->(context_v, constraint_v){ on_valid_date(constraint_v, context_v){ |x, y| (x > y) } },
20
19
  SEMVER_EQ: ->(context_v, constraint_v){ on_valid_version(constraint_v, context_v){ |x, y| (x == y) } },
21
20
  SEMVER_GT: ->(context_v, constraint_v){ on_valid_version(constraint_v, context_v){ |x, y| (x < y) } },
22
- SEMVER_LT: ->(context_v, constraint_v){ on_valid_version(constraint_v, context_v){ |x, y| (x > y) } }
21
+ SEMVER_LT: ->(context_v, constraint_v){ on_valid_version(constraint_v, context_v){ |x, y| (x > y) } },
22
+ FALLBACK_VALIDATOR: ->(_context_v, _constraint_v){ false }
23
23
  }.freeze
24
24
 
25
25
  LIST_OPERATORS = [:IN, :NOT_IN, :STR_STARTS_WITH, :STR_ENDS_WITH, :STR_CONTAINS].freeze
26
26
 
27
27
  def initialize(context_name, operator, value = [], inverted: false, case_insensitive: false)
28
28
  raise ArgumentError, "context_name is not a String" unless context_name.is_a?(String)
29
- raise ArgumentError, "operator does not hold a valid value:" + OPERATORS.keys unless OPERATORS.include? operator.to_sym
30
29
 
31
- self.validate_constraint_value_type(operator.to_sym, value)
30
+ unless OPERATORS.include? operator.to_sym
31
+ Unleash.logger.warn "Operator #{operator} is not a supported operator, " \
32
+ "falling back to FALLBACK_VALIDATOR which skips this constraint."
33
+ operator = "FALLBACK_VALIDATOR"
34
+ end
35
+ self.log_inconsistent_constraint_configuration(operator.to_sym, value)
32
36
 
33
37
  self.context_name = context_name
34
38
  self.operator = operator.to_sym
@@ -38,8 +42,8 @@ module Unleash
38
42
  end
39
43
 
40
44
  def matches_context?(context)
41
- Unleash.logger.debug "Unleash::Constraint matches_context? value: #{self.value} context.get_by_name(#{self.context_name})" \
42
- " #{context.get_by_name(self.context_name)} "
45
+ Unleash.logger.debug "Unleash::Constraint matches_context? value: #{self.value} context.get_by_name(#{self.context_name})"
46
+
43
47
  match = matches_constraint?(context)
44
48
  self.inverted ? !match : match
45
49
  rescue KeyError
@@ -79,19 +83,25 @@ module Unleash
79
83
  end
80
84
 
81
85
  # This should be a private method but for some reason this fails on Ruby 2.5
82
- def validate_constraint_value_type(operator, value)
83
- raise ArgumentError, "context_name is not an Array" if LIST_OPERATORS.include?(operator) && value.is_a?(String)
84
- raise ArgumentError, "context_name is not a String" if !LIST_OPERATORS.include?(operator) && value.is_a?(Array)
86
+ def log_inconsistent_constraint_configuration(operator, value)
87
+ Unleash.logger.warn "value is a String, operator is expecting an Array" if LIST_OPERATORS.include?(operator) && value.is_a?(String)
88
+ Unleash.logger.warn "value is an Array, operator is expecting a String" if !LIST_OPERATORS.include?(operator) && value.is_a?(Array)
85
89
  end
86
90
 
87
91
  private
88
92
 
89
93
  def matches_constraint?(context)
94
+ Unleash.logger.debug "Unleash::Constraint matches_constraint? value: #{self.value} operator: #{self.operator} " \
95
+ " context.get_by_name(#{self.context_name})"
96
+
90
97
  unless OPERATORS.include?(self.operator)
91
98
  Unleash.logger.warn "Invalid constraint operator: #{self.operator}, this should be unreachable. Always returning false."
92
99
  false
93
100
  end
94
101
 
102
+ # when the operator is NOT_IN and there is no data, return true. In all other cases the operator doesn't match.
103
+ return self.operator == :NOT_IN unless context.include?(self.context_name)
104
+
95
105
  v = self.value.dup
96
106
  context_value = context.get_by_name(self.context_name)
97
107
 
@@ -33,6 +33,13 @@ module Unleash
33
33
  end
34
34
  end
35
35
 
36
+ def include?(name)
37
+ normalized_name = underscore(name)
38
+ return self.instance_variable_defined? "@#{normalized_name}" if ATTRS.include? normalized_name.to_sym
39
+
40
+ self.properties.include?(normalized_name.to_sym) || self.properties.include?(name.to_sym)
41
+ end
42
+
36
43
  private
37
44
 
38
45
  # Method to fetch values from hash for two types of keys: string in camelCase and symbol in snake_case
@@ -9,13 +9,13 @@ module Unleash
9
9
  class FeatureToggle
10
10
  attr_accessor :name, :enabled, :strategies, :variant_definitions
11
11
 
12
- def initialize(params = {})
12
+ def initialize(params = {}, segment_map = {})
13
13
  params = {} if params.nil?
14
14
 
15
15
  self.name = params.fetch('name', nil)
16
16
  self.enabled = params.fetch('enabled', false)
17
17
 
18
- self.strategies = initialize_strategies(params)
18
+ self.strategies = initialize_strategies(params, segment_map)
19
19
  self.variant_definitions = initialize_variant_definitions(params)
20
20
  end
21
21
 
@@ -75,12 +75,14 @@ module Unleash
75
75
  end
76
76
 
77
77
  def strategy_enabled?(strategy, context)
78
- r = Unleash::STRATEGIES.fetch(strategy.name.to_sym, :unknown).is_enabled?(strategy.params, context)
78
+ r = Unleash.strategies.fetch(strategy.name).is_enabled?(strategy.params, context)
79
79
  Unleash.logger.debug "Unleash::FeatureToggle.strategy_enabled? Strategy #{strategy.name} returned #{r} with context: #{context}"
80
80
  r
81
81
  end
82
82
 
83
83
  def strategy_constraint_matches?(strategy, context)
84
+ return false if strategy.disabled
85
+
84
86
  strategy.constraints.empty? || strategy.constraints.all?{ |c| c.matches_context?(context) }
85
87
  end
86
88
 
@@ -128,26 +130,35 @@ module Unleash
128
130
  context
129
131
  end
130
132
 
131
- def initialize_strategies(params)
133
+ def initialize_strategies(params, segment_map)
132
134
  params.fetch('strategies', [])
133
- .select{ |s| s.has_key?('name') && Unleash::STRATEGIES.has_key?(s['name'].to_sym) }
135
+ .select{ |s| s.has_key?('name') && Unleash.strategies.includes?(s['name']) }
134
136
  .map do |s|
135
137
  ActivationStrategy.new(
136
138
  s['name'],
137
139
  s['parameters'],
138
- (s['constraints'] || []).map do |c|
139
- Constraint.new(
140
- c.fetch('contextName'),
141
- c.fetch('operator'),
142
- c.fetch('values', nil) || c.fetch('value', nil),
143
- inverted: c.fetch('inverted', false),
144
- case_insensitive: c.fetch('caseInsensitive', false)
145
- )
146
- end
140
+ resolve_constraints(s, segment_map)
147
141
  )
148
142
  end || []
149
143
  end
150
144
 
145
+ def resolve_constraints(strategy, segment_map)
146
+ segment_constraints = (strategy["segments"] || []).map do |segment_id|
147
+ segment_map[segment_id]&.fetch("constraints")
148
+ end
149
+ (strategy.fetch("constraints", []) + segment_constraints).flatten.map do |constraint|
150
+ return nil if constraint.nil?
151
+
152
+ Constraint.new(
153
+ constraint.fetch('contextName'),
154
+ constraint.fetch('operator'),
155
+ constraint.fetch('value', nil) || constraint.fetch('values', nil),
156
+ inverted: constraint.fetch('inverted', false),
157
+ case_insensitive: constraint.fetch('caseInsensitive', false)
158
+ )
159
+ end
160
+ end
161
+
151
162
  def initialize_variant_definitions(params)
152
163
  (params.fetch('variants', []) || [])
153
164
  .select{ |v| v.is_a?(Hash) && v.has_key?('name') }
@@ -0,0 +1,80 @@
1
+ require 'unleash/strategy/base'
2
+ Gem.find_files('unleash/strategy/**/*.rb').each{ |path| require path }
3
+
4
+ module Unleash
5
+ class Strategies
6
+ def initialize
7
+ @strategies = {}
8
+ register_strategies
9
+ end
10
+
11
+ def keys
12
+ @strategies.keys
13
+ end
14
+
15
+ def includes?(name)
16
+ @strategies.has_key?(name.to_s)
17
+ end
18
+
19
+ def fetch(name)
20
+ raise Unleash::Strategy::NotImplemented, "Strategy is not implemented" unless (strategy = @strategies[name.to_s])
21
+
22
+ strategy
23
+ end
24
+
25
+ def add(strategy)
26
+ @strategies[strategy.name] = strategy
27
+ end
28
+
29
+ def []=(key, strategy)
30
+ warn_deprecated_registration(strategy, 'modifying Unleash::STRATEGIES')
31
+ @strategies[key.to_s] = strategy
32
+ end
33
+
34
+ def [](key)
35
+ @strategies[key.to_s]
36
+ end
37
+
38
+ def register_strategies
39
+ register_base_strategies
40
+ register_custom_strategies
41
+ end
42
+
43
+ protected
44
+
45
+ # Deprecated: Use Unleash.configuration to add custom strategies
46
+ def register_custom_strategies
47
+ Unleash::Strategy.constants
48
+ .select{ |c| Unleash::Strategy.const_get(c).is_a? Class }
49
+ .reject{ |c| ['NotImplemented', 'Base'].include?(c.to_s) } # Reject abstract classes
50
+ .map{ |c| Object.const_get("Unleash::Strategy::#{c}") }
51
+ .reject{ |c| DEFAULT_STRATEGIES.include?(c) } # Reject base classes
52
+ .each do |c|
53
+ strategy = c.new
54
+ warn_deprecated_registration(strategy, 'adding custom class into Unleash::Strategy namespace')
55
+ self.add(strategy)
56
+ end
57
+ end
58
+
59
+ def register_base_strategies
60
+ DEFAULT_STRATEGIES.each{ |c| self.add(c.new) }
61
+ end
62
+
63
+ DEFAULT_STRATEGIES = [
64
+ Unleash::Strategy::ApplicationHostname,
65
+ Unleash::Strategy::Default,
66
+ Unleash::Strategy::FlexibleRollout,
67
+ Unleash::Strategy::GradualRolloutRandom,
68
+ Unleash::Strategy::GradualRolloutSessionId,
69
+ Unleash::Strategy::GradualRolloutUserId,
70
+ Unleash::Strategy::RemoteAddress,
71
+ Unleash::Strategy::UserWithId
72
+ ].freeze
73
+
74
+ def warn_deprecated_registration(strategy, method)
75
+ warn "[DEPRECATED] Registering custom Unleash strategy by #{method} is deprecated.
76
+ Please use Unleash configuration to register custom strategy: " \
77
+ "`Unleash.configure {|c| c.strategies.add(#{strategy.class.name}.new) }`"
78
+ end
79
+ end
80
+ end
@@ -10,7 +10,7 @@ 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.class.name == 'Unleash::Context'
13
+ return false unless context.instance_of?(Unleash::Context)
14
14
 
15
15
  stickiness = params.fetch('stickiness', 'default')
16
16
  stickiness_id = resolve_stickiness(stickiness, context)
@@ -10,7 +10,7 @@ module Unleash
10
10
  # need: params['percentage'], params['groupId'], context.user_id,
11
11
  def is_enabled?(params = {}, context = nil)
12
12
  return false unless params.is_a?(Hash) && params.has_key?('percentage')
13
- return false unless context.class.name == 'Unleash::Context'
13
+ return false unless context.instance_of?(Unleash::Context)
14
14
  return false if context.session_id.nil? || context.session_id.empty?
15
15
 
16
16
  percentage = Integer(params['percentage'] || 0)
@@ -10,7 +10,7 @@ module Unleash
10
10
  # need: params['percentage'], params['groupId'], context.user_id,
11
11
  def is_enabled?(params = {}, context = nil, _constraints = [])
12
12
  return false unless params.is_a?(Hash) && params.has_key?('percentage')
13
- return false unless context.class.name == 'Unleash::Context'
13
+ return false unless context.instance_of?(Unleash::Context)
14
14
  return false if context.user_id.nil? || context.user_id.empty?
15
15
 
16
16
  percentage = Integer(params['percentage'] || 0)
@@ -11,7 +11,7 @@ module Unleash
11
11
  def is_enabled?(params = {}, context = nil)
12
12
  return false unless params.is_a?(Hash) && params.has_key?(PARAM)
13
13
  return false unless params.fetch(PARAM, nil).is_a? String
14
- return false unless context.class.name == 'Unleash::Context'
14
+ return false unless context.instance_of?(Unleash::Context)
15
15
 
16
16
  remote_address = ipaddr_or_nil_from_str(context.remote_address)
17
17
 
@@ -11,7 +11,7 @@ module Unleash
11
11
  def is_enabled?(params = {}, context = nil)
12
12
  return false unless params.is_a?(Hash) && params.has_key?(PARAM)
13
13
  return false unless params.fetch(PARAM, nil).is_a? String
14
- return false unless context.class.name == 'Unleash::Context'
14
+ return false unless context.instance_of?(Unleash::Context)
15
15
 
16
16
  params[PARAM].split(",").map(&:strip).include?(context.user_id)
17
17
  end
@@ -5,11 +5,12 @@ require 'json'
5
5
 
6
6
  module Unleash
7
7
  class ToggleFetcher
8
- attr_accessor :toggle_cache, :toggle_lock, :toggle_resource, :etag, :retry_count
8
+ attr_accessor :toggle_cache, :toggle_lock, :toggle_resource, :etag, :retry_count, :segment_cache
9
9
 
10
10
  def initialize
11
11
  self.etag = nil
12
12
  self.toggle_cache = nil
13
+ self.segment_cache = nil
13
14
  self.toggle_lock = Mutex.new
14
15
  self.toggle_resource = ConditionVariable.new
15
16
  self.retry_count = 0
@@ -95,9 +96,10 @@ module Unleash
95
96
  end
96
97
 
97
98
  def update_running_client!
98
- if Unleash.toggles != self.toggles
99
+ if Unleash.toggles != self.toggles["features"] || Unleash.segment_cache != self.toggles["segments"]
99
100
  Unleash.logger.info "Updating toggles to main client, there has been a change in the server."
100
- Unleash.toggles = self.toggles
101
+ Unleash.toggles = self.toggles["features"]
102
+ Unleash.segment_cache = self.toggles["segments"]
101
103
  end
102
104
  end
103
105
 
@@ -126,10 +128,19 @@ module Unleash
126
128
  Unleash.configuration.bootstrap_config = nil
127
129
  end
128
130
 
131
+ def build_segment_map(segments_array)
132
+ return {} if segments_array.nil?
133
+
134
+ segments_array.map{ |segment| [segment["id"], segment] }.to_h
135
+ end
136
+
129
137
  # @param response_body [String]
130
138
  def get_features(response_body)
131
139
  response_hash = JSON.parse(response_body)
132
- return response_hash['features'] if response_hash['version'] >= 1
140
+
141
+ if response_hash['version'] >= 1
142
+ return { "features" => response_hash["features"], "segments" => build_segment_map(response_hash["segments"]) }
143
+ end
133
144
 
134
145
  raise NotImplemented, "Version of features provided by unleash server" \
135
146
  " is unsupported by this client."
@@ -4,7 +4,7 @@ module Unleash
4
4
  class VariantDefinition
5
5
  attr_accessor :name, :weight, :payload, :overrides, :stickiness
6
6
 
7
- def initialize(name, weight = 0, payload = nil, stickiness = nil, overrides = [])
7
+ def initialize(name, weight = 0, payload = nil, stickiness = nil, overrides = []) # rubocop:disable Metrics/ParameterLists
8
8
  self.name = name
9
9
  self.weight = weight
10
10
  self.payload = payload
@@ -14,7 +14,7 @@ module Unleash
14
14
  end
15
15
 
16
16
  def matches_context?(context)
17
- raise ArgumentError, 'context must be of class Unleash::Context' unless context.class.name == 'Unleash::Context'
17
+ raise ArgumentError, 'context must be of class Unleash::Context' unless context.instance_of?(Unleash::Context)
18
18
 
19
19
  context_value =
20
20
  case self.context_name
@@ -1,3 +1,3 @@
1
1
  module Unleash
2
- VERSION = "4.3.0".freeze
2
+ VERSION = "4.4.0".freeze
3
3
  end
data/lib/unleash.rb CHANGED
@@ -1,35 +1,31 @@
1
1
  require 'unleash/version'
2
2
  require 'unleash/configuration'
3
- require 'unleash/strategy/base'
3
+ require 'unleash/strategies'
4
4
  require 'unleash/context'
5
5
  require 'unleash/client'
6
6
  require 'logger'
7
7
 
8
- Gem.find_files('unleash/strategy/**/*.rb').each{ |path| require path unless path.end_with? '_spec.rb' }
9
-
10
8
  module Unleash
11
9
  TIME_RESOLUTION = 3
12
10
 
13
- STRATEGIES = Unleash::Strategy.constants
14
- .select{ |c| Unleash::Strategy.const_get(c).is_a? Class }
15
- .reject{ |c| ['NotImplemented', 'Base'].include?(c.to_s) }
16
- .map do |c|
17
- lowered_c = c.to_s
18
- lowered_c[0] = lowered_c[0].downcase
19
- [lowered_c.to_sym, Object.const_get("Unleash::Strategy::#{c}").new]
20
- end
21
- .to_h
22
-
23
11
  class << self
24
- attr_accessor :configuration, :toggle_fetcher, :toggles, :toggle_metrics, :reporter, :logger
12
+ attr_accessor :configuration, :toggle_fetcher, :toggles, :toggle_metrics, :reporter, :segment_cache, :logger
25
13
  end
26
14
 
15
+ self.configuration = Unleash::Configuration.new
16
+
17
+ # Deprecated: Use Unleash.configure to add custom strategies
18
+ STRATEGIES = self.configuration.strategies
19
+
27
20
  # Support for configuration via yield:
28
21
  def self.configure
29
- self.configuration ||= Unleash::Configuration.new
30
22
  yield(configuration)
31
23
 
32
24
  self.configuration.validate!
33
25
  self.configuration.refresh_backup_file!
34
26
  end
27
+
28
+ def self.strategies
29
+ self.configuration.strategies
30
+ end
35
31
  end
@@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
31
31
  spec.add_development_dependency "rspec-json_expectations", "~> 2.2"
32
32
  spec.add_development_dependency "webmock", "~> 3.8"
33
33
 
34
- spec.add_development_dependency "rubocop", "< 1.0.0"
34
+ spec.add_development_dependency "rubocop", "~> 1.28.2"
35
35
  spec.add_development_dependency "simplecov", "~> 0.21.2"
36
36
  spec.add_development_dependency "simplecov-lcov", "~> 0.8.0"
37
37
  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.3.0
4
+ version: 4.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Renato Arruda
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-07-14 00:00:00.000000000 Z
11
+ date: 2022-09-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: murmurhash3
@@ -98,16 +98,16 @@ dependencies:
98
98
  name: rubocop
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - "<"
101
+ - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: 1.0.0
103
+ version: 1.28.2
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - "<"
108
+ - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: 1.0.0
110
+ version: 1.28.2
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: simplecov
113
113
  requirement: !ruby/object:Gem::Requirement
@@ -176,6 +176,7 @@ files:
176
176
  - lib/unleash/metrics.rb
177
177
  - lib/unleash/metrics_reporter.rb
178
178
  - lib/unleash/scheduled_executor.rb
179
+ - lib/unleash/strategies.rb
179
180
  - lib/unleash/strategy/application_hostname.rb
180
181
  - lib/unleash/strategy/base.rb
181
182
  - lib/unleash/strategy/default.rb
@@ -212,7 +213,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
212
213
  - !ruby/object:Gem::Version
213
214
  version: '0'
214
215
  requirements: []
215
- rubygems_version: 3.3.6
216
+ rubygems_version: 3.3.5
216
217
  signing_key:
217
218
  specification_version: 4
218
219
  summary: Unleash feature toggle client.