toggly 1.2.0 → 1.2.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/CHANGELOG.md +8 -0
- data/lib/toggly/client.rb +31 -16
- data/lib/toggly/definitions_provider.rb +5 -3
- data/lib/toggly/variant_allocator.rb +3 -4
- data/lib/toggly/version.rb +1 -1
- data/lib/toggly.rb +5 -3
- 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: a484fa27dd7cc53210f2af73984c93d9109205f797172203aea42c44c4722569
|
|
4
|
+
data.tar.gz: 79a64ed42c9497e8ce8ca0f59d4dc3c21e52e68ba5e24b3b76680767d2a4787b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 07f924fb8addc26c7af35f31f0a023680201f097cfa6b26826c333a2dc3a2086c07ae094ca23413daf176546a886dbd39598914a79c0ae6cd35c8c0aeb5df564
|
|
7
|
+
data.tar.gz: 353559ba9d83853a589d843f01b52cbdb9287074b7be77505784515df99b2505317d54225456b7818c210d050b5cd170abe1a8bc315545dad5f534f9a7537244
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.2.1] - 2026-09-28
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Always apply HTTP 200 definition bodies when the storage revision matches.
|
|
13
|
+
Same-revision responses still count as cache hits for telemetry after the
|
|
14
|
+
body is applied [OPS-1568].
|
|
15
|
+
|
|
8
16
|
## [1.2.0] - 2026-09-24
|
|
9
17
|
|
|
10
18
|
### Added
|
data/lib/toggly/client.rb
CHANGED
|
@@ -24,6 +24,8 @@ module Toggly
|
|
|
24
24
|
# )
|
|
25
25
|
# client = Toggly::Client.new(config)
|
|
26
26
|
class Client
|
|
27
|
+
UNHANDLED_VARIANT_VALUE = Object.new.freeze
|
|
28
|
+
|
|
27
29
|
include CacheTelemetry
|
|
28
30
|
include SnapshotSupport
|
|
29
31
|
|
|
@@ -359,30 +361,43 @@ module Toggly
|
|
|
359
361
|
return block.call(value) if block
|
|
360
362
|
return nil if as.nil?
|
|
361
363
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
return value.is_a?(String) ? value : nil
|
|
365
|
-
end
|
|
366
|
-
if as == Integer
|
|
367
|
-
return value.is_a?(Integer) && !value.is_a?(TrueClass) && !value.is_a?(FalseClass) ? value : nil
|
|
368
|
-
end
|
|
369
|
-
if as == Float
|
|
370
|
-
return value.is_a?(Numeric) && !value.is_a?(TrueClass) && !value.is_a?(FalseClass) ? value.to_f : nil
|
|
371
|
-
end
|
|
372
|
-
if [TrueClass, FalseClass, :boolean].include?(as)
|
|
373
|
-
return [true, false].include?(value) ? value : nil
|
|
374
|
-
end
|
|
364
|
+
scalar_value = decode_scalar_variant_value(value, as)
|
|
365
|
+
return scalar_value unless scalar_value.equal?(UNHANDLED_VARIANT_VALUE)
|
|
375
366
|
|
|
376
367
|
# is_a? requires a Module; symbols like :boolean are handled above.
|
|
377
368
|
return value if as.is_a?(Module) && value.is_a?(as)
|
|
378
|
-
return as.new(**value.transform_keys(&:to_sym)) if as.respond_to?(:new) && value.is_a?(Hash)
|
|
379
|
-
return as.json_create(value) if as.respond_to?(:json_create) && value.is_a?(Hash)
|
|
380
369
|
|
|
381
|
-
|
|
370
|
+
decode_variant_object(value, as)
|
|
382
371
|
rescue StandardError
|
|
383
372
|
nil
|
|
384
373
|
end
|
|
385
374
|
|
|
375
|
+
# Scalars / :boolean before Class#new — String.new({}) would soft-fail to "".
|
|
376
|
+
def decode_scalar_variant_value(value, as)
|
|
377
|
+
return value.is_a?(String) ? value : nil if as == String
|
|
378
|
+
return numeric_variant_value(value, Integer) if as == Integer
|
|
379
|
+
return numeric_variant_value(value, Float) if as == Float
|
|
380
|
+
return [true, false].include?(value) ? value : nil if [TrueClass, FalseClass, :boolean].include?(as)
|
|
381
|
+
|
|
382
|
+
UNHANDLED_VARIANT_VALUE
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
def numeric_variant_value(value, type)
|
|
386
|
+
return nil if value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
|
387
|
+
return value if type == Integer && value.is_a?(Integer)
|
|
388
|
+
return value.to_f if type == Float && value.is_a?(Numeric)
|
|
389
|
+
|
|
390
|
+
nil
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
def decode_variant_object(value, as)
|
|
394
|
+
return nil unless value.is_a?(Hash)
|
|
395
|
+
return as.new(**value.transform_keys(&:to_sym)) if as.respond_to?(:new)
|
|
396
|
+
return as.json_create(value) if as.respond_to?(:json_create)
|
|
397
|
+
|
|
398
|
+
nil
|
|
399
|
+
end
|
|
400
|
+
|
|
386
401
|
# `definitions` / `definitions-signed` → local rule eval. The sole
|
|
387
402
|
# source of truth for `enabled?`; also carries `variants` / `allocation`
|
|
388
403
|
# for the catalog-local `get_variant` assignment.
|
|
@@ -189,9 +189,11 @@ module Toggly
|
|
|
189
189
|
log_debug("Definitions not modified")
|
|
190
190
|
FetchResult.new(definitions: nil, cache_outcome: :hit)
|
|
191
191
|
when :same_revision
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
192
|
+
# Same storage revision is a telemetry hit, but HTTP 200 still carries
|
|
193
|
+
# an authoritative body — apply it (parity with Go/Rust/Python).
|
|
194
|
+
log_debug("Definitions revision matches existing; applying HTTP 200 body")
|
|
195
|
+
result = handle_new_content(response, response_etag, response_lm)
|
|
196
|
+
FetchResult.new(definitions: result.definitions, cache_outcome: :hit)
|
|
195
197
|
when :new_content
|
|
196
198
|
handle_new_content(response, response_etag, response_lm)
|
|
197
199
|
when :error_status
|
|
@@ -112,13 +112,12 @@ module Toggly
|
|
|
112
112
|
return nil if list.empty?
|
|
113
113
|
|
|
114
114
|
pct = compute_percentile(identity, seed, feature_key, ignore_case)
|
|
115
|
-
list.
|
|
115
|
+
matching_entry = list.find do |entry|
|
|
116
116
|
from = entry[:from].to_f
|
|
117
117
|
to = entry[:to].to_f
|
|
118
|
-
|
|
119
|
-
return entry[:variant] if matched
|
|
118
|
+
to >= 100.0 ? pct >= from : (pct >= from && pct < to)
|
|
120
119
|
end
|
|
121
|
-
nil
|
|
120
|
+
matching_entry&.fetch(:variant, nil)
|
|
122
121
|
end
|
|
123
122
|
|
|
124
123
|
# MF-parity percentile hash.
|
data/lib/toggly/version.rb
CHANGED
data/lib/toggly.rb
CHANGED
|
@@ -71,6 +71,8 @@ require_relative "toggly/client"
|
|
|
71
71
|
# }
|
|
72
72
|
# )
|
|
73
73
|
module Toggly
|
|
74
|
+
NOT_CONFIGURED_ERROR = "Toggly not configured. Call Toggly.configure first."
|
|
75
|
+
|
|
74
76
|
class << self
|
|
75
77
|
# Global client instance
|
|
76
78
|
attr_accessor :client
|
|
@@ -92,7 +94,7 @@ module Toggly
|
|
|
92
94
|
# @param context [Context, nil] Optional evaluation context
|
|
93
95
|
# @return [Boolean]
|
|
94
96
|
def enabled?(feature_key, context: nil)
|
|
95
|
-
raise Error,
|
|
97
|
+
raise Error, NOT_CONFIGURED_ERROR unless @client
|
|
96
98
|
|
|
97
99
|
@client.enabled?(feature_key, context: context)
|
|
98
100
|
end
|
|
@@ -113,7 +115,7 @@ module Toggly
|
|
|
113
115
|
# @param context [Context, nil]
|
|
114
116
|
# @return [VariantResult, nil]
|
|
115
117
|
def get_variant(feature_key, context: nil)
|
|
116
|
-
raise Error,
|
|
118
|
+
raise Error, NOT_CONFIGURED_ERROR unless @client
|
|
117
119
|
|
|
118
120
|
@client.get_variant(feature_key, context: context)
|
|
119
121
|
end
|
|
@@ -124,7 +126,7 @@ module Toggly
|
|
|
124
126
|
# @param context [Context, nil]
|
|
125
127
|
# @return [Object, nil]
|
|
126
128
|
def get_variant_value(feature_key, context: nil, as: nil, &)
|
|
127
|
-
raise Error,
|
|
129
|
+
raise Error, NOT_CONFIGURED_ERROR unless @client
|
|
128
130
|
|
|
129
131
|
@client.get_variant_value(feature_key, context: context, as: as, &)
|
|
130
132
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: toggly
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.2.
|
|
4
|
+
version: 1.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ops.ai
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-10-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: High-performance Ruby SDK for Toggly feature flag management. Works with
|
|
14
14
|
or without Toggly.io.
|