sdk-reforge 1.9.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.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/.envrc.sample +3 -0
  3. data/.github/CODEOWNERS +2 -0
  4. data/.github/pull_request_template.md +8 -0
  5. data/.github/workflows/ruby.yml +48 -0
  6. data/.gitmodules +3 -0
  7. data/.rubocop.yml +13 -0
  8. data/.tool-versions +1 -0
  9. data/CHANGELOG.md +257 -0
  10. data/CODEOWNERS +1 -0
  11. data/Gemfile +29 -0
  12. data/Gemfile.lock +182 -0
  13. data/LICENSE.txt +20 -0
  14. data/README.md +105 -0
  15. data/Rakefile +63 -0
  16. data/VERSION +1 -0
  17. data/compile_protos.sh +20 -0
  18. data/dev/allocation_stats +60 -0
  19. data/dev/benchmark +40 -0
  20. data/dev/console +12 -0
  21. data/dev/script_setup.rb +18 -0
  22. data/lib/prefab_pb.rb +77 -0
  23. data/lib/reforge/caching_http_connection.rb +95 -0
  24. data/lib/reforge/client.rb +133 -0
  25. data/lib/reforge/config_client.rb +275 -0
  26. data/lib/reforge/config_client_presenter.rb +18 -0
  27. data/lib/reforge/config_loader.rb +67 -0
  28. data/lib/reforge/config_resolver.rb +84 -0
  29. data/lib/reforge/config_value_unwrapper.rb +123 -0
  30. data/lib/reforge/config_value_wrapper.rb +18 -0
  31. data/lib/reforge/context.rb +241 -0
  32. data/lib/reforge/context_shape.rb +20 -0
  33. data/lib/reforge/context_shape_aggregator.rb +70 -0
  34. data/lib/reforge/criteria_evaluator.rb +345 -0
  35. data/lib/reforge/duration.rb +58 -0
  36. data/lib/reforge/encryption.rb +65 -0
  37. data/lib/reforge/error.rb +6 -0
  38. data/lib/reforge/errors/env_var_parse_error.rb +11 -0
  39. data/lib/reforge/errors/initialization_timeout_error.rb +12 -0
  40. data/lib/reforge/errors/invalid_sdk_key_error.rb +19 -0
  41. data/lib/reforge/errors/missing_default_error.rb +13 -0
  42. data/lib/reforge/errors/missing_env_var_error.rb +11 -0
  43. data/lib/reforge/errors/uninitialized_error.rb +13 -0
  44. data/lib/reforge/evaluation.rb +53 -0
  45. data/lib/reforge/evaluation_summary_aggregator.rb +86 -0
  46. data/lib/reforge/example_contexts_aggregator.rb +77 -0
  47. data/lib/reforge/exponential_backoff.rb +21 -0
  48. data/lib/reforge/feature_flag_client.rb +43 -0
  49. data/lib/reforge/fixed_size_hash.rb +14 -0
  50. data/lib/reforge/http_connection.rb +45 -0
  51. data/lib/reforge/internal_logger.rb +43 -0
  52. data/lib/reforge/javascript_stub.rb +99 -0
  53. data/lib/reforge/local_config_parser.rb +151 -0
  54. data/lib/reforge/murmer3.rb +50 -0
  55. data/lib/reforge/options.rb +191 -0
  56. data/lib/reforge/periodic_sync.rb +74 -0
  57. data/lib/reforge/prefab.rb +120 -0
  58. data/lib/reforge/rate_limit_cache.rb +41 -0
  59. data/lib/reforge/resolved_config_presenter.rb +86 -0
  60. data/lib/reforge/semver.rb +132 -0
  61. data/lib/reforge/sse_config_client.rb +112 -0
  62. data/lib/reforge/time_helpers.rb +7 -0
  63. data/lib/reforge/weighted_value_resolver.rb +42 -0
  64. data/lib/reforge/yaml_config_parser.rb +34 -0
  65. data/lib/reforge-sdk.rb +57 -0
  66. data/test/fixtures/datafile.json +87 -0
  67. data/test/integration_test.rb +171 -0
  68. data/test/integration_test_helpers.rb +114 -0
  69. data/test/support/common_helpers.rb +201 -0
  70. data/test/support/mock_base_client.rb +41 -0
  71. data/test/support/mock_config_client.rb +19 -0
  72. data/test/support/mock_config_loader.rb +1 -0
  73. data/test/test_caching_http_connection.rb +218 -0
  74. data/test/test_client.rb +351 -0
  75. data/test/test_config_client.rb +84 -0
  76. data/test/test_config_loader.rb +82 -0
  77. data/test/test_config_resolver.rb +502 -0
  78. data/test/test_config_value_unwrapper.rb +270 -0
  79. data/test/test_config_value_wrapper.rb +42 -0
  80. data/test/test_context.rb +271 -0
  81. data/test/test_context_shape.rb +50 -0
  82. data/test/test_context_shape_aggregator.rb +150 -0
  83. data/test/test_criteria_evaluator.rb +1180 -0
  84. data/test/test_duration.rb +37 -0
  85. data/test/test_encryption.rb +16 -0
  86. data/test/test_evaluation_summary_aggregator.rb +162 -0
  87. data/test/test_example_contexts_aggregator.rb +233 -0
  88. data/test/test_exponential_backoff.rb +18 -0
  89. data/test/test_feature_flag_client.rb +16 -0
  90. data/test/test_fixed_size_hash.rb +119 -0
  91. data/test/test_helper.rb +17 -0
  92. data/test/test_integration.rb +75 -0
  93. data/test/test_internal_logger.rb +25 -0
  94. data/test/test_javascript_stub.rb +176 -0
  95. data/test/test_local_config_parser.rb +147 -0
  96. data/test/test_logger_initialization.rb +12 -0
  97. data/test/test_options.rb +93 -0
  98. data/test/test_prefab.rb +16 -0
  99. data/test/test_rate_limit_cache.rb +44 -0
  100. data/test/test_semver.rb +108 -0
  101. data/test/test_sse_config_client.rb +211 -0
  102. data/test/test_weighted_value_resolver.rb +71 -0
  103. metadata +345 -0
@@ -0,0 +1,211 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+ require 'webrick'
5
+ require 'ostruct'
6
+
7
+ class TestSSEConfigClient < Minitest::Test
8
+ def test_client
9
+ sources = [
10
+ 'https://primary.goatsofreforge.com'
11
+ ]
12
+
13
+ options = Reforge::Options.new(sources: sources, sdk_key: ENV.fetch('REFORGE_INTEGRATION_TEST_SDK_KEY', nil))
14
+
15
+ config_loader = OpenStruct.new(highwater_mark: 4)
16
+
17
+ client = Reforge::SSEConfigClient.new(options, config_loader)
18
+
19
+ assert_equal 4, client.headers['Last-Event-ID']
20
+ assert_equal "https://stream.goatsofreforge.com", client.source
21
+
22
+ result = nil
23
+
24
+ # fake our load_configs block
25
+ client.start do |c, _event, source|
26
+ result = c
27
+ assert_equal :sse, source
28
+ end
29
+
30
+ wait_for -> { !result.nil? }
31
+
32
+ assert result.configs.size > 30
33
+ ensure
34
+ client.close
35
+ end
36
+
37
+ def test_failing_over
38
+ sources = [
39
+ 'https://does.not.exist.staging-prefab.cloud/',
40
+ 'https://api.goatsofreforge.com/'
41
+ ]
42
+
43
+ prefab_options = Reforge::Options.new(sources: sources, sdk_key: ENV.fetch('REFORGE_INTEGRATION_TEST_SDK_KEY', nil))
44
+
45
+ config_loader = OpenStruct.new(highwater_mark: 4)
46
+
47
+ sse_options = Reforge::SSEConfigClient::Options.new(seconds_between_new_connection: 0.01, sleep_delay_for_new_connection_check: 0.01)
48
+
49
+ client = Reforge::SSEConfigClient.new(prefab_options, config_loader, sse_options)
50
+
51
+ assert_equal 4, client.headers['Last-Event-ID']
52
+
53
+ result = nil
54
+
55
+ # fake our load_configs block
56
+ client.start do |c, _event, source|
57
+ result = c
58
+ assert_equal :sse, source
59
+ end
60
+
61
+ wait_for -> { !result.nil? }
62
+
63
+ assert result.configs.size > 30
64
+ ensure
65
+ client.close
66
+
67
+ assert_logged [
68
+ %r{failed to connect: .*https://does.not.exist},
69
+ /HTTP::ConnectionError/
70
+ ]
71
+ end
72
+
73
+ def test_recovering_from_disconnection
74
+ server, = start_webrick_server(4567, DisconnectingEndpoint)
75
+
76
+ config_loader = OpenStruct.new(highwater_mark: 4)
77
+
78
+ prefab_options = OpenStruct.new(sse_sources: ['http://localhost:4567'], sdk_key: 'test')
79
+ last_event_id = nil
80
+ client = nil
81
+
82
+ begin
83
+ Thread.new do
84
+ server.start
85
+ end
86
+
87
+ sse_options = Reforge::SSEConfigClient::Options.new(
88
+ sse_default_reconnect_time: 0.1
89
+ )
90
+ client = Reforge::SSEConfigClient.new(prefab_options, config_loader, sse_options)
91
+
92
+ client.start do |_configs, event, _source|
93
+ last_event_id = event.id.to_i
94
+ end
95
+
96
+ wait_for -> { last_event_id && last_event_id > 1 }
97
+ ensure
98
+ client.close
99
+ server.stop
100
+
101
+ refute_nil last_event_id, 'Expected to have received an event'
102
+ assert last_event_id > 1, 'Expected to have received multiple events (indicating a retry)'
103
+ end
104
+ end
105
+
106
+ def test_recovering_from_an_error
107
+ log_output = StringIO.new
108
+ logger = Logger.new(log_output)
109
+
110
+ server, = start_webrick_server(4568, ErroringEndpoint)
111
+
112
+ config_loader = OpenStruct.new(highwater_mark: 4)
113
+
114
+ prefab_options = OpenStruct.new(sse_sources: ['http://localhost:4568'], sdk_key: 'test')
115
+ last_event_id = nil
116
+ client = nil
117
+
118
+ begin
119
+ Thread.new do
120
+ server.start
121
+ end
122
+
123
+ sse_options = Reforge::SSEConfigClient::Options.new(
124
+ sse_default_reconnect_time: 0.1,
125
+ seconds_between_new_connection: 0.1,
126
+ sleep_delay_for_new_connection_check: 0.1,
127
+ errors_to_close_connection: [SSE::Errors::HTTPStatusError]
128
+ )
129
+ client = Reforge::SSEConfigClient.new(prefab_options, config_loader, sse_options, logger)
130
+
131
+ client.start do |_configs, event, _source|
132
+ last_event_id = event.id.to_i
133
+ end
134
+
135
+ wait_for -> { last_event_id && last_event_id > 2 }
136
+ ensure
137
+ server.stop
138
+ client.close
139
+
140
+ refute_nil last_event_id, 'Expected to have received an event'
141
+ assert last_event_id > 2, 'Expected to have received multiple events (indicating a reconnect)'
142
+ end
143
+
144
+ log_lines = log_output.string.split("\n")
145
+
146
+ assert_match(/SSE Streaming Connect/, log_lines[0])
147
+ assert_match(/SSE Streaming Error/, log_lines[1], 'Expected to have logged an error. If this starts failing after an ld-eventsource upgrade, you might need to tweak NUMBER_OF_FAILURES below')
148
+ assert_match(/Closing SSE connection/, log_lines[2])
149
+ assert_match(/Reconnecting SSE client/, log_lines[3])
150
+ assert_match(/SSE Streaming Connect/, log_lines[4])
151
+ end
152
+
153
+ def start_webrick_server(port, endpoint_class)
154
+ log_string = StringIO.new
155
+ logger = WEBrick::Log.new(log_string)
156
+ server = WEBrick::HTTPServer.new(Port: port, Logger: logger, AccessLog: [])
157
+ server.mount '/api/v2/sse/config', endpoint_class
158
+
159
+ [server, log_string]
160
+ end
161
+
162
+ module SharedEndpointLogic
163
+ def event_id
164
+ @@event_id ||= 0
165
+ @@event_id += 1
166
+ end
167
+
168
+ def setup_response(response)
169
+ response.status = 200
170
+ response['Content-Type'] = 'text/event-stream'
171
+ response['Cache-Control'] = 'no-cache'
172
+ response['Connection'] = 'keep-alive'
173
+
174
+ response.chunked = false
175
+ end
176
+ end
177
+
178
+ class DisconnectingEndpoint < WEBrick::HTTPServlet::AbstractServlet
179
+ include SharedEndpointLogic
180
+
181
+ def do_GET(_request, response)
182
+ setup_response(response)
183
+
184
+ output = response.body
185
+
186
+ output << "id: #{event_id}\n"
187
+ output << "event: message\n"
188
+ output << "data: CmYIu8fh4YaO0x4QZBo0bG9nLWxldmVsLmNsb3VkLnByZWZhYi5zZXJ2ZXIubG9nZ2luZy5FdmVudFByb2Nlc3NvciIfCAESG2phbWVzLmtlYmluZ2VyQHByZWZhYi5jbG91ZDgGSAkSDQhkELvH4eGGjtMeGGU=\n\n"
189
+ end
190
+ end
191
+
192
+ class ErroringEndpoint < WEBrick::HTTPServlet::AbstractServlet
193
+ include SharedEndpointLogic
194
+ NUMBER_OF_FAILURES = 5
195
+
196
+ def do_GET(_request, response)
197
+ setup_response(response)
198
+
199
+ output = response.body
200
+
201
+ output << "id: #{event_id}\n"
202
+
203
+ if event_id < NUMBER_OF_FAILURES
204
+ raise 'ErroringEndpoint' # This manifests as an SSE::Errors::HTTPStatusError
205
+ end
206
+
207
+ output << "event: message\n"
208
+ output << "data: CmYIu8fh4YaO0x4QZBo0bG9nLWxldmVsLmNsb3VkLnByZWZhYi5zZXJ2ZXIubG9nZ2luZy5FdmVudFByb2Nlc3NvciIfCAESG2phbWVzLmtlYmluZ2VyQHByZWZhYi5jbG91ZDgGSAkSDQhkELvH4eGGjtMeGGU=\n\n"
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class TestWeightedValueResolver < Minitest::Test
6
+ KEY = 'config_key'
7
+
8
+ def test_resolving_single_value
9
+ values = weighted_values([['abc', 1]])
10
+ resolver = Reforge::WeightedValueResolver.new(values, KEY, nil)
11
+ assert_equal 'abc', resolver.resolve[0].value.string
12
+ assert_equal 0, resolver.resolve[1]
13
+ end
14
+
15
+ def test_resolving_multiple_values_evenly_distributed
16
+ values = weighted_values([['abc', 1], ['def', 1]])
17
+
18
+ resolver = Reforge::WeightedValueResolver.new(values, KEY, 'user:001')
19
+ assert_equal 'abc', resolver.resolve[0].value.string
20
+ assert_equal 0, resolver.resolve[1]
21
+
22
+ resolver = Reforge::WeightedValueResolver.new(values, KEY, 'user:456')
23
+ assert_equal 'def', resolver.resolve[0].value.string
24
+ assert_equal 1, resolver.resolve[1]
25
+ end
26
+
27
+ def test_resolving_multiple_values_unevenly_distributed
28
+ values = weighted_values([['abc', 1], ['def', 98], ['ghi', 1]])
29
+
30
+ resolver = Reforge::WeightedValueResolver.new(values, KEY, 'user:456')
31
+ assert_equal 'def', resolver.resolve[0].value.string
32
+ assert_equal 1, resolver.resolve[1]
33
+
34
+ resolver = Reforge::WeightedValueResolver.new(values, KEY, 'user:103')
35
+ assert_equal 'ghi', resolver.resolve[0].value.string
36
+ assert_equal 2, resolver.resolve[1]
37
+
38
+ resolver = Reforge::WeightedValueResolver.new(values, KEY, 'user:119')
39
+ assert_equal 'abc', resolver.resolve[0].value.string
40
+ assert_equal 0, resolver.resolve[1]
41
+ end
42
+
43
+ def test_resolving_multiple_values_with_simulation
44
+ values = weighted_values([['abc', 1], ['def', 98], ['ghi', 1]])
45
+ results = {}
46
+
47
+ 10_000.times do |i|
48
+ result = Reforge::WeightedValueResolver.new(values, KEY, "user:#{i}").resolve[0].value.string
49
+ results[result] ||= 0
50
+ results[result] += 1
51
+ end
52
+
53
+ assert_in_delta 100, results['abc'], 20
54
+ assert_in_delta 9800, results['def'], 50
55
+ assert_in_delta 100, results['ghi'], 20
56
+ end
57
+
58
+ private
59
+
60
+ def weighted_values(values_and_weights)
61
+ values_and_weights.map do |value, weight|
62
+ weighted_value(value, weight)
63
+ end
64
+ end
65
+
66
+ def weighted_value(string, weight)
67
+ PrefabProto::WeightedValue.new(
68
+ value: PrefabProto::ConfigValue.new(string: string), weight: weight
69
+ )
70
+ end
71
+ end
metadata ADDED
@@ -0,0 +1,345 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sdk-reforge
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.9.0
5
+ platform: ruby
6
+ authors:
7
+ - Jeff Dwyer
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2025-09-23 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: concurrent-ruby
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.0'
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: 1.0.5
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: '1.0'
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: 1.0.5
32
+ - !ruby/object:Gem::Dependency
33
+ name: faraday
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: googleapis-common-protos-types
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ type: :runtime
54
+ prerelease: false
55
+ version_requirements: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ - !ruby/object:Gem::Dependency
61
+ name: google-protobuf
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ type: :runtime
68
+ prerelease: false
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ - !ruby/object:Gem::Dependency
75
+ name: ld-eventsource
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ type: :runtime
82
+ prerelease: false
83
+ version_requirements: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ - !ruby/object:Gem::Dependency
89
+ name: uuid
90
+ requirement: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ type: :runtime
96
+ prerelease: false
97
+ version_requirements: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ - !ruby/object:Gem::Dependency
103
+ name: activesupport
104
+ requirement: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '4'
109
+ type: :runtime
110
+ prerelease: false
111
+ version_requirements: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '4'
116
+ - !ruby/object:Gem::Dependency
117
+ name: semantic_logger
118
+ requirement: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - "!="
121
+ - !ruby/object:Gem::Version
122
+ version: 4.16.0
123
+ type: :runtime
124
+ prerelease: false
125
+ version_requirements: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "!="
128
+ - !ruby/object:Gem::Version
129
+ version: 4.16.0
130
+ - !ruby/object:Gem::Dependency
131
+ name: allocation_stats
132
+ requirement: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ type: :development
138
+ prerelease: false
139
+ version_requirements: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ - !ruby/object:Gem::Dependency
145
+ name: benchmark-ips
146
+ requirement: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ type: :development
152
+ prerelease: false
153
+ version_requirements: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ - !ruby/object:Gem::Dependency
159
+ name: bundler
160
+ requirement: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ type: :development
166
+ prerelease: false
167
+ version_requirements: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ version: '0'
172
+ - !ruby/object:Gem::Dependency
173
+ name: juwelier
174
+ requirement: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - "~>"
177
+ - !ruby/object:Gem::Version
178
+ version: 2.4.9
179
+ type: :development
180
+ prerelease: false
181
+ version_requirements: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - "~>"
184
+ - !ruby/object:Gem::Version
185
+ version: 2.4.9
186
+ - !ruby/object:Gem::Dependency
187
+ name: rdoc
188
+ requirement: !ruby/object:Gem::Requirement
189
+ requirements:
190
+ - - ">="
191
+ - !ruby/object:Gem::Version
192
+ version: '0'
193
+ type: :development
194
+ prerelease: false
195
+ version_requirements: !ruby/object:Gem::Requirement
196
+ requirements:
197
+ - - ">="
198
+ - !ruby/object:Gem::Version
199
+ version: '0'
200
+ - !ruby/object:Gem::Dependency
201
+ name: simplecov
202
+ requirement: !ruby/object:Gem::Requirement
203
+ requirements:
204
+ - - ">="
205
+ - !ruby/object:Gem::Version
206
+ version: '0'
207
+ type: :development
208
+ prerelease: false
209
+ version_requirements: !ruby/object:Gem::Requirement
210
+ requirements:
211
+ - - ">="
212
+ - !ruby/object:Gem::Version
213
+ version: '0'
214
+ description: Feature Flags, Live Config as a service
215
+ email: jeff.dwyer@reforge.com.cloud
216
+ executables: []
217
+ extensions: []
218
+ extra_rdoc_files:
219
+ - CHANGELOG.md
220
+ - LICENSE.txt
221
+ - README.md
222
+ files:
223
+ - ".envrc.sample"
224
+ - ".github/CODEOWNERS"
225
+ - ".github/pull_request_template.md"
226
+ - ".github/workflows/ruby.yml"
227
+ - ".gitmodules"
228
+ - ".rubocop.yml"
229
+ - ".tool-versions"
230
+ - CHANGELOG.md
231
+ - CODEOWNERS
232
+ - Gemfile
233
+ - Gemfile.lock
234
+ - LICENSE.txt
235
+ - README.md
236
+ - Rakefile
237
+ - VERSION
238
+ - compile_protos.sh
239
+ - dev/allocation_stats
240
+ - dev/benchmark
241
+ - dev/console
242
+ - dev/script_setup.rb
243
+ - lib/prefab_pb.rb
244
+ - lib/reforge-sdk.rb
245
+ - lib/reforge/caching_http_connection.rb
246
+ - lib/reforge/client.rb
247
+ - lib/reforge/config_client.rb
248
+ - lib/reforge/config_client_presenter.rb
249
+ - lib/reforge/config_loader.rb
250
+ - lib/reforge/config_resolver.rb
251
+ - lib/reforge/config_value_unwrapper.rb
252
+ - lib/reforge/config_value_wrapper.rb
253
+ - lib/reforge/context.rb
254
+ - lib/reforge/context_shape.rb
255
+ - lib/reforge/context_shape_aggregator.rb
256
+ - lib/reforge/criteria_evaluator.rb
257
+ - lib/reforge/duration.rb
258
+ - lib/reforge/encryption.rb
259
+ - lib/reforge/error.rb
260
+ - lib/reforge/errors/env_var_parse_error.rb
261
+ - lib/reforge/errors/initialization_timeout_error.rb
262
+ - lib/reforge/errors/invalid_sdk_key_error.rb
263
+ - lib/reforge/errors/missing_default_error.rb
264
+ - lib/reforge/errors/missing_env_var_error.rb
265
+ - lib/reforge/errors/uninitialized_error.rb
266
+ - lib/reforge/evaluation.rb
267
+ - lib/reforge/evaluation_summary_aggregator.rb
268
+ - lib/reforge/example_contexts_aggregator.rb
269
+ - lib/reforge/exponential_backoff.rb
270
+ - lib/reforge/feature_flag_client.rb
271
+ - lib/reforge/fixed_size_hash.rb
272
+ - lib/reforge/http_connection.rb
273
+ - lib/reforge/internal_logger.rb
274
+ - lib/reforge/javascript_stub.rb
275
+ - lib/reforge/local_config_parser.rb
276
+ - lib/reforge/murmer3.rb
277
+ - lib/reforge/options.rb
278
+ - lib/reforge/periodic_sync.rb
279
+ - lib/reforge/prefab.rb
280
+ - lib/reforge/rate_limit_cache.rb
281
+ - lib/reforge/resolved_config_presenter.rb
282
+ - lib/reforge/semver.rb
283
+ - lib/reforge/sse_config_client.rb
284
+ - lib/reforge/time_helpers.rb
285
+ - lib/reforge/weighted_value_resolver.rb
286
+ - lib/reforge/yaml_config_parser.rb
287
+ - test/fixtures/datafile.json
288
+ - test/integration_test.rb
289
+ - test/integration_test_helpers.rb
290
+ - test/support/common_helpers.rb
291
+ - test/support/mock_base_client.rb
292
+ - test/support/mock_config_client.rb
293
+ - test/support/mock_config_loader.rb
294
+ - test/test_caching_http_connection.rb
295
+ - test/test_client.rb
296
+ - test/test_config_client.rb
297
+ - test/test_config_loader.rb
298
+ - test/test_config_resolver.rb
299
+ - test/test_config_value_unwrapper.rb
300
+ - test/test_config_value_wrapper.rb
301
+ - test/test_context.rb
302
+ - test/test_context_shape.rb
303
+ - test/test_context_shape_aggregator.rb
304
+ - test/test_criteria_evaluator.rb
305
+ - test/test_duration.rb
306
+ - test/test_encryption.rb
307
+ - test/test_evaluation_summary_aggregator.rb
308
+ - test/test_example_contexts_aggregator.rb
309
+ - test/test_exponential_backoff.rb
310
+ - test/test_feature_flag_client.rb
311
+ - test/test_fixed_size_hash.rb
312
+ - test/test_helper.rb
313
+ - test/test_integration.rb
314
+ - test/test_internal_logger.rb
315
+ - test/test_javascript_stub.rb
316
+ - test/test_local_config_parser.rb
317
+ - test/test_logger_initialization.rb
318
+ - test/test_options.rb
319
+ - test/test_prefab.rb
320
+ - test/test_rate_limit_cache.rb
321
+ - test/test_semver.rb
322
+ - test/test_sse_config_client.rb
323
+ - test/test_weighted_value_resolver.rb
324
+ homepage: http://github.com/ReforgeHQ/sdk-ruby
325
+ licenses:
326
+ - MIT
327
+ metadata: {}
328
+ rdoc_options: []
329
+ require_paths:
330
+ - lib
331
+ required_ruby_version: !ruby/object:Gem::Requirement
332
+ requirements:
333
+ - - ">="
334
+ - !ruby/object:Gem::Version
335
+ version: '0'
336
+ required_rubygems_version: !ruby/object:Gem::Requirement
337
+ requirements:
338
+ - - ">="
339
+ - !ruby/object:Gem::Version
340
+ version: '0'
341
+ requirements: []
342
+ rubygems_version: 3.6.9
343
+ specification_version: 4
344
+ summary: Reforge Launch Ruby Infrastructure
345
+ test_files: []