quonfig 1.7.0 → 1.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ea0ee1cb9b012622117d24f82d00e9ce5fdf61d160562846c6c9b6fa03b4a524
4
- data.tar.gz: 2aa051314de0c538eb77218e015368dab3c4fd5c0405ea5469b75c84817314c3
3
+ metadata.gz: 1b22d7d46b27ff2ab1452b41eaa68940d886216aaeb16c61a3faddf0778d352c
4
+ data.tar.gz: 0ca1f8e621bcfcd839a28d1d754560b5cf023eff205b24b83049196e64a8e4b0
5
5
  SHA512:
6
- metadata.gz: 0a0d54912f70d45cec55a53c758636d8bb7448eac31c1aee6a19463e6575247e8bc0374ed9ff3b279630b00031556dacb6657ffef7a9f16ac87ec5eb2a2f65ef
7
- data.tar.gz: 8838087ac35f83bc5c8761cba0c58a1a8a3fc1a0b3472c6de0b81c20a71d950f4ec1759098ce72e129fd6b4dadd8dd5b6190416d58014cf9dffb99b2bfc9cdd0
6
+ metadata.gz: 720d2acbfe8d30a785c1197fa319d608b1b4e11a1b60657b23d7b3dd921ed0c4882c99e7446b469a009552f09ca54538482509d9dc6a6968dc11df6f0e42d7c2
7
+ data.tar.gz: 11a8f0fbb7851dba082a46a7b657e9ec49aa61ae4af155cf9bf2272a35a3e5f75fa8148acdf0f3df8b936798cae5d8619dbc86bb981199f5bf29ceadf5f79567
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.7.1 - 2026-10-03
4
+
5
+ - **Behaviour change (bug fix): weighted rollouts now hash non-ASCII text the same way as every other Quonfig SDK (qfg-1mvb).** The MurmurHash3 used to pick a weighted variant counted characters instead of UTF-8 bytes, so a config key or hash value containing non-ASCII text (for example `josé`, `日本語`, `🚀`) hashed to a different number than sdk-java, sdk-python, sdk-go and sdk-node. It now hashes the UTF-8 bytes. Ruby users whose hash value (or the flag's key) contains non-ASCII characters may move to a different variant on upgrade; afterwards they get the same variant as in the other SDKs. ASCII-only values and keys are unchanged.
6
+ - **Fix: `Quonfig::Reason.compute` now returns `:SPLIT` for weighted bucket 0 (qfg-mn8f).** It returned `:SPLIT` only for buckets 1 and up, so bucket 0 was reported as `:DEFAULT`/`:RULE_MATCH`. The SDK's own evaluation path does not use this method and already reported bucket 0 as `SPLIT`; this only affects code that calls `Quonfig::Reason.compute` directly.
7
+
3
8
  ## 1.7.0 - 2026-10-02
4
9
 
5
10
  - **New: public `Client#get_or_raise` and `Client#get_duration_details` (also on `BoundClient`) (qfg-2agi.27).** `get_or_raise(key, default:, context:)` returns the value like `get`; a missing key with no `default:` raises `MissingDefaultError` even under `on_no_default: :return_nil`, and an unset ENV_VAR (`MissingEnvVarError`), an uncoercible ENV_VAR or malformed stored duration (`EnvVarParseError`) or a decryption failure (`DecryptionError`) raises. `get_duration_details` returns integer milliseconds in `EvaluationDetails`; a non-duration config is `ERROR`/`TYPE_MISMATCH` and a malformed value is `ERROR` with a nil value. The shared integration suite's init-timeout `get_or_raise` case now calls the public method instead of falling back to `get`.
@@ -25,10 +25,14 @@ class Murmur3
25
25
  (k1 * 0x1b873593) & MASK32
26
26
  end
27
27
 
28
+ # Hashes the string's BYTES (UTF-8 for any normal Ruby string), like every
29
+ # other Quonfig SDK. Using the character count instead of the byte count put
30
+ # non-ASCII input in the wrong tail/length mix (qfg-1mvb).
28
31
  def self.murmur3_32(str, seed = 0)
32
+ bytes = str.b
29
33
  h1 = seed
30
- numbers = str.unpack('V*C*')
31
- tailn = str.length % 4
34
+ numbers = bytes.unpack('V*C*')
35
+ tailn = bytes.bytesize % 4
32
36
  tail = numbers.slice!(numbers.size - tailn, tailn)
33
37
  numbers.each do |k1|
34
38
  h1 ^= murmur3_32__mmix(k1)
@@ -44,7 +48,7 @@ class Murmur3
44
48
  h1 ^= murmur3_32__mmix(k1)
45
49
  end
46
50
 
47
- h1 ^= str.length
51
+ h1 ^= bytes.bytesize
48
52
  murmur3_32_fmix(h1)
49
53
  end
50
54
  end
@@ -7,7 +7,7 @@ module Quonfig
7
7
  # :DEFAULT — config has no targeting rules; matched value is the static default
8
8
  # :RULE_MATCH — at least one targeting rule exists on the config (the matched
9
9
  # conditional may itself be ALWAYS_TRUE, but the *config* is targeted)
10
- # :SPLIT — matched value came from a non-default weighted variant
10
+ # :SPLIT — matched value came from a weighted variant (any bucket, including 0)
11
11
  # :ERROR — evaluation failed
12
12
  # :UNKNOWN — unable to determine
13
13
  module Reason
@@ -20,7 +20,7 @@ module Quonfig
20
20
  module_function
21
21
 
22
22
  def compute(config:, conditional_value:, weighted_value_index: nil)
23
- return SPLIT if weighted_value_index&.positive?
23
+ return SPLIT unless weighted_value_index.nil?
24
24
  return RULE_MATCH if targeting_rules?(config)
25
25
  return RULE_MATCH if non_always_true_criteria?(conditional_value)
26
26
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Quonfig
4
- VERSION = '1.7.0'
4
+ VERSION = '1.7.1'
5
5
  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: 1.7.0
4
+ version: 1.7.1
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-10-02 00:00:00.000000000 Z
11
+ date: 2026-10-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport