unleash 6.3.1 → 6.4.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: c9c5d4638648ffd4a6891f6b6f851437028af770b417d020b6ae852f24d5ead7
4
- data.tar.gz: 9933cfff16b53947a37385f0193dd61b6955d9010865cb9ad02e193d3363afdf
3
+ metadata.gz: a76eb26bedcade3ed88389aa909f7ead99177e336509bc4fd99d85c4703f1405
4
+ data.tar.gz: 43410bf468ac4fd3b56af79a354eb84794c40c67b6c2d96c27bdf26772b1c54d
5
5
  SHA512:
6
- metadata.gz: 4f0ab91b15331353b5d3024f102a9915c68c62471da230057c363dfbd0098b14619ec0df7cc63a5aec906760d25f5dcd70af7f6490c57e7f1ada3a1878eaac9c
7
- data.tar.gz: 3bf0dd46e6f23715e5495fd3fcf09940cd39722d32d938730102a45661813efefc164bb3e84b55410db753e5531b4e57bdea3476ad263500d8a077f832e968ca
6
+ metadata.gz: 3e43cbef4ba85e84265f10b9414eba95c92aeb59792a40318e15df37313ee5a6b99ce3c3e3e89d465c56e40fff92e61ed4115766d59b6ee9147c2d2c5920ebf7
7
+ data.tar.gz: 2401fa3bf2d0f824d7da3cf2e01cc4c20fe740f26d2019f89554adbbc67fdc5c0f4fa65003e143391b14ea2466314db36852edd0ccefca8178f6545e3186170d
data/CHANGELOG.md CHANGED
@@ -13,7 +13,15 @@ Note: These changes are not considered notable:
13
13
 
14
14
  ## [Unreleased]
15
15
 
16
- ## [6.3.0] - 2025-05-27
16
+ ## [6.4.0] - 2025-08-12
17
+ ### Added
18
+ - Experimental streaming support
19
+
20
+ #### Changed
21
+ - SDK registration name
22
+ - base64 dependency version
23
+
24
+ ## [6.3.1] - 2025-05-27
17
25
  ### Fixed
18
26
  - Upgraded Yggdrasil engine to fix a memory leak in metrics.
19
27
 
data/README.md CHANGED
@@ -1,10 +1,12 @@
1
1
  # Unleash::Client
2
2
 
3
- ![Build Status](https://github.com/Unleash/unleash-client-ruby/actions/workflows/pull_request.yml/badge.svg?branch=main)
4
- [![Coverage Status](https://coveralls.io/repos/github/Unleash/unleash-client-ruby/badge.svg?branch=main)](https://coveralls.io/github/Unleash/unleash-client-ruby?branch=main)
3
+ ![Build Status](https://github.com/Unleash/unleash-ruby-sdk/actions/workflows/pull_request.yml/badge.svg?branch=main)
4
+ [![Coverage Status](https://coveralls.io/repos/github/Unleash/unleash-ruby-sdk/badge.svg?branch=main)](https://coveralls.io/github/Unleash/unleash-ruby-sdk?branch=main)
5
5
  [![Gem Version](https://badge.fury.io/rb/unleash.svg)](https://badge.fury.io/rb/unleash)
6
6
 
7
- Ruby client for the [Unleash](https://github.com/Unleash/unleash) feature management service.
7
+ Unleash is a private, secure, and scalable [feature management platform](https://www.getunleash.io/) built to reduce the risk of releasing new features and accelerate software development. This Ruby SDK is designed to help you integrate with Unleash and evaluate feature flags inside your application.
8
+
9
+ You can use this client with [Unleash Enterprise](https://www.getunleash.io/pricing?utm_source=readme&utm_medium=ruby) or [Unleash Open Source](https://github.com/Unleash/unleash).
8
10
 
9
11
  > **Migrating to v6**
10
12
  >
@@ -563,7 +565,7 @@ To release a new version, follow these steps:
563
565
 
564
566
  ## Contributing
565
567
 
566
- Bug reports and pull requests are welcome on GitHub at https://github.com/unleash/unleash-client-ruby.
568
+ Bug reports and pull requests are welcome on GitHub at https://github.com/unleash/unleash-ruby-sdk.
567
569
 
568
570
  Be sure to run both `bundle exec rspec` and `bundle exec rubocop` in your branch before creating a pull request.
569
571
 
data/bin/unleash-client CHANGED
@@ -77,7 +77,7 @@ log_level = \
77
77
 
78
78
  @unleash = Unleash::Client.new(
79
79
  url: options[:url],
80
- app_name: 'unleash-client-ruby-cli',
80
+ app_name: 'unleash-ruby-sdk-cli',
81
81
  disable_metrics: options[:metrics],
82
82
  custom_http_headers: options[:custom_http_headers],
83
83
  log_level: log_level
data/examples/simple.rb CHANGED
@@ -18,8 +18,8 @@ puts ">> START simple.rb"
18
18
  # or:
19
19
 
20
20
  @unleash = Unleash::Client.new(
21
- url: 'https://unleash.herokuapp.com/api',
22
- custom_http_headers: { 'Authorization': '943ca9171e2c884c545c5d82417a655fb77cec970cc3b78a8ff87f4406b495d0' },
21
+ url: 'https://app.unleash-hosted.com/demo/api',
22
+ custom_http_headers: { 'Authorization': 'demo-app:dev.9fc74dd72d2b88bea5253c04240b21a54841f08d9918046ed55a06b5' },
23
23
  app_name: 'simple-test',
24
24
  instance_id: 'local-test-cli',
25
25
  refresh_interval: 2,
@@ -27,8 +27,7 @@ puts ">> START simple.rb"
27
27
  retry_limit: 2
28
28
  )
29
29
 
30
- # feature_name = "AwesomeFeature"
31
- feature_name = "4343443"
30
+ feature_name = "example-flag"
32
31
  unleash_context = Unleash::Context.new
33
32
  unleash_context.user_id = 123
34
33
 
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'unleash'
4
+ require 'unleash/context'
5
+
6
+ puts ">> START streaming.rb"
7
+
8
+ @unleash = Unleash::Client.new(
9
+ url: 'https://app.unleash-hosted.com/demo/api',
10
+ custom_http_headers: { 'Authorization': 'demo-app:dev.9fc74dd72d2b88bea5253c04240b21a54841f08d9918046ed55a06b5' },
11
+ app_name: 'streaming-test',
12
+ instance_id: 'local-streaming-cli',
13
+ refresh_interval: 2,
14
+ metrics_interval: 2,
15
+ retry_limit: 2,
16
+ experimental_mode: { type: 'streaming' },
17
+ timeout: 5,
18
+ log_level: Logger::DEBUG
19
+ )
20
+
21
+ feature_name = "example-flag"
22
+ unleash_context = Unleash::Context.new
23
+ unleash_context.user_id = 123
24
+
25
+ puts "Waiting for client to initialize..."
26
+ sleep 2
27
+
28
+ 100.times do
29
+ if @unleash.is_enabled?(feature_name, unleash_context)
30
+ puts "> #{feature_name} is enabled"
31
+ else
32
+ puts "> #{feature_name} is not enabled"
33
+ end
34
+ sleep 1
35
+ puts "---"
36
+ puts ""
37
+ puts ""
38
+ end
39
+ feature_name = "foobar"
40
+ if @unleash.is_enabled?(feature_name, unleash_context, true)
41
+ puts "> #{feature_name} is enabled"
42
+ else
43
+ puts "> #{feature_name} is not enabled"
44
+ end
45
+
46
+ puts "> shutting down client..."
47
+
48
+ @unleash.shutdown
49
+
50
+ puts ">> END streaming.rb"
@@ -2,8 +2,10 @@ require 'unleash/configuration'
2
2
  require 'unleash/toggle_fetcher'
3
3
  require 'unleash/metrics_reporter'
4
4
  require 'unleash/scheduled_executor'
5
+ require 'unleash/streaming_client_executor'
5
6
  require 'unleash/variant'
6
7
  require 'unleash/util/http'
8
+ require 'unleash/util/event_source_wrapper'
7
9
  require 'logger'
8
10
  require 'time'
9
11
 
@@ -21,7 +23,8 @@ module Unleash
21
23
  Unleash.engine = YggdrasilEngine.new
22
24
  Unleash.engine.register_custom_strategies(Unleash.configuration.strategies.custom_strategies)
23
25
 
24
- Unleash.toggle_fetcher = Unleash::ToggleFetcher.new Unleash.engine
26
+ Unleash.toggle_fetcher = Unleash::ToggleFetcher.new Unleash.engine unless Unleash.configuration.streaming_mode?
27
+
25
28
  if Unleash.configuration.disable_client
26
29
  Unleash.logger.warn "Unleash::Client is disabled! Will only return default (or bootstrapped if available) results!"
27
30
  Unleash.logger.warn "Unleash::Client is disabled! Metrics and MetricsReporter are also disabled!"
@@ -30,7 +33,9 @@ module Unleash
30
33
  end
31
34
 
32
35
  register
33
- start_toggle_fetcher
36
+
37
+ initialize_client_mode
38
+
34
39
  start_metrics unless Unleash.configuration.disable_metrics
35
40
  end
36
41
  # rubocop:enable Metrics/AbcSize
@@ -105,7 +110,7 @@ module Unleash
105
110
  # quick shutdown: just kill running threads
106
111
  def shutdown!
107
112
  unless Unleash.configuration.disable_client
108
- self.fetcher_scheduled_executor.exit
113
+ self.fetcher_scheduled_executor&.exit
109
114
  self.metrics_scheduled_executor.exit unless Unleash.configuration.disable_metrics
110
115
  end
111
116
  end
@@ -117,7 +122,7 @@ module Unleash
117
122
  'appName': Unleash.configuration.app_name,
118
123
  'instanceId': Unleash.configuration.instance_id,
119
124
  'connectionId': Unleash.configuration.connection_id,
120
- 'sdkVersion': "unleash-client-ruby:" + Unleash::VERSION,
125
+ 'sdkVersion': "unleash-ruby-sdk:" + Unleash::VERSION,
121
126
  'strategies': Unleash.strategies.known_strategies,
122
127
  'started': Time.now.iso8601(Unleash::TIME_RESOLUTION),
123
128
  'interval': Unleash.configuration.metrics_interval_in_millis,
@@ -140,6 +145,11 @@ module Unleash
140
145
  end
141
146
  end
142
147
 
148
+ def start_streaming_client
149
+ self.fetcher_scheduled_executor = Unleash::StreamingClientExecutor.new('StreamingExecutor', Unleash.engine)
150
+ self.fetcher_scheduled_executor.run
151
+ end
152
+
143
153
  def start_metrics
144
154
  Unleash.reporter = Unleash::MetricsReporter.new
145
155
  self.metrics_scheduled_executor = Unleash::ScheduledExecutor.new(
@@ -172,5 +182,13 @@ module Unleash
172
182
  def first_fetch_is_eager
173
183
  Unleash.configuration.use_bootstrap?
174
184
  end
185
+
186
+ def initialize_client_mode
187
+ if Unleash.configuration.streaming_mode?
188
+ start_streaming_client
189
+ else
190
+ start_toggle_fetcher
191
+ end
192
+ end
175
193
  end
176
194
  end
@@ -22,7 +22,8 @@ module Unleash
22
22
  :log_level,
23
23
  :bootstrap_config,
24
24
  :strategies,
25
- :use_delta_api
25
+ :use_delta_api,
26
+ :experimental_mode
26
27
  attr_reader :connection_id
27
28
 
28
29
  def initialize(opts = {})
@@ -57,7 +58,7 @@ module Unleash
57
58
  'UNLEASH-INSTANCEID' => self.instance_id,
58
59
  'UNLEASH-APPNAME' => self.app_name,
59
60
  'Unleash-Client-Spec' => CLIENT_SPECIFICATION_VERSION,
60
- 'UNLEASH-SDK' => "unleash-client-ruby:#{Unleash::VERSION}"
61
+ 'UNLEASH-SDK' => "unleash-ruby-sdk:#{Unleash::VERSION}"
61
62
  }.merge!(generate_custom_http_headers)
62
63
  headers['UNLEASH-CONNECTION-ID'] = @connection_id
63
64
  headers
@@ -67,7 +68,9 @@ module Unleash
67
68
  uri = nil
68
69
  ## Personal feeling but Rubocop's suggestion here is too dense to be properly readable
69
70
  # rubocop:disable Style/ConditionalAssignment
70
- if self.use_delta_api
71
+ if streaming_mode?
72
+ uri = URI("#{self.url_stripped_of_slash}/client/streaming")
73
+ elsif self.use_delta_api || polling_with_delta?
71
74
  uri = URI("#{self.url_stripped_of_slash}/client/delta")
72
75
  else
73
76
  uri = URI("#{self.url_stripped_of_slash}/client/features")
@@ -93,6 +96,17 @@ module Unleash
93
96
  self.bootstrap_config&.valid?
94
97
  end
95
98
 
99
+ def streaming_mode?
100
+ validate_streaming_support! if streaming_configured?
101
+ streaming_configured?
102
+ end
103
+
104
+ def polling_with_delta?
105
+ self.experimental_mode.is_a?(Hash) &&
106
+ self.experimental_mode[:type] == 'polling' &&
107
+ self.experimental_mode[:format] == 'delta'
108
+ end
109
+
96
110
  private
97
111
 
98
112
  def set_defaults
@@ -112,6 +126,7 @@ module Unleash
112
126
  self.bootstrap_config = nil
113
127
  self.strategies = Unleash::Strategies.new
114
128
  self.use_delta_api = false
129
+ self.experimental_mode = nil
115
130
 
116
131
  self.custom_http_headers = {}
117
132
  @connection_id = SecureRandom.uuid
@@ -149,5 +164,15 @@ module Unleash
149
164
  rescue NoMethodError
150
165
  raise ArgumentError, "unknown configuration parameter '#{val}'"
151
166
  end
167
+
168
+ def streaming_configured?
169
+ self.experimental_mode.is_a?(Hash) && self.experimental_mode[:type] == 'streaming'
170
+ end
171
+
172
+ def validate_streaming_support!
173
+ return unless RUBY_ENGINE == 'jruby'
174
+
175
+ raise "Streaming mode is not supported on JRuby. Please use polling mode instead or switch to MRI/CRuby."
176
+ end
152
177
  end
153
178
  end
@@ -0,0 +1,85 @@
1
+ require 'unleash/streaming_event_processor'
2
+ require 'unleash/util/event_source_wrapper'
3
+
4
+ module Unleash
5
+ class StreamingClientExecutor
6
+ attr_accessor :name, :event_source, :event_processor, :running
7
+
8
+ def initialize(name, engine)
9
+ self.name = name || 'StreamingClientExecutor'
10
+ self.event_source = nil
11
+ self.event_processor = Unleash::StreamingEventProcessor.new(engine)
12
+ self.running = false
13
+ end
14
+
15
+ def run(&_block)
16
+ start
17
+ end
18
+
19
+ def start
20
+ return if self.running || Unleash.configuration.disable_client
21
+
22
+ Unleash.logger.debug "Streaming client #{self.name} starting connection to: #{Unleash.configuration.fetch_toggles_uri}"
23
+
24
+ self.event_source = create_event_source
25
+ setup_event_handlers
26
+
27
+ self.running = true
28
+ Unleash.logger.debug "Streaming client #{self.name} connection established"
29
+ end
30
+
31
+ def stop
32
+ return unless self.running
33
+
34
+ Unleash.logger.debug "Streaming client #{self.name} stopping connection"
35
+ self.running = false
36
+ self.event_source&.close
37
+ self.event_source = nil
38
+ Unleash.logger.debug "Streaming client #{self.name} connection closed"
39
+ end
40
+
41
+ alias exit stop
42
+
43
+ def running?
44
+ self.running
45
+ end
46
+
47
+ private
48
+
49
+ def create_event_source
50
+ sse_client = Unleash::Util::EventSourceWrapper.client
51
+ if sse_client.nil?
52
+ raise "Streaming mode is configured but EventSource client is not available. " \
53
+ "Please install the 'ld-eventsource' gem or switch to polling mode."
54
+ end
55
+
56
+ headers = (Unleash.configuration.http_headers || {}).dup
57
+
58
+ sse_client.new(
59
+ Unleash.configuration.fetch_toggles_uri.to_s,
60
+ headers: headers,
61
+ read_timeout: 60,
62
+ reconnect_time: 2,
63
+ connect_timeout: 10,
64
+ logger: Unleash.logger
65
+ )
66
+ end
67
+
68
+ def setup_event_handlers
69
+ self.event_source.on_event do |event|
70
+ handle_event(event)
71
+ end
72
+
73
+ self.event_source.on_error do |error|
74
+ Unleash.logger.warn "Streaming client #{self.name} error: #{error}"
75
+ end
76
+ end
77
+
78
+ def handle_event(event)
79
+ self.event_processor.process_event(event)
80
+ rescue StandardError => e
81
+ Unleash.logger.error "Streaming client #{self.name} threw exception #{e.class}: '#{e}'"
82
+ Unleash.logger.debug "stacktrace: #{e.backtrace}"
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,53 @@
1
+ require 'json'
2
+
3
+ module Unleash
4
+ class StreamingEventProcessor
5
+ attr_accessor :toggle_engine, :mutex
6
+
7
+ def initialize(toggle_engine)
8
+ self.toggle_engine = toggle_engine
9
+ self.mutex = Mutex.new
10
+ end
11
+
12
+ def process_event(event)
13
+ case event.type.to_s
14
+ when 'unleash-connected'
15
+ Unleash.logger.debug "Streaming client connected"
16
+ handle_connected_event(event)
17
+ when 'unleash-updated'
18
+ Unleash.logger.debug "Received streaming update"
19
+ handle_updated_event(event)
20
+ else
21
+ Unleash.logger.debug "Received unknown event type: #{event.type}"
22
+ end
23
+ rescue StandardError => e
24
+ Unleash.logger.error "Error handling streaming event threw exception #{e.class}: '#{e}'"
25
+ Unleash.logger.debug "stacktrace: #{e.backtrace}"
26
+ end
27
+
28
+ def handle_delta_event(event_data)
29
+ self.mutex.synchronize do
30
+ self.toggle_engine.take_state(event_data)
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def handle_connected_event(event)
37
+ Unleash.logger.debug "Processing initial hydration data"
38
+ handle_updated_event(event)
39
+ end
40
+
41
+ def handle_updated_event(event)
42
+ handle_delta_event(event.data)
43
+
44
+ # TODO: update backup file
45
+ rescue JSON::ParserError => e
46
+ Unleash.logger.error "Unable to parse JSON from streaming event data. Exception thrown #{e.class}: '#{e}'"
47
+ Unleash.logger.debug "stacktrace: #{e.backtrace}"
48
+ rescue StandardError => e
49
+ Unleash.logger.error "Error processing delta update threw exception #{e.class}: '#{e}'"
50
+ Unleash.logger.debug "stacktrace: #{e.backtrace}"
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,17 @@
1
+ module Unleash
2
+ module Util
3
+ module EventSourceWrapper
4
+ def self.client
5
+ return nil if RUBY_ENGINE == 'jruby'
6
+
7
+ begin
8
+ require 'ld-eventsource'
9
+ SSE::Client
10
+ rescue LoadError => e
11
+ Unleash.logger.error "Failed to load ld-eventsource: #{e.message}"
12
+ nil
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,3 +1,3 @@
1
1
  module Unleash
2
- VERSION = "6.3.1".freeze
2
+ VERSION = "6.4.0".freeze
3
3
  end
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
13
13
  spec.description = "This is the ruby client for Unleash, a powerful feature toggle system
14
14
  that gives you a great overview over all feature toggles across all your applications and services."
15
15
 
16
- spec.homepage = "https://github.com/unleash/unleash-client-ruby"
16
+ spec.homepage = "https://github.com/unleash/unleash-ruby-sdk"
17
17
 
18
18
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
19
19
  f.match(%r{^(test|spec|features)/})
@@ -23,9 +23,10 @@ Gem::Specification.new do |spec|
23
23
  spec.require_paths = ["lib"]
24
24
  spec.required_ruby_version = ">= 2.7"
25
25
 
26
+ spec.add_dependency "ld-eventsource", "2.2.4" unless RUBY_ENGINE == 'jruby'
26
27
  spec.add_dependency "yggdrasil-engine", "~> 1.0.4"
27
28
 
28
- spec.add_dependency "base64", "~> 0.2.0"
29
+ spec.add_dependency "base64", "~> 0.3.0"
29
30
  spec.add_dependency "logger", "~> 1.6"
30
31
 
31
32
  spec.add_development_dependency "bundler", "~> 2.1"
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unleash
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.3.1
4
+ version: 6.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Renato Arruda
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-05-27 00:00:00.000000000 Z
11
+ date: 2025-08-12 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ld-eventsource
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 2.2.4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 2.2.4
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: yggdrasil-engine
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -30,14 +44,14 @@ dependencies:
30
44
  requirements:
31
45
  - - "~>"
32
46
  - !ruby/object:Gem::Version
33
- version: 0.2.0
47
+ version: 0.3.0
34
48
  type: :runtime
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
52
  - - "~>"
39
53
  - !ruby/object:Gem::Version
40
- version: 0.2.0
54
+ version: 0.3.0
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: logger
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -193,6 +207,7 @@ files:
193
207
  - examples/default-toggles.json
194
208
  - examples/extending_unleash_with_opentelemetry.rb
195
209
  - examples/simple.rb
210
+ - examples/streaming.rb
196
211
  - lib/unleash.rb
197
212
  - lib/unleash/bootstrap/configuration.rb
198
213
  - lib/unleash/bootstrap/handler.rb
@@ -206,17 +221,20 @@ files:
206
221
  - lib/unleash/scheduled_executor.rb
207
222
  - lib/unleash/spec_version.rb
208
223
  - lib/unleash/strategies.rb
224
+ - lib/unleash/streaming_client_executor.rb
225
+ - lib/unleash/streaming_event_processor.rb
209
226
  - lib/unleash/toggle_fetcher.rb
227
+ - lib/unleash/util/event_source_wrapper.rb
210
228
  - lib/unleash/util/http.rb
211
229
  - lib/unleash/variant.rb
212
230
  - lib/unleash/version.rb
213
231
  - unleash-client.gemspec
214
232
  - v6_MIGRATION_GUIDE.md
215
- homepage: https://github.com/unleash/unleash-client-ruby
233
+ homepage: https://github.com/unleash/unleash-ruby-sdk
216
234
  licenses:
217
235
  - Apache-2.0
218
236
  metadata: {}
219
- post_install_message:
237
+ post_install_message:
220
238
  rdoc_options: []
221
239
  require_paths:
222
240
  - lib
@@ -231,8 +249,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
231
249
  - !ruby/object:Gem::Version
232
250
  version: '0'
233
251
  requirements: []
234
- rubygems_version: 3.5.6
235
- signing_key:
252
+ rubygems_version: 3.5.22
253
+ signing_key:
236
254
  specification_version: 4
237
255
  summary: Unleash feature toggle client.
238
256
  test_files: []