featurehub-sdk 1.0.0 → 1.2.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 +4 -4
- data/.rubocop.yml +3 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +13 -0
- data/Gemfile.lock +1 -1
- data/lib/feature_hub/sdk/context.rb +1 -1
- data/lib/feature_hub/sdk/feature_hub_config.rb +8 -2
- data/lib/feature_hub/sdk/impl/apply_features.rb +11 -7
- data/lib/feature_hub/sdk/poll_edge_service.rb +65 -15
- data/lib/feature_hub/sdk/streaming_edge_service.rb +44 -8
- data/lib/feature_hub/sdk/version.rb +1 -1
- data/sig/feature_hub/featurehub.rbs +69 -15
- metadata +3 -3
- data/featurehub-sdk.gemspec +0 -45
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 033fbbd756fd2a748d584634724881aae1a2d49feec0a2f7cec4aa7245dc8440
|
4
|
+
data.tar.gz: 042ff7ee449a8b80a5f120d203c791e7e42c896dce377a5c7af4367c94048829
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0fe881017cbb21138eed0663f0df11d18df61e63be666c5e2e26b68ca478792e8bb51f123ea415d86c8ce6ede3e087b2c7e8b35991c90d6939bac7672f0f7de4
|
7
|
+
data.tar.gz: 944146e27443caad934f35b1efb30179ec3c1a8b8c78d2def46e68250b338b156184e79b861be2ae3b929cf4b38a7514978a1757bd7e24a65ede50930010c9a7
|
data/.rubocop.yml
CHANGED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.7.6
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
## [1.2.0] - 2022-10-14
|
2
|
+
|
3
|
+
- Fixing polling client for server eval
|
4
|
+
- Added in tests for polling client
|
5
|
+
- Added in server eval for streaming client
|
6
|
+
- Added expired environment support for polling and streaming
|
7
|
+
- Added cache busting for server eval polling client
|
8
|
+
-
|
9
|
+
|
10
|
+
## [1.1.0] - 2022-10-12
|
11
|
+
|
12
|
+
- Adds support for array values in flag evaluation contexts via [this PR](https://github.com/featurehub-io/featurehub-ruby-sdk/pull/12)
|
13
|
+
|
1
14
|
## [1.0.0] - 2022-06-06
|
2
15
|
|
3
16
|
- Initial release, feature complete
|
data/Gemfile.lock
CHANGED
@@ -74,7 +74,10 @@ module FeatureHub
|
|
74
74
|
edge_provider
|
75
75
|
end
|
76
76
|
|
77
|
-
def use_polling_edge_service(interval = ENV.fetch("FEATUREHUB_POLL_INTERVAL", "30").to_i)
|
77
|
+
def use_polling_edge_service(interval = ENV.fetch("FEATUREHUB_POLL_INTERVAL", "30").to_i)
|
78
|
+
@interval = interval
|
79
|
+
@edge_service_provider = method(:create_polling_edge_provider)
|
80
|
+
end
|
78
81
|
|
79
82
|
def new_context
|
80
83
|
get_or_create_edge_service
|
@@ -99,8 +102,11 @@ module FeatureHub
|
|
99
102
|
@edge_service_provider.call(@repository, @api_keys, @edge_url, @logger)
|
100
103
|
end
|
101
104
|
|
105
|
+
def create_polling_edge_provider(repo, api_keys, edge_url, logger)
|
106
|
+
FeatureHub::Sdk::PollingEdgeService.new(repo, api_keys, edge_url, @interval, logger)
|
107
|
+
end
|
108
|
+
|
102
109
|
def create_default_provider(repo, api_keys, edge_url, logger)
|
103
|
-
# FeatureHub::Sdk::PollingEdgeService.new(repo, api_keys, edge_url, 10, logger)
|
104
110
|
FeatureHub::Sdk::StreamingEdgeService.new(repo, api_keys, edge_url, logger)
|
105
111
|
end
|
106
112
|
|
@@ -67,26 +67,30 @@ module FeatureHub
|
|
67
67
|
|
68
68
|
def match_attribute(context, rs_attr)
|
69
69
|
rs_attr.attributes.each do |attr|
|
70
|
-
|
71
|
-
if
|
70
|
+
supplied_values = context.get_attr(attr.field_name)
|
71
|
+
if supplied_values.empty? && attr.field_name.downcase == "now"
|
72
72
|
case attr.field_type
|
73
73
|
when "DATE"
|
74
|
-
|
74
|
+
supplied_values = [Time.new.utc.iso8601[0..9]]
|
75
75
|
when "DATETIME"
|
76
|
-
|
76
|
+
supplied_values = [Time.new.utc.iso8601]
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
|
-
if attr.values.nil? &&
|
80
|
+
if attr.values.nil? && supplied_values.empty?
|
81
81
|
return false unless attr.conditional.equals?
|
82
82
|
|
83
83
|
next
|
84
84
|
end
|
85
85
|
|
86
|
-
return false if attr.values.nil? ||
|
86
|
+
return false if attr.values.nil? || supplied_values.empty?
|
87
87
|
|
88
88
|
# this attribute has to match or we failed
|
89
|
-
|
89
|
+
match = supplied_values.any? do |supplied_value|
|
90
|
+
@matcher_repository.find_matcher(attr).match(supplied_value, attr)
|
91
|
+
end
|
92
|
+
|
93
|
+
return false unless match
|
90
94
|
end
|
91
95
|
|
92
96
|
true
|
@@ -4,12 +4,13 @@ require "faraday"
|
|
4
4
|
require "faraday/net_http"
|
5
5
|
require "json"
|
6
6
|
require "concurrent-ruby"
|
7
|
+
require "digest/sha2"
|
7
8
|
|
8
9
|
module FeatureHub
|
9
10
|
module Sdk
|
10
11
|
# uses a periodic polling mechanism to get updates
|
11
12
|
class PollingEdgeService < EdgeService
|
12
|
-
attr_reader :repository, :api_keys, :edge_url, :interval
|
13
|
+
attr_reader :repository, :api_keys, :edge_url, :interval, :stopped, :etag, :cancel, :sha_context
|
13
14
|
|
14
15
|
def initialize(repository, api_keys, edge_url, interval, logger = nil)
|
15
16
|
super(repository, api_keys, edge_url)
|
@@ -25,6 +26,8 @@ module FeatureHub
|
|
25
26
|
@cancel = false
|
26
27
|
@context = nil
|
27
28
|
@etag = nil
|
29
|
+
@stopped = false
|
30
|
+
@sha_context = nil
|
28
31
|
|
29
32
|
generate_url
|
30
33
|
end
|
@@ -48,34 +51,53 @@ module FeatureHub
|
|
48
51
|
return if new_header == @context
|
49
52
|
|
50
53
|
@context = new_header
|
54
|
+
@sha_context = Digest::SHA256.hexdigest(@context)
|
51
55
|
|
52
|
-
|
56
|
+
if active
|
57
|
+
get_updates
|
58
|
+
else
|
59
|
+
poll
|
60
|
+
end
|
53
61
|
end
|
54
62
|
|
55
63
|
def close
|
56
64
|
cancel_task
|
57
65
|
end
|
58
66
|
|
67
|
+
def active
|
68
|
+
!@task.nil?
|
69
|
+
end
|
70
|
+
|
59
71
|
private
|
60
72
|
|
61
73
|
def poll_with_interval
|
62
|
-
return if @cancel || !@task.nil?
|
63
|
-
|
64
|
-
get_updates
|
74
|
+
return if @cancel || !@task.nil? || @stopped
|
65
75
|
|
66
76
|
@logger.info("starting polling for #{determine_request_url}")
|
67
|
-
@task = Concurrent::TimerTask.new(execution_interval: @interval) do
|
77
|
+
@task = Concurrent::TimerTask.new(execution_interval: @interval, run_now: false) do
|
68
78
|
get_updates
|
69
79
|
end
|
70
|
-
|
80
|
+
|
81
|
+
get_updates
|
82
|
+
|
83
|
+
@task&.execute # could have been shutdown
|
71
84
|
end
|
72
85
|
|
73
86
|
def cancel_task
|
87
|
+
@cancel = true
|
88
|
+
shutdown_task
|
89
|
+
end
|
90
|
+
|
91
|
+
def stopped_task
|
92
|
+
@stopped = true
|
93
|
+
shutdown_task
|
94
|
+
end
|
95
|
+
|
96
|
+
def shutdown_task
|
74
97
|
return if @task.nil?
|
75
98
|
|
76
99
|
@task.shutdown
|
77
100
|
@task = nil
|
78
|
-
@cancel = true
|
79
101
|
end
|
80
102
|
|
81
103
|
# rubocop:disable Naming/AccessorMethodName
|
@@ -86,36 +108,63 @@ module FeatureHub
|
|
86
108
|
"X-SDK": "Ruby",
|
87
109
|
"X-SDK-Version": FeatureHub::Sdk::VERSION
|
88
110
|
}
|
111
|
+
|
112
|
+
headers["x-featurehub"] = @context unless @context.nil?
|
89
113
|
headers["if-none-match"] = @etag unless @etag.nil?
|
114
|
+
|
90
115
|
@logger.debug("polling for #{url}")
|
91
|
-
resp = @conn.get
|
116
|
+
resp = @conn.get url, {}, headers
|
92
117
|
case resp.status
|
93
118
|
when 200
|
94
|
-
|
95
|
-
|
119
|
+
success(resp)
|
120
|
+
when 236
|
121
|
+
stopped_task
|
122
|
+
success(resp)
|
96
123
|
when 404 # no such key
|
97
124
|
@repository.notify("failed", nil)
|
98
|
-
|
125
|
+
cancel_task
|
99
126
|
@logger.error("featurehub: key does not exist, stopping polling")
|
100
127
|
when 503 # dacha busy
|
101
|
-
@logger.debug("featurehub: dacha is busy, trying
|
128
|
+
@logger.debug("featurehub: dacha is busy, trying again")
|
102
129
|
else
|
103
130
|
@logger.debug("featurehub: unknown error #{resp.status}")
|
104
131
|
end
|
105
132
|
end
|
133
|
+
|
106
134
|
# rubocop:enable Naming/AccessorMethodName
|
107
135
|
|
136
|
+
def success(resp)
|
137
|
+
@etag = resp.headers["etag"]
|
138
|
+
|
139
|
+
check_interval_change(resp.headers["cache-control"]) if resp.headers["cache-control"]
|
140
|
+
|
141
|
+
process_results(JSON.parse(resp.body))
|
142
|
+
end
|
143
|
+
|
108
144
|
def process_results(data)
|
109
145
|
data.each do |environment|
|
110
146
|
@repository.notify("features", environment["features"]) if environment
|
111
147
|
end
|
112
148
|
end
|
113
149
|
|
150
|
+
def check_interval_change(cache_control_header)
|
151
|
+
found = cache_control_header.scan(/max-age=(\d+)/)
|
152
|
+
|
153
|
+
return if @task.nil? || found.empty? || found[0].empty?
|
154
|
+
|
155
|
+
new_interval = found[0][0].to_i
|
156
|
+
|
157
|
+
return unless new_interval.positive? && new_interval != @interval
|
158
|
+
|
159
|
+
@interval = new_interval
|
160
|
+
@task.execution_interval = @interval
|
161
|
+
end
|
162
|
+
|
114
163
|
def determine_request_url
|
115
164
|
if @context.nil?
|
116
|
-
@url
|
165
|
+
"#{@url}&contextSha=0"
|
117
166
|
else
|
118
|
-
"#{@url}
|
167
|
+
"#{@url}&contextSha=#{@sha_context}"
|
119
168
|
end
|
120
169
|
end
|
121
170
|
|
@@ -125,6 +174,7 @@ module FeatureHub
|
|
125
174
|
@timeout = ENV.fetch("FEATUREHUB_POLL_HTTP_TIMEOUT", "12").to_i
|
126
175
|
@conn = Faraday.new(url: @edge_url) do |f|
|
127
176
|
f.adapter :net_http
|
177
|
+
f.options.timeout = @timeout
|
128
178
|
end
|
129
179
|
end
|
130
180
|
end
|
@@ -7,7 +7,7 @@ module FeatureHub
|
|
7
7
|
module Sdk
|
8
8
|
# provides a streaming service
|
9
9
|
class StreamingEdgeService < FeatureHub::Sdk::EdgeService
|
10
|
-
attr_reader :repository, :sse_client, :url, :
|
10
|
+
attr_reader :repository, :sse_client, :url, :stopped
|
11
11
|
|
12
12
|
def initialize(repository, api_keys, edge_url, logger = nil)
|
13
13
|
super(repository, api_keys, edge_url)
|
@@ -15,42 +15,78 @@ module FeatureHub
|
|
15
15
|
@url = "#{edge_url}features/#{api_keys[0]}"
|
16
16
|
@repository = repository
|
17
17
|
@sse_client = nil
|
18
|
-
@
|
18
|
+
@context = nil
|
19
19
|
@logger = logger || FeatureHub::Sdk.default_logger
|
20
20
|
end
|
21
21
|
|
22
|
+
def closed
|
23
|
+
@sse_client.nil?
|
24
|
+
end
|
25
|
+
|
22
26
|
def poll
|
23
|
-
start_streaming unless @sse_client
|
27
|
+
start_streaming unless @sse_client || @stopped
|
24
28
|
end
|
25
29
|
|
26
30
|
def active
|
27
|
-
!@
|
31
|
+
!@sse_client.nil?
|
28
32
|
end
|
29
33
|
|
30
34
|
def close
|
31
|
-
|
35
|
+
close_connection
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def close_connection
|
32
41
|
return if @sse_client.nil?
|
33
42
|
|
34
43
|
@sse_client.close
|
35
44
|
@sse_client = nil
|
36
45
|
end
|
37
46
|
|
38
|
-
|
47
|
+
def stop
|
48
|
+
@stopped = true
|
49
|
+
close_connection
|
50
|
+
end
|
51
|
+
|
52
|
+
def context_change(new_header)
|
53
|
+
return if new_header == @context
|
54
|
+
|
55
|
+
@context = new_header
|
56
|
+
close
|
57
|
+
poll
|
58
|
+
end
|
39
59
|
|
40
60
|
def start_streaming
|
41
|
-
@closed = false
|
42
61
|
@logger.info("streaming from #{@url}")
|
62
|
+
# we can get an error before returning the new() function and get a race condition on the close
|
63
|
+
must_close = false
|
43
64
|
@sse_client = SSE::Client.new(@url) do |client|
|
44
65
|
client.on_event do |event|
|
45
|
-
|
66
|
+
json_data = JSON.parse(event.data)
|
67
|
+
|
68
|
+
if event.type == "config"
|
69
|
+
process_config(json_data)
|
70
|
+
else
|
71
|
+
@repository.notify(event.type, json_data)
|
72
|
+
end
|
46
73
|
end
|
47
74
|
client.on_error do |error|
|
48
75
|
if error.is_a?(SSE::Errors::HTTPStatusError) && (error.status == 404)
|
49
76
|
@repository.notify("failure", nil)
|
50
77
|
close
|
78
|
+
must_close = true
|
51
79
|
end
|
52
80
|
end
|
53
81
|
end
|
82
|
+
|
83
|
+
return unless must_close
|
84
|
+
|
85
|
+
close # try again
|
86
|
+
end
|
87
|
+
|
88
|
+
def process_config(json_data)
|
89
|
+
stop if json_data["edge.stale"]
|
54
90
|
end
|
55
91
|
end
|
56
92
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module FeatureHub
|
2
2
|
module Sdk
|
3
3
|
VERSION: String
|
4
|
+
|
4
5
|
# See the writing guide of rbs: https://github.com/ruby/rbs#guides
|
5
6
|
#
|
6
7
|
|
@@ -11,13 +12,17 @@ module FeatureHub
|
|
11
12
|
feature_state: Hash[untyped, untyped]?, parent_state: FeatureState?, ctx: ClientContext?) -> void
|
12
13
|
|
13
14
|
def locked?: -> bool
|
15
|
+
|
14
16
|
def exists?: (top_feature: FeatureState?) -> bool
|
17
|
+
|
15
18
|
def id: -> String?
|
19
|
+
|
16
20
|
def feature_type: -> String?
|
17
21
|
|
18
22
|
def with_context: (ctx: ClientContext) -> FeatureState
|
19
23
|
|
20
24
|
def update_feature_state: (feature_state: Hash[untyped, untyped]) -> void
|
25
|
+
|
21
26
|
# this is the feature state of the top level, it always walks up
|
22
27
|
def feature_state: () -> Hash[untyped, untyped]
|
23
28
|
|
@@ -57,19 +62,20 @@ module FeatureHub
|
|
57
62
|
attr_reader matched: bool
|
58
63
|
attr_reader value: [bool? | String? | Float?]
|
59
64
|
|
60
|
-
def initialize:(matched: bool, value: [bool? | String? | Float?]) -> void
|
65
|
+
def initialize: (matched: bool, value: [bool? | String? | Float?]) -> void
|
61
66
|
end
|
62
67
|
|
63
68
|
class InterceptorValue
|
64
|
-
def initialize: (val: [bool? | String? | Float?
|
65
|
-
|
69
|
+
def initialize: (val: [bool? | String? | Float?]) -> void
|
70
|
+
|
71
|
+
def cast: (feature_type: String?) -> [bool? | String? | Float?]
|
66
72
|
end
|
67
73
|
|
68
74
|
class ValueInterceptor
|
69
75
|
def intercepted_value: (feature_key: Symbol) -> InterceptorValue?
|
70
76
|
end
|
71
77
|
|
72
|
-
class EnvironmentInterceptor <
|
78
|
+
class EnvironmentInterceptor < ValueInterceptor
|
73
79
|
end
|
74
80
|
|
75
81
|
class PercentageCalculator
|
@@ -78,17 +84,25 @@ module FeatureHub
|
|
78
84
|
|
79
85
|
class ApplyFeatures
|
80
86
|
def initialize: (percent_calculator: PercentageCalculator?, matcher_repository: MatcherRepository?) -> void
|
87
|
+
|
81
88
|
def apply: (strategies: Array[RolloutStrategy], key: String, feature_value_id: String, context: ClientContext) -> Applied
|
89
|
+
|
82
90
|
def match_attribute: (context: ClientContext, rs: RolloutStrategyAttribute) -> bool
|
91
|
+
|
83
92
|
def self.determine_percentage_key: (context: ClientContext, rsi: RolloutStrategy) -> String?
|
84
93
|
end
|
85
94
|
|
86
95
|
class InternalFeatureRepository
|
87
96
|
def feature: (key: String) -> FeatureState?
|
97
|
+
|
88
98
|
def find_interceptor: (feature_value: String) -> InterceptorValue?
|
99
|
+
|
89
100
|
def ready?: -> bool
|
101
|
+
|
90
102
|
def not_ready!: -> void
|
103
|
+
|
91
104
|
def apply: (strategies: Array[RolloutStrategy], key: String, feature_id: String, context: ClientContext) -> Applied
|
105
|
+
|
92
106
|
def notify: (status: String, data: Hash[untyped, untyped]) -> void
|
93
107
|
end
|
94
108
|
|
@@ -96,15 +110,20 @@ module FeatureHub
|
|
96
110
|
@features: Hash[String, FeatureState]
|
97
111
|
@ready: bool
|
98
112
|
|
99
|
-
def initialize: (apply_features: nil | ApplyFeatures
|
100
|
-
|
113
|
+
def initialize: (apply_features: nil | ApplyFeatures) -> void
|
114
|
+
|
115
|
+
def apply: (strategies: Array[RolloutStrategy], key: String, feature_id: String, context: ClientContext) -> Applied
|
101
116
|
|
102
117
|
def notify: (status: String, data: Hash[untyped, untyped]) -> void
|
118
|
+
|
103
119
|
def feature: (key: String) -> FeatureState
|
120
|
+
|
104
121
|
def register_interceptor: (interceptor: ValueInterceptor) -> void
|
105
122
|
|
106
123
|
def find_interceptor: (feature_value: String) -> InterceptorValue?
|
124
|
+
|
107
125
|
def ready?: -> bool
|
126
|
+
|
108
127
|
def not_ready!: -> void
|
109
128
|
|
110
129
|
def extract_feature_state: -> Array[Hash[untyped, untyped]]
|
@@ -114,37 +133,61 @@ module FeatureHub
|
|
114
133
|
attr_reader repo: InternalFeatureRepository
|
115
134
|
|
116
135
|
def initialize: (repository: InternalFeatureRepository) -> void
|
136
|
+
|
117
137
|
def user_key: (value: String) -> ClientContext
|
138
|
+
|
118
139
|
def session_key: (value: String) -> ClientContext
|
140
|
+
|
119
141
|
def version: (value: String) -> ClientContext
|
120
|
-
|
121
|
-
def
|
122
|
-
|
123
|
-
def
|
142
|
+
|
143
|
+
def country: (value: Symbol) -> ClientContext
|
144
|
+
|
145
|
+
def platform: (value: Symbol) -> ClientContext
|
146
|
+
|
147
|
+
def device: (value: Symbol) -> ClientContext
|
148
|
+
|
149
|
+
def attribute_value: (key: String, values: Array[String]) -> ClientContext
|
150
|
+
|
124
151
|
def clear: -> ClientContext
|
125
|
-
|
152
|
+
|
153
|
+
def get_attr: (key: String, default_val: String?) -> Array[String]
|
154
|
+
|
126
155
|
def default_percentage_key: -> String?
|
156
|
+
|
127
157
|
def enabled: (key: String) -> bool?
|
128
158
|
|
129
159
|
def feature: (key: String) -> FeatureState
|
160
|
+
|
130
161
|
def set?: (key: String) -> bool?
|
162
|
+
|
131
163
|
def number: (key: String) -> Float?
|
164
|
+
|
132
165
|
def string: (key: String) -> String?
|
166
|
+
|
133
167
|
def json: (key: String) -> Hash[untyped, untyped]?
|
168
|
+
|
134
169
|
def raw_json: (key: String) -> String?
|
170
|
+
|
135
171
|
def flag: (key: String) -> bool?
|
172
|
+
|
136
173
|
def boolean: (key: String) -> bool?
|
174
|
+
|
137
175
|
def exists?: (key: String) -> bool
|
138
176
|
|
139
177
|
def build: -> ClientContext
|
178
|
+
|
140
179
|
def build_sync: -> ClientContext
|
180
|
+
|
141
181
|
def close: -> ClientContext
|
142
182
|
end
|
143
183
|
|
144
184
|
class EdgeService
|
145
185
|
def initialize: (repository: InternalFeatureRepository, api_keys: Array[String], edge_url: String) -> void
|
186
|
+
|
146
187
|
def poll: -> void
|
188
|
+
|
147
189
|
def context_change: (new_header: String?) -> void
|
190
|
+
|
148
191
|
def close: -> void
|
149
192
|
end
|
150
193
|
|
@@ -189,15 +232,25 @@ module FeatureHub
|
|
189
232
|
|
190
233
|
class RolloutStrategyCondition
|
191
234
|
def equals?: -> bool
|
235
|
+
|
192
236
|
def ends_with?: -> bool
|
237
|
+
|
193
238
|
def starts_with?: -> bool
|
239
|
+
|
194
240
|
def greater?: -> bool
|
241
|
+
|
195
242
|
def greater_equals?: -> bool
|
243
|
+
|
196
244
|
def less?: -> bool
|
245
|
+
|
197
246
|
def less_equals?: -> bool
|
247
|
+
|
198
248
|
def not_equals?: -> bool
|
249
|
+
|
199
250
|
def includes?: -> bool
|
251
|
+
|
200
252
|
def excludes?: -> bool
|
253
|
+
|
201
254
|
def regex?: -> bool
|
202
255
|
end
|
203
256
|
|
@@ -209,6 +262,7 @@ module FeatureHub
|
|
209
262
|
attr_reader field_type: String
|
210
263
|
|
211
264
|
def float_values: -> Array[Float]
|
265
|
+
|
212
266
|
def str_values: -> Array[String]
|
213
267
|
end
|
214
268
|
|
@@ -221,15 +275,16 @@ module FeatureHub
|
|
221
275
|
attr_reader value: [bool? | String? | Float?]
|
222
276
|
|
223
277
|
def percentage_attributes?: -> bool
|
278
|
+
|
224
279
|
def attributes?: -> bool
|
225
280
|
end
|
226
281
|
|
227
282
|
class StrategyMatcher
|
228
|
-
def match: (supplied_value: String, attr: RolloutStrategyAttribute
|
283
|
+
def match: (supplied_value: String, attr: RolloutStrategyAttribute) -> bool
|
229
284
|
end
|
230
285
|
|
231
286
|
class BooleanMatcher < StrategyMatcher
|
232
|
-
def match: (supplied_value: String, attr: RolloutStrategyAttribute
|
287
|
+
def match: (supplied_value: String, attr: RolloutStrategyAttribute) -> bool
|
233
288
|
end
|
234
289
|
|
235
290
|
class StringMatcher < StrategyMatcher
|
@@ -250,7 +305,6 @@ module FeatureHub
|
|
250
305
|
|
251
306
|
class MatcherRegistry < MatcherRepository
|
252
307
|
end
|
253
|
-
end
|
308
|
+
end
|
254
309
|
|
255
310
|
end
|
256
|
-
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: featurehub-sdk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Vowles
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2022-
|
12
|
+
date: 2022-11-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: concurrent-ruby
|
@@ -90,13 +90,13 @@ extra_rdoc_files: []
|
|
90
90
|
files:
|
91
91
|
- ".rspec"
|
92
92
|
- ".rubocop.yml"
|
93
|
+
- ".ruby-version"
|
93
94
|
- CHANGELOG.md
|
94
95
|
- CODE_OF_CONDUCT.md
|
95
96
|
- Gemfile
|
96
97
|
- Gemfile.lock
|
97
98
|
- LICENSE.txt
|
98
99
|
- Rakefile
|
99
|
-
- featurehub-sdk.gemspec
|
100
100
|
- featurehub-sdk.iml
|
101
101
|
- lib/feature_hub/sdk/context.rb
|
102
102
|
- lib/feature_hub/sdk/feature_hub_config.rb
|
data/featurehub-sdk.gemspec
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
lib = File.expand_path("lib", __dir__)
|
4
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
-
require "feature_hub/sdk/version"
|
6
|
-
|
7
|
-
Gem::Specification.new do |spec|
|
8
|
-
spec.name = "featurehub-sdk"
|
9
|
-
spec.version = FeatureHub::Sdk::VERSION
|
10
|
-
spec.authors = ["Richard Vowles", "Irina Southwell"]
|
11
|
-
spec.email = ["richard@bluetrainsoftware.com"]
|
12
|
-
|
13
|
-
spec.summary = "FeatureHub Ruby SDK"
|
14
|
-
spec.description = "FeatureHub Ruby SDK"
|
15
|
-
spec.homepage = "https://www.featurehub.io"
|
16
|
-
spec.license = "MIT"
|
17
|
-
spec.required_ruby_version = ">= 2.6.0"
|
18
|
-
|
19
|
-
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
20
|
-
|
21
|
-
spec.metadata["homepage_uri"] = spec.homepage
|
22
|
-
spec.metadata["source_code_uri"] = "https://github.com/featurehub-io/featurehub-ruby-sdk"
|
23
|
-
spec.metadata["changelog_uri"] = "https://github.com/featurehub-io/featurehub-ruby-sdk/featurehub-sdk/CHANGELOG.md"
|
24
|
-
spec.metadata["rubygems_mfa_required"] = "true"
|
25
|
-
|
26
|
-
# Specify which files should be added to the gem when it is released.
|
27
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
28
|
-
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
29
|
-
`git ls-files -z`.split("\x0").reject do |f|
|
30
|
-
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
31
|
-
end
|
32
|
-
end
|
33
|
-
spec.bindir = "exe"
|
34
|
-
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
35
|
-
spec.require_paths = ["lib"]
|
36
|
-
|
37
|
-
spec.add_dependency "concurrent-ruby", "~> 1.1.10"
|
38
|
-
spec.add_dependency "faraday", "~> 2.3"
|
39
|
-
spec.add_dependency "ld-eventsource", "~> 2.2.0"
|
40
|
-
spec.add_dependency "murmurhash3", "~> 0.1.6"
|
41
|
-
spec.add_dependency "sem_version", "~> 2.0.0"
|
42
|
-
|
43
|
-
# For more information and examples about making a new gem, check out our
|
44
|
-
# guide at: https://bundler.io/guides/creating_gem.html
|
45
|
-
end
|