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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8a918d4c2cd828f070ede69b41bccaef489154f189bc93b497256f3589abec7f
4
- data.tar.gz: 320522f90925794e6a12df0e3a034ddb46b81a69445d201447bc1bd51743d4e6
3
+ metadata.gz: 033fbbd756fd2a748d584634724881aae1a2d49feec0a2f7cec4aa7245dc8440
4
+ data.tar.gz: 042ff7ee449a8b80a5f120d203c791e7e42c896dce377a5c7af4367c94048829
5
5
  SHA512:
6
- metadata.gz: 5ab24c0101cf91b2a0be85a92e5665d21397a7733be165c2c718208887b1060e29c621e12595ff4b2bbbf4f502fd40a6f1da89b62e147d77f2e9a4c9bd981f15
7
- data.tar.gz: 00ee27a652e9c85cabed5e331c18d4bfb68ee32e625e513f330f8534a89cc191c63f1320ce466d80807cf7a0d7518950da78b005e3c759d0e76791a060757607
6
+ metadata.gz: 0fe881017cbb21138eed0663f0df11d18df61e63be666c5e2e26b68ca478792e8bb51f123ea415d86c8ce6ede3e087b2c7e8b35991c90d6939bac7672f0f7de4
7
+ data.tar.gz: 944146e27443caad934f35b1efb30179ec3c1a8b8c78d2def46e68250b338b156184e79b861be2ae3b929cf4b38a7514978a1757bd7e24a65ede50930010c9a7
data/.rubocop.yml CHANGED
@@ -12,6 +12,9 @@ Style/StringLiteralsInInterpolation:
12
12
  Metrics/BlockLength:
13
13
  Enabled: false
14
14
 
15
+ Metrics/ClassLength:
16
+ Enabled: false
17
+
15
18
  # affects similarity to other SDKs + pointless
16
19
  Metrics/MethodLength:
17
20
  Enabled: false
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- featurehub-sdk (1.0.0)
4
+ featurehub-sdk (1.2.0)
5
5
  concurrent-ruby (~> 1.1.10)
6
6
  faraday (~> 2.3)
7
7
  ld-eventsource (~> 2.2.0)
@@ -73,7 +73,7 @@ module FeatureHub
73
73
  end
74
74
 
75
75
  def get_attr(key, default_val = nil)
76
- (@attributes[key.to_sym] || [default_val])[0]
76
+ (@attributes[key.to_sym] || [default_val]).compact
77
77
  end
78
78
 
79
79
  def default_percentage_key
@@ -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); end
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
- supplied_value = context.get_attr(attr.field_name)
71
- if supplied_value.nil? && attr.field_name.downcase == "now"
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
- supplied_value = Time.new.utc.iso8601[0..9]
74
+ supplied_values = [Time.new.utc.iso8601[0..9]]
75
75
  when "DATETIME"
76
- supplied_value = Time.new.utc.iso8601
76
+ supplied_values = [Time.new.utc.iso8601]
77
77
  end
78
78
  end
79
79
 
80
- if attr.values.nil? && supplied_value.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? || supplied_value.nil?
86
+ return false if attr.values.nil? || supplied_values.empty?
87
87
 
88
88
  # this attribute has to match or we failed
89
- return false unless @matcher_repository.find_matcher(attr).match(supplied_value, attr)
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
- get_updates
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
- @task.execute
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(url, request: { timeout: @timeout }, headers: headers)
116
+ resp = @conn.get url, {}, headers
92
117
  case resp.status
93
118
  when 200
94
- @etag = resp.headers["etag"]
95
- process_results(JSON.parse(resp.body))
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
- @cancel = true
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 tgaina")
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}&#{@context}"
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, :closed
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
- @closed = true
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
- !@closed && !@sse_client.nil?
31
+ !@sse_client.nil?
28
32
  end
29
33
 
30
34
  def close
31
- @closed = true
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
- private
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
- @repository.notify(event.type, JSON.parse(event.data))
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
@@ -3,7 +3,7 @@
3
3
  module FeatureHub
4
4
  # already documented elsewhere
5
5
  module Sdk
6
- VERSION = "1.0.0"
6
+ VERSION = "1.2.0"
7
7
 
8
8
  def default_logger
9
9
  log = ::Logger.new($stdout)
@@ -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? ]) -> void
65
- def cast:(feature_type: String?) -> [bool? | String? | Float? ]
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 < ValueInterceptor
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 ) -> void
100
- def apply:(strategies: Array[RolloutStrategy], key: String, feature_id: String, context: ClientContext) -> Applied
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
- def country: (value: Symbol ) -> ClientContext
121
- def platform: (value: Symbol ) -> ClientContext
122
- def device: (value: Symbol ) -> ClientContext
123
- def attribute_value: (key: String, values: Array[String] ) -> ClientContext
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
- def get_attr: (key: String, default_val: String?) -> String?
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 ) -> bool
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 ) -> bool
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.0.0
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-06-16 00:00:00.000000000 Z
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
@@ -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