featurehub-sdk 2.0.1 → 2.1.1

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.
@@ -2,67 +2,110 @@
2
2
 
3
3
  require "json"
4
4
  require "concurrent-ruby"
5
+ require_relative "session_store_helpers"
5
6
 
6
7
  module FeatureHub
7
8
  module Sdk
9
+ # Optional configuration for RedisSessionStore.
10
+ class RedisSessionStoreOptions
11
+ attr_reader :prefix, :backoff_timeout, :retry_update_count, :refresh_timeout, :logger, :db
12
+
13
+ def initialize(opts = nil)
14
+ opts ||= {}
15
+ @prefix = opts[:prefix] || "featurehub"
16
+ @backoff_timeout = opts[:backoff_timeout] || 500
17
+ @retry_update_count = opts[:retry_update_count] || 10
18
+ @refresh_timeout = opts[:refresh_timeout] || 300
19
+ @logger = opts[:logger]
20
+ @db = opts[:db] || 0
21
+ end
22
+ end
23
+
8
24
  # Persists feature values from a FeatureHubRepository in Redis so they survive
9
25
  # process restarts and are shared across multiple processes.
10
26
  #
11
- # WARNING: Do not use with server-evaluated features. Each server-evaluated
12
- # context sends different resolved values; storing them in a shared Redis key
13
- # will cause processes to overwrite each other's feature states.
27
+ # Uses SHA256-based change detection and Redis WATCH/MULTI/EXEC for multi-process safety.
14
28
  #
15
- # On initialization the store checks Redis for previously saved features and
16
- # replays them into the repository. It then listens for live updates via
17
- # RawUpdateFeatureListener and writes newer versions back to Redis. A periodic
18
- # timer re-reads all features from Redis so that updates published by other
19
- # processes are picked up automatically.
29
+ # WARNING: Do not use with server-evaluated features. Each server-evaluated context
30
+ # sends different resolved values; storing them in a shared Redis key will cause
31
+ # processes to overwrite each other's feature states.
20
32
  #
21
- # Options (symbol keys):
22
- # :namespace - Redis db index (default: 0)
23
- # :prefix - key prefix for all Redis keys (default: "featurehub")
24
- # :timeout - seconds between periodic reloads (default: 30)
33
+ # On initialization the store reads any previously saved features from Redis and
34
+ # replays them into the repository. A periodic timer re-reads the SHA key so that
35
+ # updates published by other processes are picked up automatically.
25
36
  class RedisSessionStore < RawUpdateFeatureListener
37
+ include SessionStoreHelpers
38
+
26
39
  SOURCE = "redis-store"
27
40
 
28
- def initialize(connection_string, repository, opts = nil)
41
+ # @param connection_or_client [String, Array, Object] Redis URL, list of cluster URLs,
42
+ # or an existing Redis client
43
+ # @param config [FeatureHubConfig] SDK config (provides repository and environment_id)
44
+ # @param opts [RedisSessionStoreOptions, Hash, nil] optional configuration
45
+ def initialize(connection_or_client, config, opts = nil)
29
46
  super()
30
47
 
31
- opts ||= {}
32
- @repository = repository
33
- @prefix = opts[:prefix] || "featurehub"
34
- @timeout = opts[:timeout] || 30
35
- @namespace = opts[:namespace] || 0
36
- @password = opts[:password]
37
- @logger = opts[:logger]
48
+ options = opts.is_a?(RedisSessionStoreOptions) ? opts : RedisSessionStoreOptions.new(opts)
49
+
50
+ @repository = config.repository
51
+ @environment_id = config.environment_id
52
+ @prefix = options.prefix
53
+ @backoff_timeout = options.backoff_timeout
54
+ @retry_update_count = options.retry_update_count
55
+ @refresh_timeout = options.refresh_timeout
56
+ @internal_sha = nil
57
+ @mutex = Mutex.new
38
58
  @task = nil
59
+ @logger = options.logger
39
60
 
40
61
  return unless redis_available?
41
62
 
42
- redis_opts = { url: connection_string, db: @namespace }
43
- redis_opts[:password] = @password if @password
44
- @redis = Redis.new(**redis_opts)
45
- load_from_redis
63
+ @redis = if connection_or_client.is_a?(String)
64
+ Redis.new(url: connection_or_client, db: options.db)
65
+ else
66
+ connection_or_client
67
+ end
68
+
69
+ config.register_raw_update_listener(self)
70
+
71
+ @logger&.debug("[featurehubsdk] started redis store")
72
+ Concurrent::Future.execute { load_from_redis }
46
73
  start_timer
47
74
  end
48
75
 
49
76
  def process_updates(features, source)
50
77
  return if source == SOURCE || !redis_available?
51
78
 
52
- features.each { |f| store_feature(f) }
79
+ incoming_sha = calculate_sha(features)
80
+ return if incoming_sha == @redis.get(sha_key)
81
+
82
+ perform_store_with_retry do |redis_features|
83
+ has_newer = features.any? do |f|
84
+ existing = redis_features.find { |rf| rf["id"] == f["id"] }
85
+ existing.nil? || version_of(f) > version_of(existing)
86
+ end
87
+ has_newer ? merge_features(redis_features, features) : nil
88
+ end
53
89
  end
54
90
 
55
91
  def process_update(feature, source)
56
92
  return if source == SOURCE || !redis_available?
57
93
 
58
- store_feature(feature)
94
+ perform_store_with_retry do |redis_features|
95
+ existing = redis_features.find { |f| f["id"] == feature["id"] }
96
+ next nil if existing && version_of(existing) >= version_of(feature)
97
+
98
+ merge_features(redis_features, [feature])
99
+ end
59
100
  end
60
101
 
61
102
  def delete_feature(feature, source)
62
103
  return if source == SOURCE || !redis_available? || !feature["id"]
63
104
 
64
- @redis.srem(ids_key, feature["id"])
65
- @redis.del(feature_key(feature["id"]))
105
+ perform_store_with_retry do |redis_features|
106
+ updated = redis_features.reject { |f| f["id"] == feature["id"] }
107
+ updated.length < redis_features.length ? updated : nil
108
+ end
66
109
  end
67
110
 
68
111
  def close
@@ -84,14 +127,10 @@ module FeatureHub
84
127
  end
85
128
 
86
129
  def load_from_redis
87
- ids = @redis.smembers(ids_key)
88
- return if ids.empty?
89
-
90
- features = ids.filter_map do |id|
91
- json = @redis.get(feature_key(id))
92
- JSON.parse(json) if json
93
- end
130
+ sha = @redis.get(sha_key)
131
+ @mutex.synchronize { @internal_sha = sha }
94
132
 
133
+ features = read_features_from_redis
95
134
  return if features.empty?
96
135
 
97
136
  @logger&.debug("[featurehubsdk] loading #{features.size} feature(s) from redis")
@@ -99,31 +138,88 @@ module FeatureHub
99
138
  end
100
139
 
101
140
  def start_timer
102
- @task = Concurrent::TimerTask.new(execution_interval: @timeout, run_now: false) do
103
- load_from_redis
141
+ @task = Concurrent::TimerTask.new(execution_interval: @refresh_timeout, run_now: false) do
142
+ check_for_updates
104
143
  end
105
144
  @task.execute
106
145
  end
107
146
 
108
- def store_feature(feature)
109
- return unless feature && feature["id"] && feature["key"]
147
+ def check_for_updates
148
+ return unless redis_available?
149
+
150
+ current_sha = @redis.get(sha_key)
151
+ stored_sha = @mutex.synchronize { @internal_sha }
152
+ return if current_sha == stored_sha
153
+
154
+ features = read_features_from_redis
155
+ @logger&.debug("[featurehubsdk] detected redis change, reloading #{features.size} feature(s)")
156
+ @repository.notify("features", features, SOURCE)
157
+ @mutex.synchronize { @internal_sha = current_sha }
158
+ end
110
159
 
111
- existing_json = @redis.get(feature_key(feature["id"]))
112
- if existing_json
113
- existing = JSON.parse(existing_json)
114
- return if existing["version"].to_i >= feature["version"].to_i
160
+ # Computes what to write by yielding the current Redis features to the block.
161
+ # The block returns the new features array to store, or nil to abort.
162
+ # Uses WATCH/MULTI/EXEC with retry to handle multi-process contention.
163
+ def perform_store_with_retry
164
+ attempt = 0
165
+ while attempt < @retry_update_count
166
+ redis_features = read_features_from_redis
167
+ new_features = yield(redis_features)
168
+ return if new_features.nil?
169
+
170
+ new_sha = calculate_sha(new_features)
171
+ current_internal = @mutex.synchronize { @internal_sha }
172
+ return if new_sha == current_internal
173
+
174
+ current_sha = @redis.get(sha_key)
175
+
176
+ if current_sha != current_internal
177
+ # Another process updated Redis — reload and recheck on next attempt
178
+ sleep(@backoff_timeout / 1000.0) unless attempt == @retry_update_count - 1
179
+ attempt += 1
180
+ next
181
+ end
182
+
183
+ stored = attempt_atomic_write(current_internal, new_sha, new_features)
184
+
185
+ if stored
186
+ @mutex.synchronize { @internal_sha = new_sha }
187
+ return
188
+ end
189
+
190
+ sleep(@backoff_timeout / 1000.0) unless attempt == @retry_update_count - 1
191
+ attempt += 1
115
192
  end
116
193
 
117
- @redis.sadd(ids_key, feature["id"])
118
- @redis.set(feature_key(feature["id"]), feature.to_json)
194
+ @logger&.warn("[featurehubsdk] failed to update redis after #{@retry_update_count} retries")
119
195
  end
120
196
 
121
- def ids_key
122
- "#{@prefix}_ids"
197
+ # Uses WATCH + MULTI/EXEC to atomically update both keys only if sha_key has not
198
+ # been modified since we read it. Returns true if the transaction committed.
199
+ def attempt_atomic_write(current_internal, new_sha, new_features)
200
+ @redis.watch(sha_key)
201
+ current = @redis.get(sha_key)
202
+
203
+ unless current == current_internal
204
+ @redis.unwatch
205
+ return false
206
+ end
207
+
208
+ result = @redis.multi do |tx|
209
+ tx.set(features_key, new_features.to_json)
210
+ tx.set(sha_key, new_sha)
211
+ end
212
+
213
+ result.is_a?(Array)
123
214
  end
124
215
 
125
- def feature_key(id)
126
- "#{@prefix}_#{id}"
216
+ def read_features_from_redis
217
+ json = @redis.get(features_key)
218
+ return [] unless json
219
+
220
+ JSON.parse(json)
221
+ rescue JSON::ParserError
222
+ []
127
223
  end
128
224
  end
129
225
  end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest"
4
+
5
+ module FeatureHub
6
+ module Sdk
7
+ # Shared pure helpers for MemcacheSessionStore and RedisSessionStore.
8
+ # Both stores rely on SHA256-based change detection and a two-key layout:
9
+ # ${prefix}_${environment_id} → JSON array of FeatureState objects
10
+ # ${prefix}_${environment_id}_sha → SHA256 of "id:version|..." for change detection
11
+ module SessionStoreHelpers
12
+ private
13
+
14
+ def calculate_sha(features)
15
+ parts = features.map { |f| "#{f["id"]}:#{version_of(f)}" }.join("|")
16
+ Digest::SHA256.hexdigest(parts)
17
+ end
18
+
19
+ def merge_features(base, updates)
20
+ result = base.dup
21
+ updates.each do |update|
22
+ idx = result.find_index { |f| f["id"] == update["id"] }
23
+ if idx
24
+ result[idx] = update if version_of(update) > version_of(result[idx])
25
+ else
26
+ result << update
27
+ end
28
+ end
29
+ result
30
+ end
31
+
32
+ def version_of(feature)
33
+ (feature["version"] || 0).to_i
34
+ end
35
+
36
+ def features_key
37
+ "#{@prefix}_#{@environment_id}"
38
+ end
39
+
40
+ def sha_key
41
+ "#{@prefix}_#{@environment_id}_sha"
42
+ end
43
+ end
44
+ end
45
+ end
@@ -3,6 +3,6 @@
3
3
  module FeatureHub
4
4
  # already documented elsewhere
5
5
  module Sdk
6
- VERSION = "2.0.1"
6
+ VERSION = "2.1.1"
7
7
  end
8
8
  end
@@ -15,7 +15,9 @@ require_relative "feature_hub/sdk/strategy_attributes"
15
15
  require_relative "feature_hub/sdk/local_yaml_interceptor"
16
16
  require_relative "feature_hub/sdk/raw_update_feature_listener"
17
17
  require_relative "feature_hub/sdk/local_yaml_store"
18
+ require_relative "feature_hub/sdk/session_store_helpers"
18
19
  require_relative "feature_hub/sdk/redis_session_store"
20
+ require_relative "feature_hub/sdk/memcache_session_store"
19
21
  require_relative "feature_hub/sdk/impl/strategy_wrappers"
20
22
  require_relative "feature_hub/sdk/poll_edge_service"
21
23
  require_relative "feature_hub/sdk/streaming_edge_service"
@@ -66,6 +66,8 @@ module FeatureHub
66
66
 
67
67
  def feature_type: -> String?
68
68
 
69
+ def feature_properties: -> Hash[String, String]
70
+
69
71
  def with_context: (ctx: ClientContext) -> FeatureStateHolder
70
72
 
71
73
  def update_feature_state: (feature_state: FeatureState) -> void
@@ -94,6 +96,8 @@ module FeatureHub
94
96
 
95
97
  def set?: () -> bool?
96
98
 
99
+ def phantom?: () -> bool
100
+
97
101
  private
98
102
 
99
103
  def top_feature_state: -> FeatureStateHolder
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: 2.0.1
4
+ version: 2.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Vowles
@@ -28,30 +28,36 @@ dependencies:
28
28
  name: faraday
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '2'
33
+ version: '0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '2'
40
+ version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: ld-eventsource
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: 2.5.1
48
+ - - "<"
49
+ - !ruby/object:Gem::Version
50
+ version: 2.7.0
48
51
  type: :runtime
49
52
  prerelease: false
50
53
  version_requirements: !ruby/object:Gem::Requirement
51
54
  requirements:
52
- - - "~>"
55
+ - - ">="
53
56
  - !ruby/object:Gem::Version
54
57
  version: 2.5.1
58
+ - - "<"
59
+ - !ruby/object:Gem::Version
60
+ version: 2.7.0
55
61
  - !ruby/object:Gem::Dependency
56
62
  name: murmurhash3
57
63
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +86,20 @@ dependencies:
80
86
  - - "~>"
81
87
  - !ruby/object:Gem::Version
82
88
  version: 2.0.0
89
+ - !ruby/object:Gem::Dependency
90
+ name: dalli
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '4'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '4'
83
103
  - !ruby/object:Gem::Dependency
84
104
  name: redis
85
105
  requirement: !ruby/object:Gem::Requirement
@@ -102,6 +122,7 @@ extensions: []
102
122
  extra_rdoc_files: []
103
123
  files:
104
124
  - ".claude/CLAUDE.md"
125
+ - ".dockerignore"
105
126
  - ".rspec"
106
127
  - ".rubocop.yml"
107
128
  - ".ruby-version"
@@ -111,6 +132,7 @@ files:
111
132
  - Gemfile.lock
112
133
  - LICENSE
113
134
  - LICENSE.txt
135
+ - Makefile
114
136
  - README.md
115
137
  - Rakefile
116
138
  - examples/rails_example/.env.example
@@ -209,16 +231,14 @@ files:
209
231
  - examples/sinatra/build.sh
210
232
  - examples/sinatra/conf/nginx.conf
211
233
  - examples/sinatra/conf/nsswitch.conf
234
+ - examples/sinatra/conf/webapp.conf
212
235
  - examples/sinatra/config.ru
213
236
  - examples/sinatra/docker-compose.yaml
214
237
  - examples/sinatra/docker_start.sh
215
238
  - examples/sinatra/feature-flags.yaml
216
- - examples/sinatra/sinatra.iml
217
239
  - examples/sinatra/start.sh
218
240
  - examples/sinatra/thin.ru
219
- - featurehub-ruby-sdk.iml
220
241
  - featurehub-sdk.gemspec
221
- - featurehub-sdk.iml
222
242
  - lib/feature_hub/sdk/context.rb
223
243
  - lib/feature_hub/sdk/feature_hub_config.rb
224
244
  - lib/feature_hub/sdk/feature_repository.rb
@@ -231,10 +251,12 @@ files:
231
251
  - lib/feature_hub/sdk/internal_feature_repository.rb
232
252
  - lib/feature_hub/sdk/local_yaml_interceptor.rb
233
253
  - lib/feature_hub/sdk/local_yaml_store.rb
254
+ - lib/feature_hub/sdk/memcache_session_store.rb
234
255
  - lib/feature_hub/sdk/percentage_calc.rb
235
256
  - lib/feature_hub/sdk/poll_edge_service.rb
236
257
  - lib/feature_hub/sdk/raw_update_feature_listener.rb
237
258
  - lib/feature_hub/sdk/redis_session_store.rb
259
+ - lib/feature_hub/sdk/session_store_helpers.rb
238
260
  - lib/feature_hub/sdk/strategy_attributes.rb
239
261
  - lib/feature_hub/sdk/streaming_edge_service.rb
240
262
  - lib/feature_hub/sdk/version.rb
@@ -1,43 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <module type="RUBY_MODULE" version="4">
3
- <component name="NewModuleRootManager" inherit-compiler-output="true">
4
- <exclude-output />
5
- <content url="file://$MODULE_DIR$" />
6
- <orderEntry type="jdk" jdkName="rbenv: 3.3.10" jdkType="RUBY_SDK" />
7
- <orderEntry type="sourceFolder" forTests="false" />
8
- <orderEntry type="library" scope="PROVIDED" name="addressable (v2.8.9, rbenv: 3.3.10) [gem]" level="application" />
9
- <orderEntry type="library" scope="PROVIDED" name="bundler (v2.3.15, rbenv: 3.3.10) [gem]" level="application" />
10
- <orderEntry type="library" scope="PROVIDED" name="concurrent-ruby (v1.3.6, rbenv: 3.3.10) [gem]" level="application" />
11
- <orderEntry type="library" scope="PROVIDED" name="connection_pool (v3.0.2, rbenv: 3.3.10) [gem]" level="application" />
12
- <orderEntry type="library" scope="PROVIDED" name="daemons (v1.4.1, rbenv: 3.3.10) [gem]" level="application" />
13
- <orderEntry type="library" scope="PROVIDED" name="domain_name (v0.6.20240107, rbenv: 3.3.10) [gem]" level="application" />
14
- <orderEntry type="library" scope="PROVIDED" name="eventmachine (v1.2.7, rbenv: 3.3.10) [gem]" level="application" />
15
- <orderEntry type="library" scope="PROVIDED" name="faraday (v2.14.1, rbenv: 3.3.10) [gem]" level="application" />
16
- <orderEntry type="library" scope="PROVIDED" name="faraday-net_http (v3.4.2, rbenv: 3.3.10) [gem]" level="application" />
17
- <orderEntry type="library" scope="PROVIDED" name="ffi (v1.17.3, rbenv: 3.3.10) [gem]" level="application" />
18
- <orderEntry type="library" scope="PROVIDED" name="ffi-compiler (v1.3.2, rbenv: 3.3.10) [gem]" level="application" />
19
- <orderEntry type="library" scope="PROVIDED" name="http (v5.3.1, rbenv: 3.3.10) [gem]" level="application" />
20
- <orderEntry type="library" scope="PROVIDED" name="http-cookie (v1.1.0, rbenv: 3.3.10) [gem]" level="application" />
21
- <orderEntry type="library" scope="PROVIDED" name="http-form_data (v2.3.0, rbenv: 3.3.10) [gem]" level="application" />
22
- <orderEntry type="library" scope="PROVIDED" name="json (v2.19.2, rbenv: 3.3.10) [gem]" level="application" />
23
- <orderEntry type="library" scope="PROVIDED" name="ld-eventsource (v2.5.1, rbenv: 3.3.10) [gem]" level="application" />
24
- <orderEntry type="library" scope="PROVIDED" name="llhttp-ffi (v0.5.1, rbenv: 3.3.10) [gem]" level="application" />
25
- <orderEntry type="library" scope="PROVIDED" name="logger (v1.7.0, rbenv: 3.3.10) [gem]" level="application" />
26
- <orderEntry type="library" scope="PROVIDED" name="murmurhash3 (v0.1.7, rbenv: 3.3.10) [gem]" level="application" />
27
- <orderEntry type="library" scope="PROVIDED" name="mustermann (v2.0.2, rbenv: 3.3.10) [gem]" level="application" />
28
- <orderEntry type="library" scope="PROVIDED" name="net-http (v0.9.1, rbenv: 3.3.10) [gem]" level="application" />
29
- <orderEntry type="library" scope="PROVIDED" name="public_suffix (v7.0.5, rbenv: 3.3.10) [gem]" level="application" />
30
- <orderEntry type="library" scope="PROVIDED" name="rack (v2.2.6.4, rbenv: 3.3.10) [gem]" level="application" />
31
- <orderEntry type="library" scope="PROVIDED" name="rack-protection (v2.2.3, rbenv: 3.3.10) [gem]" level="application" />
32
- <orderEntry type="library" scope="PROVIDED" name="rake (v13.3.1, rbenv: 3.3.10) [gem]" level="application" />
33
- <orderEntry type="library" scope="PROVIDED" name="redis (v5.4.1, rbenv: 3.3.10) [gem]" level="application" />
34
- <orderEntry type="library" scope="PROVIDED" name="redis-client (v0.28.0, rbenv: 3.3.10) [gem]" level="application" />
35
- <orderEntry type="library" scope="PROVIDED" name="ruby2_keywords (v0.0.5, rbenv: 3.3.10) [gem]" level="application" />
36
- <orderEntry type="library" scope="PROVIDED" name="sem_version (v2.0.1, rbenv: 3.3.10) [gem]" level="application" />
37
- <orderEntry type="library" scope="PROVIDED" name="shotgun (v0.9.2, rbenv: 3.3.10) [gem]" level="application" />
38
- <orderEntry type="library" scope="PROVIDED" name="sinatra (v2.2.3, rbenv: 3.3.10) [gem]" level="application" />
39
- <orderEntry type="library" scope="PROVIDED" name="thin (v1.8.1, rbenv: 3.3.10) [gem]" level="application" />
40
- <orderEntry type="library" scope="PROVIDED" name="tilt (v2.1.0, rbenv: 3.3.10) [gem]" level="application" />
41
- <orderEntry type="library" scope="PROVIDED" name="uri (v1.1.1, rbenv: 3.3.10) [gem]" level="application" />
42
- </component>
43
- </module>
@@ -1,9 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <module type="JAVA_MODULE" version="4">
3
- <component name="NewModuleRootManager" inherit-compiler-output="true">
4
- <exclude-output />
5
- <content url="file://$MODULE_DIR$" />
6
- <orderEntry type="inheritedJdk" />
7
- <orderEntry type="sourceFolder" forTests="false" />
8
- </component>
9
- </module>
data/featurehub-sdk.iml DELETED
@@ -1,87 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <module type="RUBY_MODULE" version="4">
3
- <component name="NewModuleRootManager" inherit-compiler-output="true">
4
- <exclude-output />
5
- <content url="file://$MODULE_DIR$" />
6
- <orderEntry type="jdk" jdkName="rbenv: 3.0.6" jdkType="RUBY_SDK" />
7
- <orderEntry type="sourceFolder" forTests="false" />
8
- <orderEntry type="library" scope="PROVIDED" name="addressable (v2.8.0, rbenv: 3.0.6) [gem]" level="application" />
9
- <orderEntry type="library" scope="PROVIDED" name="ast (v2.4.2, rbenv: 3.0.6) [gem]" level="application" />
10
- <orderEntry type="library" scope="PROVIDED" name="concurrent-ruby (v1.1.10, rbenv: 3.0.6) [gem]" level="application" />
11
- <orderEntry type="library" scope="PROVIDED" name="diff-lcs (v1.5.0, rbenv: 3.0.6) [gem]" level="application" />
12
- <orderEntry type="library" scope="PROVIDED" name="docile (v1.4.0, rbenv: 3.0.6) [gem]" level="application" />
13
- <orderEntry type="library" scope="PROVIDED" name="domain_name (v0.5.20190701, rbenv: 3.0.6) [gem]" level="application" />
14
- <orderEntry type="library" scope="PROVIDED" name="faraday (v2.3.0, rbenv: 3.0.6) [gem]" level="application" />
15
- <orderEntry type="library" scope="PROVIDED" name="faraday-net_http (v2.0.3, rbenv: 3.0.6) [gem]" level="application" />
16
- <orderEntry type="library" scope="PROVIDED" name="ffi (v1.15.5, rbenv: 3.0.6) [gem]" level="application" />
17
- <orderEntry type="library" scope="PROVIDED" name="ffi-compiler (v1.0.1, rbenv: 3.0.6) [gem]" level="application" />
18
- <orderEntry type="library" scope="PROVIDED" name="http (v5.0.4, rbenv: 3.0.6) [gem]" level="application" />
19
- <orderEntry type="library" scope="PROVIDED" name="http-cookie (v1.0.5, rbenv: 3.0.6) [gem]" level="application" />
20
- <orderEntry type="library" scope="PROVIDED" name="http-form_data (v2.3.0, rbenv: 3.0.6) [gem]" level="application" />
21
- <orderEntry type="library" scope="PROVIDED" name="ld-eventsource (v2.2.0, rbenv: 3.0.6) [gem]" level="application" />
22
- <orderEntry type="library" scope="PROVIDED" name="llhttp-ffi (v0.4.0, rbenv: 3.0.6) [gem]" level="application" />
23
- <orderEntry type="library" scope="PROVIDED" name="murmurhash3 (v0.1.6, rbenv: 3.0.6) [gem]" level="application" />
24
- <orderEntry type="library" scope="PROVIDED" name="parallel (v1.22.1, rbenv: 3.0.6) [gem]" level="application" />
25
- <orderEntry type="library" scope="PROVIDED" name="parser (v3.1.2.0, rbenv: 3.0.6) [gem]" level="application" />
26
- <orderEntry type="library" scope="PROVIDED" name="public_suffix (v4.0.7, rbenv: 3.0.6) [gem]" level="application" />
27
- <orderEntry type="library" scope="PROVIDED" name="rainbow (v3.1.1, rbenv: 3.0.6) [gem]" level="application" />
28
- <orderEntry type="library" scope="PROVIDED" name="rake (v13.0.6, rbenv: 3.0.6) [gem]" level="application" />
29
- <orderEntry type="library" scope="PROVIDED" name="regexp_parser (v2.5.0, rbenv: 3.0.6) [gem]" level="application" />
30
- <orderEntry type="library" scope="PROVIDED" name="rexml (v3.2.5, rbenv: 3.0.6) [gem]" level="application" />
31
- <orderEntry type="library" scope="PROVIDED" name="rspec (v3.11.0, rbenv: 3.0.6) [gem]" level="application" />
32
- <orderEntry type="library" scope="PROVIDED" name="rspec-core (v3.11.0, rbenv: 3.0.6) [gem]" level="application" />
33
- <orderEntry type="library" scope="PROVIDED" name="rspec-expectations (v3.11.0, rbenv: 3.0.6) [gem]" level="application" />
34
- <orderEntry type="library" scope="PROVIDED" name="rspec-mocks (v3.11.1, rbenv: 3.0.6) [gem]" level="application" />
35
- <orderEntry type="library" scope="PROVIDED" name="rspec-support (v3.11.0, rbenv: 3.0.6) [gem]" level="application" />
36
- <orderEntry type="library" scope="PROVIDED" name="rubocop (v1.30.1, rbenv: 3.0.6) [gem]" level="application" />
37
- <orderEntry type="library" scope="PROVIDED" name="rubocop-ast (v1.18.0, rbenv: 3.0.6) [gem]" level="application" />
38
- <orderEntry type="library" scope="PROVIDED" name="ruby-progressbar (v1.11.0, rbenv: 3.0.6) [gem]" level="application" />
39
- <orderEntry type="library" scope="PROVIDED" name="ruby2_keywords (v0.0.5, rbenv: 3.0.6) [gem]" level="application" />
40
- <orderEntry type="library" scope="PROVIDED" name="sem_version (v2.0.1, rbenv: 3.0.6) [gem]" level="application" />
41
- <orderEntry type="library" scope="PROVIDED" name="simplecov (v0.21.2, rbenv: 3.0.6) [gem]" level="application" />
42
- <orderEntry type="library" scope="PROVIDED" name="simplecov-html (v0.12.3, rbenv: 3.0.6) [gem]" level="application" />
43
- <orderEntry type="library" scope="PROVIDED" name="simplecov_json_formatter (v0.1.4, rbenv: 3.0.6) [gem]" level="application" />
44
- <orderEntry type="library" scope="PROVIDED" name="unf (v0.1.4, rbenv: 3.0.6) [gem]" level="application" />
45
- <orderEntry type="library" scope="PROVIDED" name="unf_ext (v0.0.8.2, rbenv: 3.0.6) [gem]" level="application" />
46
- <orderEntry type="library" scope="PROVIDED" name="unicode-display_width (v2.1.0, rbenv: 3.0.6) [gem]" level="application" />
47
- </component>
48
- <component name="RakeTasksCache">
49
- <option name="myRootTask">
50
- <RakeTaskImpl id="rake">
51
- <subtasks>
52
- <RakeTaskImpl description="Build featurehub-sdk-1.2.3.gem into the pkg directory" fullCommand="build" id="build" />
53
- <RakeTaskImpl id="build">
54
- <subtasks>
55
- <RakeTaskImpl description="Generate SHA512 checksum if featurehub-sdk-1.2.3.gem into the checksums directory" fullCommand="build:checksum" id="checksum" />
56
- </subtasks>
57
- </RakeTaskImpl>
58
- <RakeTaskImpl description="Remove any temporary products" fullCommand="clean" id="clean" />
59
- <RakeTaskImpl description="Remove any generated files" fullCommand="clobber" id="clobber" />
60
- <RakeTaskImpl description="Build and install featurehub-sdk-1.2.3.gem into system gems" fullCommand="install" id="install" />
61
- <RakeTaskImpl id="install">
62
- <subtasks>
63
- <RakeTaskImpl description="Build and install featurehub-sdk-1.2.3.gem into system gems without network access" fullCommand="install:local" id="local" />
64
- </subtasks>
65
- </RakeTaskImpl>
66
- <RakeTaskImpl description="Create tag v1.2.3 and build and push featurehub-sdk-1.2.3.gem to https://rubygems.org" fullCommand="release[remote]" id="release[remote]" />
67
- <RakeTaskImpl description="Run RuboCop" fullCommand="rubocop" id="rubocop" />
68
- <RakeTaskImpl id="rubocop">
69
- <subtasks>
70
- <RakeTaskImpl description="Autocorrect RuboCop offenses" fullCommand="rubocop:auto_correct" id="auto_correct" />
71
- </subtasks>
72
- </RakeTaskImpl>
73
- <RakeTaskImpl description="Run RSpec code examples" fullCommand="spec" id="spec" />
74
- <RakeTaskImpl description="" fullCommand="default" id="default" />
75
- <RakeTaskImpl description="" fullCommand="release" id="release" />
76
- <RakeTaskImpl id="release">
77
- <subtasks>
78
- <RakeTaskImpl description="" fullCommand="release:guard_clean" id="guard_clean" />
79
- <RakeTaskImpl description="" fullCommand="release:rubygem_push" id="rubygem_push" />
80
- <RakeTaskImpl description="" fullCommand="release:source_control_push" id="source_control_push" />
81
- </subtasks>
82
- </RakeTaskImpl>
83
- </subtasks>
84
- </RakeTaskImpl>
85
- </option>
86
- </component>
87
- </module>