quonfig 0.0.11 → 0.0.13

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/README.md +94 -0
  4. data/lib/quonfig/caching_http_connection.rb +3 -3
  5. data/lib/quonfig/client.rb +22 -27
  6. data/lib/quonfig/config_loader.rb +5 -1
  7. data/lib/quonfig/config_store.rb +10 -6
  8. data/lib/quonfig/context.rb +5 -4
  9. data/lib/quonfig/datadir.rb +4 -10
  10. data/lib/quonfig/dev_context.rb +4 -2
  11. data/lib/quonfig/duration.rb +2 -2
  12. data/lib/quonfig/encryption.rb +12 -16
  13. data/lib/quonfig/errors/invalid_environment_error.rb +1 -3
  14. data/lib/quonfig/errors/invalid_sdk_key_error.rb +6 -7
  15. data/lib/quonfig/errors/missing_env_var_error.rb +0 -3
  16. data/lib/quonfig/errors/missing_environment_error.rb +1 -1
  17. data/lib/quonfig/errors/uninitialized_error.rb +1 -1
  18. data/lib/quonfig/evaluation.rb +11 -8
  19. data/lib/quonfig/evaluator.rb +47 -37
  20. data/lib/quonfig/fixed_size_hash.rb +1 -0
  21. data/lib/quonfig/http_connection.rb +2 -4
  22. data/lib/quonfig/internal_logger.rb +63 -27
  23. data/lib/quonfig/murmer3.rb +2 -2
  24. data/lib/quonfig/options.rb +62 -75
  25. data/lib/quonfig/periodic_sync.rb +1 -1
  26. data/lib/quonfig/quonfig.rb +3 -3
  27. data/lib/quonfig/reason.rb +2 -1
  28. data/lib/quonfig/resolver.rb +8 -9
  29. data/lib/quonfig/semantic_logger_filter.rb +4 -3
  30. data/lib/quonfig/semver.rb +6 -8
  31. data/lib/quonfig/sse_config_client.rb +13 -14
  32. data/lib/quonfig/stdlib_formatter.rb +3 -3
  33. data/lib/quonfig/telemetry/context_shape_aggregator.rb +2 -3
  34. data/lib/quonfig/telemetry/example_contexts_aggregator.rb +1 -1
  35. data/lib/quonfig/telemetry/telemetry_reporter.rb +1 -0
  36. data/lib/quonfig/time_helpers.rb +2 -0
  37. data/lib/quonfig/version.rb +1 -1
  38. data/quonfig.gemspec +6 -7
  39. metadata +2 -16
@@ -44,7 +44,7 @@ module Quonfig
44
44
  # raise into the test's expected default).
45
45
  def get(key, context = nil)
46
46
  config = raw(key)
47
- raise Quonfig::Errors::MissingDefaultError.new(key) if config.nil?
47
+ raise Quonfig::Errors::MissingDefaultError, key if config.nil?
48
48
 
49
49
  eval_result = @evaluator.evaluate_config(config, context, resolver: self)
50
50
  return nil if eval_result.nil?
@@ -72,17 +72,16 @@ module Quonfig
72
72
 
73
73
  type = vget(value, :type, 'type')
74
74
 
75
- if type == 'provided'
76
- return resolve_provided(value, config)
77
- end
75
+ return resolve_provided(value, config) if type == 'provided'
78
76
 
79
- if type == 'weighted_values'
80
- return resolve_weighted(value, config, context, &on_weighted_index)
81
- end
77
+ return resolve_weighted(value, config, context, &on_weighted_index) if type == 'weighted_values'
82
78
 
83
79
  confidential = vget(value, :confidential, 'confidential')
84
80
  decrypt_with = vget(value, :decryptWith, 'decryptWith', :decrypt_with, 'decrypt_with')
85
- return resolve_decryption(value, config, context, decrypt_with) if confidential && decrypt_with && !decrypt_with.to_s.empty?
81
+ if confidential && decrypt_with && !decrypt_with.to_s.empty?
82
+ return resolve_decryption(value, config, context,
83
+ decrypt_with)
84
+ end
86
85
 
87
86
  value
88
87
  end
@@ -144,7 +143,7 @@ module Quonfig
144
143
  lookup = vget(provided, :lookup, 'lookup')
145
144
  return value if source != 'ENV_VAR' || lookup.nil? || lookup.to_s.empty?
146
145
 
147
- env_value = ENV[lookup.to_s]
146
+ env_value = ENV.fetch(lookup.to_s, nil)
148
147
  if env_value.nil?
149
148
  raise Quonfig::Errors::MissingEnvVarError,
150
149
  %(Environment variable "#{lookup}" not set for config "#{config_key(config)}")
@@ -27,8 +27,8 @@ module Quonfig
27
27
  LEVELS = {
28
28
  trace: 0,
29
29
  debug: 1,
30
- info: 2,
31
- warn: 3,
30
+ info: 2,
31
+ warn: 3,
32
32
  error: 4,
33
33
  fatal: 5
34
34
  }.freeze
@@ -42,7 +42,8 @@ module Quonfig
42
42
 
43
43
  def initialize(client, config_key:)
44
44
  unless self.class.semantic_logger_loaded?
45
- raise LoadError, "semantic_logger gem is required for Quonfig::SemanticLoggerFilter. Add `gem 'semantic_logger'` to your Gemfile."
45
+ raise LoadError,
46
+ "semantic_logger gem is required for Quonfig::SemanticLoggerFilter. Add `gem 'semantic_logger'` to your Gemfile."
46
47
  end
47
48
 
48
49
  @client = client
@@ -21,8 +21,8 @@ class SemanticVersion
21
21
  attr_reader :major, :minor, :patch, :prerelease, :build_metadata
22
22
 
23
23
  def self.parse(version_string)
24
- raise ArgumentError, "version string cannot be nil" if version_string.nil?
25
- raise ArgumentError, "version string cannot be empty" if version_string.empty?
24
+ raise ArgumentError, 'version string cannot be nil' if version_string.nil?
25
+ raise ArgumentError, 'version string cannot be empty' if version_string.empty?
26
26
 
27
27
  match = SEMVER_PATTERN.match(version_string)
28
28
  raise ArgumentError, "invalid semantic version format: #{version_string}" unless match
@@ -87,12 +87,12 @@ class SemanticVersion
87
87
  result
88
88
  end
89
89
 
90
- private
91
-
92
90
  def self.numeric?(str)
93
91
  str.to_i.to_s == str
94
92
  end
95
93
 
94
+ private
95
+
96
96
  def compare_prerelease(pre1, pre2)
97
97
  # If both are empty, they're equal
98
98
  return 0 if pre1.nil? && pre2.nil?
@@ -118,9 +118,7 @@ class SemanticVersion
118
118
 
119
119
  def compare_prerelease_identifiers(id1, id2)
120
120
  # If both are numeric, compare numerically
121
- if self.class.numeric?(id1) && self.class.numeric?(id2)
122
- return id1.to_i <=> id2.to_i
123
- end
121
+ return id1.to_i <=> id2.to_i if self.class.numeric?(id1) && self.class.numeric?(id2)
124
122
 
125
123
  # If only one is numeric, numeric ones have lower precedence
126
124
  return -1 if self.class.numeric?(id1)
@@ -129,4 +127,4 @@ class SemanticVersion
129
127
  # Neither is numeric, compare as strings
130
128
  id1 <=> id2
131
129
  end
132
- end
130
+ end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'base64'
3
4
  require 'json'
4
5
 
@@ -51,15 +52,15 @@ module Quonfig
51
52
  loop do
52
53
  sleep @options.sleep_delay_for_new_connection_check
53
54
 
54
- if @client.closed?
55
- closed_count += @options.sleep_delay_for_new_connection_check
55
+ next unless @client.closed?
56
56
 
57
- if closed_count > @options.seconds_between_new_connection
58
- closed_count = 0
59
- @logger.debug 'Reconnecting SSE client'
60
- @client = connect(&load_configs)
61
- end
62
- end
57
+ closed_count += @options.sleep_delay_for_new_connection_check
58
+
59
+ next unless closed_count > @options.seconds_between_new_connection
60
+
61
+ closed_count = 0
62
+ @logger.debug 'Reconnecting SSE client'
63
+ @client = connect(&load_configs)
63
64
  end
64
65
  end
65
66
  end
@@ -79,7 +80,7 @@ module Quonfig
79
80
  if event.data.nil? || event.data.empty?
80
81
  @logger.error "SSE Streaming Error: Received empty data for url #{url}"
81
82
  client.close
82
- return
83
+ next
83
84
  end
84
85
 
85
86
  begin
@@ -87,7 +88,7 @@ module Quonfig
87
88
  rescue JSON::ParserError => e
88
89
  @logger.error "SSE Streaming Error: Failed to parse JSON for url #{url}: #{e.message}"
89
90
  client.close
90
- return
91
+ next
91
92
  end
92
93
 
93
94
  envelope = Quonfig::ConfigEnvelope.new(
@@ -116,7 +117,7 @@ module Quonfig
116
117
  def headers
117
118
  auth = "1:#{@prefab_options.sdk_key}"
118
119
  auth_string = Base64.strict_encode64(auth)
119
- return {
120
+ {
120
121
  'Authorization' => "Basic #{auth_string}",
121
122
  'Accept' => 'text/event-stream',
122
123
  'X-Quonfig-SDK-Version' => "ruby-#{Quonfig::VERSION}"
@@ -126,9 +127,7 @@ module Quonfig
126
127
  def source
127
128
  @source_index = @source_index.nil? ? 0 : @source_index + 1
128
129
 
129
- if @source_index >= @prefab_options.sse_api_urls.size
130
- @source_index = 0
131
- end
130
+ @source_index = 0 if @source_index >= @prefab_options.sse_api_urls.size
132
131
 
133
132
  @prefab_options.sse_api_urls[@source_index]
134
133
  end
@@ -45,11 +45,11 @@ module Quonfig
45
45
  # every label the stdlib actually emits.
46
46
  SEVERITY_TO_LEVEL = {
47
47
  'DEBUG' => :debug,
48
- 'INFO' => :info,
49
- 'WARN' => :warn,
48
+ 'INFO' => :info,
49
+ 'WARN' => :warn,
50
50
  'ERROR' => :error,
51
51
  'FATAL' => :fatal,
52
- 'ANY' => :fatal # Logger::UNKNOWN formats as "ANY"
52
+ 'ANY' => :fatal # Logger::UNKNOWN formats as "ANY"
53
53
  }.freeze
54
54
 
55
55
  # Build a formatter Proc. Exposed on +Quonfig::Client#stdlib_formatter+;
@@ -42,10 +42,9 @@ module Quonfig
42
42
  duped = @data.dup
43
43
  @data.clear
44
44
 
45
- duped.inject({}) do |acc, (name, key, type)|
45
+ duped.each_with_object({}) do |(name, key, type), acc|
46
46
  acc[name] ||= {}
47
47
  acc[name][key] = type
48
- acc
49
48
  end
50
49
  end
51
50
 
@@ -53,7 +52,7 @@ module Quonfig
53
52
  # matching api-telemetry's ContextShapesSchema. Returns +nil+ when
54
53
  # there is nothing to ship — the reporter should skip empty events.
55
54
  def drain_event
56
- return nil if @data.size.zero?
55
+ return nil if @data.empty?
57
56
 
58
57
  shapes = prepare_data.map do |name, field_types|
59
58
  { 'name' => name, 'fieldTypes' => field_types }
@@ -45,7 +45,7 @@ module Quonfig
45
45
  # Drain accumulated examples into a single telemetry event payload
46
46
  # matching api-telemetry's ExampleContextsSchema, or +nil+ if empty.
47
47
  def drain_event
48
- return nil if @data.size.zero?
48
+ return nil if @data.empty?
49
49
 
50
50
  to_ship = prepare_data
51
51
 
@@ -157,6 +157,7 @@ module Quonfig
157
157
  # giving up. Bounded so a thread blocked on a dead telemetry endpoint
158
158
  # can't hang process exit.
159
159
  AT_EXIT_THREAD_JOIN_TIMEOUT_SECONDS = 1.0
160
+ private_constant :AT_EXIT_THREAD_JOIN_TIMEOUT_SECONDS
160
161
 
161
162
  # Idempotent final drain. Safe to call after #stop has already
162
163
  # drained: aggregators return nil when empty and #sync becomes a
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Quonfig
2
4
  module TimeHelpers
3
5
  def self.now_in_ms
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Quonfig
4
- VERSION = '0.0.11'
4
+ VERSION = '0.0.13'
5
5
  end
data/quonfig.gemspec CHANGED
@@ -14,8 +14,8 @@ Gem::Specification.new do |s|
14
14
  s.required_ruby_version = '>= 3.0'
15
15
 
16
16
  s.metadata = {
17
- 'source_code_uri' => 'https://github.com/quonfig/sdk-ruby',
18
- 'changelog_uri' => 'https://github.com/quonfig/sdk-ruby/blob/main/CHANGELOG.md',
17
+ 'source_code_uri' => 'https://github.com/quonfig/sdk-ruby',
18
+ 'changelog_uri' => 'https://github.com/quonfig/sdk-ruby/blob/main/CHANGELOG.md',
19
19
  'rubygems_mfa_required' => 'true'
20
20
  }
21
21
 
@@ -28,9 +28,8 @@ Gem::Specification.new do |s|
28
28
  ]
29
29
  s.extra_rdoc_files = %w[CHANGELOG.md LICENSE.txt README.md]
30
30
 
31
- s.add_runtime_dependency 'activesupport', '>= 4'
32
- s.add_runtime_dependency 'concurrent-ruby', '~> 1.0', '>= 1.0.5'
33
- s.add_runtime_dependency 'faraday', '>= 1.0'
34
- s.add_runtime_dependency 'ld-eventsource', '>= 2.0'
35
- s.add_runtime_dependency 'uuid', '>= 2.0'
31
+ s.add_dependency 'activesupport', '>= 4'
32
+ s.add_dependency 'concurrent-ruby', '~> 1.0', '>= 1.0.5'
33
+ s.add_dependency 'faraday', '>= 1.0'
34
+ s.add_dependency 'ld-eventsource', '>= 2.0'
36
35
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quonfig
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.11
4
+ version: 0.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Dwyer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-05-02 00:00:00.000000000 Z
11
+ date: 2026-05-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -72,20 +72,6 @@ dependencies:
72
72
  - - ">="
73
73
  - !ruby/object:Gem::Version
74
74
  version: '2.0'
75
- - !ruby/object:Gem::Dependency
76
- name: uuid
77
- requirement: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - ">="
80
- - !ruby/object:Gem::Version
81
- version: '2.0'
82
- type: :runtime
83
- prerelease: false
84
- version_requirements: !ruby/object:Gem::Requirement
85
- requirements:
86
- - - ">="
87
- - !ruby/object:Gem::Version
88
- version: '2.0'
89
75
  description: Quonfig — feature flags and live config, stored as files in git.
90
76
  email: jeff@quonfig.com
91
77
  executables: []