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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +94 -0
- data/lib/quonfig/caching_http_connection.rb +3 -3
- data/lib/quonfig/client.rb +22 -27
- data/lib/quonfig/config_loader.rb +5 -1
- data/lib/quonfig/config_store.rb +10 -6
- data/lib/quonfig/context.rb +5 -4
- data/lib/quonfig/datadir.rb +4 -10
- data/lib/quonfig/dev_context.rb +4 -2
- data/lib/quonfig/duration.rb +2 -2
- data/lib/quonfig/encryption.rb +12 -16
- data/lib/quonfig/errors/invalid_environment_error.rb +1 -3
- data/lib/quonfig/errors/invalid_sdk_key_error.rb +6 -7
- data/lib/quonfig/errors/missing_env_var_error.rb +0 -3
- data/lib/quonfig/errors/missing_environment_error.rb +1 -1
- data/lib/quonfig/errors/uninitialized_error.rb +1 -1
- data/lib/quonfig/evaluation.rb +11 -8
- data/lib/quonfig/evaluator.rb +47 -37
- data/lib/quonfig/fixed_size_hash.rb +1 -0
- data/lib/quonfig/http_connection.rb +2 -4
- data/lib/quonfig/internal_logger.rb +63 -27
- data/lib/quonfig/murmer3.rb +2 -2
- data/lib/quonfig/options.rb +62 -75
- data/lib/quonfig/periodic_sync.rb +1 -1
- data/lib/quonfig/quonfig.rb +3 -3
- data/lib/quonfig/reason.rb +2 -1
- data/lib/quonfig/resolver.rb +8 -9
- data/lib/quonfig/semantic_logger_filter.rb +4 -3
- data/lib/quonfig/semver.rb +6 -8
- data/lib/quonfig/sse_config_client.rb +13 -14
- data/lib/quonfig/stdlib_formatter.rb +3 -3
- data/lib/quonfig/telemetry/context_shape_aggregator.rb +2 -3
- data/lib/quonfig/telemetry/example_contexts_aggregator.rb +1 -1
- data/lib/quonfig/telemetry/telemetry_reporter.rb +1 -0
- data/lib/quonfig/time_helpers.rb +2 -0
- data/lib/quonfig/version.rb +1 -1
- data/quonfig.gemspec +6 -7
- metadata +2 -16
data/lib/quonfig/resolver.rb
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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:
|
|
31
|
-
warn:
|
|
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,
|
|
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
|
data/lib/quonfig/semver.rb
CHANGED
|
@@ -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,
|
|
25
|
-
raise ArgumentError,
|
|
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
|
-
|
|
55
|
-
closed_count += @options.sleep_delay_for_new_connection_check
|
|
55
|
+
next unless @client.closed?
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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'
|
|
49
|
-
'WARN'
|
|
48
|
+
'INFO' => :info,
|
|
49
|
+
'WARN' => :warn,
|
|
50
50
|
'ERROR' => :error,
|
|
51
51
|
'FATAL' => :fatal,
|
|
52
|
-
'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.
|
|
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.
|
|
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.
|
|
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
|
data/lib/quonfig/time_helpers.rb
CHANGED
data/lib/quonfig/version.rb
CHANGED
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'
|
|
18
|
-
'changelog_uri'
|
|
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.
|
|
32
|
-
s.
|
|
33
|
-
s.
|
|
34
|
-
s.
|
|
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.
|
|
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-
|
|
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: []
|