toggly 1.0.0 → 1.1.0

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: af67c2cd1ab3f935ea72b5f3959e0958f8e7225eb8d176a40fbdb9baf3b3f57d
4
- data.tar.gz: 8911b6582910f651d545a1fd625314f7a5aab4c8972188bc31844156ee37882e
3
+ metadata.gz: d728557cdb56574a22377e85047ec649a801532f9cea3574ae66ccba703e320e
4
+ data.tar.gz: 5054b15ad5ad1b48fde5c6fb174698c701bd8fa38b6054cc8afd6ff015e196ce
5
5
  SHA512:
6
- metadata.gz: 78ff528d676027bb0379688179b888fae05c567f0a1f57b78982890e07fc3c68cf68c763f0ddd63c8611e21c2aea4bcc0e25d31dde2f33e6ba11adbb52653cf4
7
- data.tar.gz: dbe5aad2ad173a579bd7385a24b00a18980be769ea89a46e875ec6ab31d5e5af4246a9ac74789530cc7bf673dd237addee389261e2c5fc515d3989fe520b5653
6
+ metadata.gz: 8871e715209c606026d37367ec46522c3bbcc7925633f9f511bad51179f141924cb4b7e123978212e1a1697050072d0bfacff0463e53cf02510012cb28531db1
7
+ data.tar.gz: c3536488d775b49aeefa375f72f3c6b88a993fb4b6464a19e8c5ae73bde75222c310bc820e563f94039509bd6dbbc698ddb04c33cd89de6beba2e7c1ac29dd56
data/CHANGELOG.md CHANGED
@@ -5,6 +5,20 @@ 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.1.0] - 2026-09-23
9
+
10
+ ### Added
11
+
12
+ - Ambient targeting identity for catalog-local variants: `Config#identity`,
13
+ `Client#identity` / `Client#set_identity`. When `get_variant` /
14
+ `get_variant_value` are called with a nil context or a blank
15
+ `context.identity`, the client identity is used as the allocator
16
+ `userId`. Groups still come only from the per-call context (never Config).
17
+ Prefer config / `set_identity` (or Rails ambient context) over building
18
+ `Context.new(identity: …)` at every call site.
19
+ `Config#identity=` is configuration-time only (`Toggly.configure` /
20
+ `Config.new`); after `Client` is built, use `Client#set_identity`.
21
+
8
22
  ## [1.0.0] - 2026-09-23
9
23
 
10
24
  ### Changed (Breaking)
data/README.md CHANGED
@@ -47,13 +47,19 @@ bit-for-bit — verified against the shared
47
47
  `variant-allocator-corpus/cases.json` gold corpus.
48
48
 
49
49
  ```ruby
50
- variant = client.get_variant('checkout-flow', context: Toggly::Context.new(identity: 'user-1'))
50
+ # Prefer ambient identity: Config#identity, Client#set_identity, or Rails
51
+ # feature_variant (uses toggly_context). Per-call context is an override.
52
+ client = Toggly::Client.new(app_key: '...', environment: 'Production', identity: 'user-1')
53
+ client.set_identity('user-2') # optional runtime update
54
+
55
+ variant = client.get_variant('checkout-flow') # uses client identity
56
+ variant = client.get_variant('checkout-flow', context: Toggly::Context.new(identity: 'override'))
51
57
  variant&.name # assigned variant name, or nil
52
58
  variant&.configuration_value # untyped configuration payload
53
59
  variant&.enabled # effective enabled after StatusOverride
54
60
  variant&.reason # "User" | "Group" | "Percentile" | "DefaultWhenEnabled" | "DefaultWhenDisabled"
55
61
 
56
- client.get_variant_value('checkout-flow', context: context) # shortcut for configuration_value
62
+ client.get_variant_value('checkout-flow') # shortcut for configuration_value
57
63
  ```
58
64
 
59
65
  `enabled?` stays filter-based only. The `enable_variants` /
data/lib/toggly/client.rb CHANGED
@@ -47,6 +47,7 @@ module Toggly
47
47
  # True once a revision (including empty) or durable snapshot was applied.
48
48
  @definitions_loaded = false
49
49
  @mutex = Mutex.new
50
+ @identity = normalize_client_identity(@config.identity)
50
51
  @ready = false
51
52
  @closed = false
52
53
 
@@ -121,6 +122,16 @@ module Toggly
121
122
  # `enable_variants` / `evaluated-variants-signed` dual-rail removed in
122
123
  # 1.0 (see CHANGELOG).
123
124
  #
125
+ # Targeting identity resolution (first non-empty wins):
126
+ # 1. per-call `context.identity`
127
+ # 2. {#identity} from Config / {#set_identity}
128
+ # 3. empty (MF anonymous)
129
+ #
130
+ # Groups come only from the per-call context (never Config). Prefer
131
+ # Rails `feature_variant` / ambient `toggly_context`, or
132
+ # `set_identity` / `Config#identity` for non-HTTP hosts — not a
133
+ # hand-built identity on every call.
134
+ #
124
135
  # NOTE: this is the actual A/B assignment. It is unrelated to the
125
136
  # `variant:` telemetry label on `record_usage` / `record_view`, which is
126
137
  # a free-form usage tag (defaults to "enabled"/"disabled").
@@ -137,7 +148,7 @@ module Toggly
137
148
  assignment = VariantAllocator.assign(
138
149
  definition,
139
150
  enabled: enabled,
140
- identity: context&.identity,
151
+ identity: variant_user_id(context),
141
152
  groups: context&.groups || []
142
153
  )
143
154
  return nil if assignment.variant_name.nil?
@@ -159,6 +170,25 @@ module Toggly
159
170
  get_variant(feature_key, context: context)&.configuration_value
160
171
  end
161
172
 
173
+ # Default targeting userId used by {#get_variant} / {#get_variant_value}
174
+ # when the per-call context has no identity.
175
+ #
176
+ # @return [String, nil]
177
+ def identity
178
+ @mutex.synchronize { @identity }
179
+ end
180
+
181
+ # Set the default targeting userId (overrides Config#identity at runtime).
182
+ # Pass +nil+ or an empty string to clear.
183
+ #
184
+ # Named +set_identity+ (not +identity=+) to match Python/Java/Go SDK APIs.
185
+ #
186
+ # @param value [String, nil]
187
+ # @return [String, nil]
188
+ def set_identity(value) # rubocop:disable Naming/AccessorMethodName
189
+ @mutex.synchronize { @identity = normalize_client_identity(value) }
190
+ end
191
+
162
192
  # Get detailed evaluation result
163
193
  #
164
194
  # @param feature_key [String, Symbol] The feature key
@@ -452,6 +482,20 @@ module Toggly
452
482
  @telemetry.record_check(feature_key, enabled, identity)
453
483
  end
454
484
 
485
+ # Allocator userId: per-call context identity when present, else client identity.
486
+ def variant_user_id(context)
487
+ return context.identity if context&.identity?
488
+
489
+ identity
490
+ end
491
+
492
+ def normalize_client_identity(value)
493
+ return nil if value.nil?
494
+
495
+ text = value.to_s
496
+ text.empty? ? nil : text
497
+ end
498
+
455
499
  def log_info(message)
456
500
  @config.logger&.info("[Toggly] #{message}")
457
501
  end
data/lib/toggly/config.rb CHANGED
@@ -12,6 +12,17 @@ module Toggly
12
12
  # @return [String] Application key from Toggly dashboard
13
13
  attr_reader :app_key
14
14
 
15
+ # Default targeting userId for catalog-local +get_variant+ /
16
+ # +get_variant_value+ when neither a per-call +Context#identity+ nor
17
+ # Rails ambient context supplies one. Groups are never taken from Config.
18
+ #
19
+ # Set at configuration time only ({Config.new}, +Toggly.configure+ block).
20
+ # After a {Client} is built, mutating this value has no effect — use
21
+ # {Client#set_identity} for runtime updates.
22
+ #
23
+ # @return [String, nil]
24
+ attr_reader :identity
25
+
15
26
  # @return [String] Environment name (e.g., "Production", "Staging")
16
27
  attr_accessor :environment
17
28
 
@@ -89,6 +100,7 @@ module Toggly
89
100
 
90
101
  def initialize(**options)
91
102
  @app_key = options[:app_key]
103
+ @identity = normalize_identity(options[:identity])
92
104
  @environment = options[:environment] || DEFAULT_ENVIRONMENT
93
105
  @base_url = normalize_url(options[:base_url] || DEFAULT_BASE_URL)
94
106
  @definitions_url = options[:definitions_url]
@@ -172,12 +184,23 @@ module Toggly
172
184
  (@app_key.nil? || @app_key.empty?) && !@defaults.empty?
173
185
  end
174
186
 
187
+ # Configuration-time identity only (e.g. inside +Toggly.configure+ before
188
+ # {Client} is constructed). Does not update an existing client's targeting
189
+ # identity — call {Client#set_identity} after initialization.
190
+ #
191
+ # @param value [String, nil]
192
+ # @return [String, nil]
193
+ def identity=(value)
194
+ @identity = normalize_identity(value)
195
+ end
196
+
175
197
  # Convert to hash
176
198
  #
177
199
  # @return [Hash]
178
200
  def to_h
179
201
  {
180
202
  app_key: @app_key,
203
+ identity: @identity,
181
204
  environment: @environment,
182
205
  base_url: @base_url,
183
206
  definitions_url: @definitions_url,
@@ -208,5 +231,12 @@ module Toggly
208
231
 
209
232
  url.end_with?("/") ? url : "#{url}/"
210
233
  end
234
+
235
+ def normalize_identity(value)
236
+ return nil if value.nil?
237
+
238
+ text = value.to_s
239
+ text.empty? ? nil : text
240
+ end
211
241
  end
212
242
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Toggly
4
- VERSION = "1.0.0"
4
+ VERSION = "1.1.0"
5
5
  end
data/lib/toggly.rb CHANGED
@@ -106,6 +106,29 @@ module Toggly
106
106
  !enabled?(feature_key, context: context)
107
107
  end
108
108
 
109
+ # Assign a catalog-local variant using the global client.
110
+ # See {Client#get_variant}.
111
+ #
112
+ # @param feature_key [String, Symbol]
113
+ # @param context [Context, nil]
114
+ # @return [VariantResult, nil]
115
+ def get_variant(feature_key, context: nil)
116
+ raise Error, "Toggly not configured. Call Toggly.configure first." unless @client
117
+
118
+ @client.get_variant(feature_key, context: context)
119
+ end
120
+
121
+ # Configuration value for the assigned variant. See {Client#get_variant_value}.
122
+ #
123
+ # @param feature_key [String, Symbol]
124
+ # @param context [Context, nil]
125
+ # @return [Object, nil]
126
+ def get_variant_value(feature_key, context: nil)
127
+ raise Error, "Toggly not configured. Call Toggly.configure first." unless @client
128
+
129
+ @client.get_variant_value(feature_key, context: context)
130
+ end
131
+
109
132
  # Reset the global client (mainly for testing)
110
133
  def reset!
111
134
  @client&.close
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.0.0
4
+ version: 1.1.0
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-09-23 00:00:00.000000000 Z
11
+ date: 2026-09-24 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.