togul 2.4.0 → 3.0.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.
- checksums.yaml +4 -4
- data/lib/togul/client.rb +13 -70
- data/lib/togul/config.rb +1 -4
- data/lib/togul/evaluate_result.rb +3 -30
- data/lib/togul/stream_client.rb +2 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8b2551b63847f7928914d173351b85f54aa78f64a67552701273b9ef334ea0e2
|
|
4
|
+
data.tar.gz: cb63352498c0f05520963a92fb0f8ea17759b91823a7538d9051134ffd91120b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7272d609f48bd5100c6e9f6bf78b00d0df4bd0a517f1e96a2c097335d6319b66d81f0f7c8485ea3a9307c7868c5ce2c74cc59bee5e8971f4884fc4247a36e843
|
|
7
|
+
data.tar.gz: 359137fd534f9dac13572955a506852ab9baa1d8c6e5a07289312075bb4d12656f3fd1e5c65dbb6df439342e18d41647e040e95a9e43ec5c20769b2010f787c3
|
data/lib/togul/client.rb
CHANGED
|
@@ -13,84 +13,22 @@ module Togul
|
|
|
13
13
|
@stream_client = nil
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
# Evaluate a feature flag.
|
|
17
|
-
#
|
|
18
|
-
# @param key [String] Flag key
|
|
19
|
-
# @param context [Hash<String, String>] User/request context
|
|
20
|
-
# @return [Boolean] Whether the flag is enabled
|
|
21
|
-
def enabled?(key, context = {})
|
|
22
|
-
evaluate_result(key, context).enabled?
|
|
23
|
-
rescue StandardError
|
|
24
|
-
case @config.fallback_mode
|
|
25
|
-
when :fail_open then true
|
|
26
|
-
else false
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Evaluate a feature flag and return the full result with typed value accessors.
|
|
16
|
+
# Evaluate a feature flag and return the result mirroring the API response.
|
|
31
17
|
#
|
|
32
18
|
# @param key [String] Flag key
|
|
33
19
|
# @param context [Hash<String, String>] User/request context
|
|
34
20
|
# @return [Togul::EvaluateResult]
|
|
35
|
-
def
|
|
21
|
+
def evaluate(key, context = {})
|
|
36
22
|
cache_key = build_cache_key(key, context)
|
|
37
23
|
|
|
38
24
|
cached = @cache.get(cache_key)
|
|
39
25
|
return cached unless cached.nil?
|
|
40
26
|
|
|
41
|
-
result =
|
|
27
|
+
result = fetch_evaluation(key, context)
|
|
42
28
|
@cache.set(cache_key, result)
|
|
43
29
|
result
|
|
44
30
|
end
|
|
45
31
|
|
|
46
|
-
# Evaluate a boolean flag.
|
|
47
|
-
#
|
|
48
|
-
# @param key [String] Flag key
|
|
49
|
-
# @param context [Hash<String, String>] User/request context
|
|
50
|
-
# @param fallback [Boolean] Value to return on error or type mismatch
|
|
51
|
-
# @return [Boolean]
|
|
52
|
-
def evaluate_bool(key, context = {}, fallback: false)
|
|
53
|
-
evaluate_result(key, context).bool_value(fallback)
|
|
54
|
-
rescue StandardError
|
|
55
|
-
fallback
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
# Evaluate a string flag.
|
|
59
|
-
#
|
|
60
|
-
# @param key [String] Flag key
|
|
61
|
-
# @param context [Hash<String, String>] User/request context
|
|
62
|
-
# @param fallback [String] Value to return on error or type mismatch
|
|
63
|
-
# @return [String]
|
|
64
|
-
def evaluate_string(key, context = {}, fallback: '')
|
|
65
|
-
evaluate_result(key, context).string_value(fallback)
|
|
66
|
-
rescue StandardError
|
|
67
|
-
fallback
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# Evaluate a number flag.
|
|
71
|
-
#
|
|
72
|
-
# @param key [String] Flag key
|
|
73
|
-
# @param context [Hash<String, String>] User/request context
|
|
74
|
-
# @param fallback [Float] Value to return on error or type mismatch
|
|
75
|
-
# @return [Float]
|
|
76
|
-
def evaluate_number(key, context = {}, fallback: 0.0)
|
|
77
|
-
evaluate_result(key, context).number_value(fallback)
|
|
78
|
-
rescue StandardError
|
|
79
|
-
fallback
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# Evaluate a JSON flag.
|
|
83
|
-
#
|
|
84
|
-
# @param key [String] Flag key
|
|
85
|
-
# @param context [Hash<String, String>] User/request context
|
|
86
|
-
# @param fallback [Object] Value to return on error or type mismatch
|
|
87
|
-
# @return [Object]
|
|
88
|
-
def evaluate_json(key, context = {}, fallback: nil)
|
|
89
|
-
evaluate_result(key, context).json_value(fallback)
|
|
90
|
-
rescue StandardError
|
|
91
|
-
fallback
|
|
92
|
-
end
|
|
93
|
-
|
|
94
32
|
# Clear all cached flag values.
|
|
95
33
|
def invalidate_cache
|
|
96
34
|
@cache.flush
|
|
@@ -101,19 +39,24 @@ module Togul
|
|
|
101
39
|
@cache.invalidate_flag(key)
|
|
102
40
|
end
|
|
103
41
|
|
|
104
|
-
# Start SSE stream for real-time cache invalidation.
|
|
105
|
-
|
|
42
|
+
# Start the SSE stream in a background thread for real-time cache invalidation.
|
|
43
|
+
# Subsequent calls are no-ops; the thread runs until the process exits.
|
|
44
|
+
def start_stream
|
|
106
45
|
@stream_client ||= StreamClient.new(@config, @cache)
|
|
46
|
+
@stream_thread ||= Thread.new { @stream_client.connect }
|
|
47
|
+
nil
|
|
107
48
|
end
|
|
108
49
|
|
|
109
50
|
# Register a listener for cache invalidation events.
|
|
51
|
+
# Call start_stream separately to begin receiving events.
|
|
110
52
|
def on_cache_invalidated(&block)
|
|
111
|
-
|
|
53
|
+
@stream_client ||= StreamClient.new(@config, @cache)
|
|
54
|
+
@stream_client.on_cache_invalidated(&block)
|
|
112
55
|
end
|
|
113
56
|
|
|
114
57
|
private
|
|
115
58
|
|
|
116
|
-
def
|
|
59
|
+
def fetch_evaluation(key, context)
|
|
117
60
|
raise Error.new('API key is required') if @config.api_key.empty?
|
|
118
61
|
|
|
119
62
|
last_error = nil
|
|
@@ -152,7 +95,7 @@ module Togul
|
|
|
152
95
|
flag_key: body['flag_key'] || key,
|
|
153
96
|
enabled: body['enabled'] == true,
|
|
154
97
|
value_type: body['value_type'].to_s,
|
|
155
|
-
|
|
98
|
+
value: body['value'],
|
|
156
99
|
reason: body['reason'].to_s
|
|
157
100
|
)
|
|
158
101
|
rescue Error
|
data/lib/togul/config.rb
CHANGED
|
@@ -4,13 +4,12 @@ module Togul
|
|
|
4
4
|
class Config
|
|
5
5
|
DEFAULT_BASE_URL = 'https://api.togul.io'
|
|
6
6
|
|
|
7
|
-
attr_reader :api_key, :environment, :timeout, :cache_ttl, :
|
|
7
|
+
attr_reader :api_key, :environment, :timeout, :cache_ttl, :retry_count, :base_url
|
|
8
8
|
|
|
9
9
|
# @param environment [String] Environment key (e.g. "production")
|
|
10
10
|
# @param api_key [String] Environment API key for evaluate/stream requests
|
|
11
11
|
# @param timeout [Numeric] HTTP timeout in seconds
|
|
12
12
|
# @param cache_ttl [Integer] Cache TTL in seconds
|
|
13
|
-
# @param fallback_mode [Symbol] :fail_closed or :fail_open
|
|
14
13
|
# @param retry_count [Integer] Number of retry attempts
|
|
15
14
|
# @param base_url [String, nil] Override default base URL (optional)
|
|
16
15
|
def initialize(
|
|
@@ -18,7 +17,6 @@ module Togul
|
|
|
18
17
|
api_key: '',
|
|
19
18
|
timeout: 5,
|
|
20
19
|
cache_ttl: 30,
|
|
21
|
-
fallback_mode: :fail_closed,
|
|
22
20
|
retry_count: 2,
|
|
23
21
|
base_url: nil
|
|
24
22
|
)
|
|
@@ -27,7 +25,6 @@ module Togul
|
|
|
27
25
|
@environment = environment
|
|
28
26
|
@timeout = timeout
|
|
29
27
|
@cache_ttl = cache_ttl
|
|
30
|
-
@fallback_mode = fallback_mode
|
|
31
28
|
@retry_count = retry_count
|
|
32
29
|
end
|
|
33
30
|
end
|
|
@@ -2,45 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
module Togul
|
|
4
4
|
class EvaluateResult
|
|
5
|
-
attr_reader :flag_key, :enabled, :value_type, :reason
|
|
5
|
+
attr_reader :flag_key, :enabled, :value_type, :value, :reason
|
|
6
6
|
|
|
7
|
-
def initialize(flag_key:, enabled:, value_type:,
|
|
7
|
+
def initialize(flag_key:, enabled:, value_type:, value:, reason:)
|
|
8
8
|
@flag_key = flag_key
|
|
9
9
|
@enabled = enabled
|
|
10
10
|
@value_type = value_type
|
|
11
|
-
@
|
|
11
|
+
@value = value
|
|
12
12
|
@reason = reason
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def enabled?
|
|
16
16
|
@enabled == true
|
|
17
17
|
end
|
|
18
|
-
|
|
19
|
-
def bool_value(fallback = false)
|
|
20
|
-
return fallback unless enabled? && @value_type == 'boolean'
|
|
21
|
-
return fallback unless @raw_value == true || @raw_value == false
|
|
22
|
-
|
|
23
|
-
@raw_value
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def string_value(fallback = '')
|
|
27
|
-
return fallback unless enabled? && @value_type == 'string'
|
|
28
|
-
return fallback unless @raw_value.is_a?(String)
|
|
29
|
-
|
|
30
|
-
@raw_value
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def number_value(fallback = 0.0)
|
|
34
|
-
return fallback unless enabled? && @value_type == 'number'
|
|
35
|
-
return fallback unless @raw_value.is_a?(Numeric)
|
|
36
|
-
|
|
37
|
-
@raw_value.to_f
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def json_value(fallback = nil)
|
|
41
|
-
return fallback unless enabled? && @value_type == 'json'
|
|
42
|
-
|
|
43
|
-
@raw_value.nil? ? fallback : @raw_value
|
|
44
|
-
end
|
|
45
18
|
end
|
|
46
19
|
end
|
data/lib/togul/stream_client.rb
CHANGED
|
@@ -37,6 +37,8 @@ module Togul
|
|
|
37
37
|
uri = URI("#{@config.base_url}/api/v1/stream")
|
|
38
38
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
39
39
|
http.use_ssl = uri.scheme == 'https'
|
|
40
|
+
http.open_timeout = @config.timeout
|
|
41
|
+
http.read_timeout = nil # SSE is long-lived; no read deadline
|
|
40
42
|
|
|
41
43
|
request = Net::HTTP::Get.new(uri.path)
|
|
42
44
|
request['Accept'] = 'text/event-stream'
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: togul
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 3.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Togul
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-05-
|
|
11
|
+
date: 2026-05-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: json
|