quonfig 0.0.13 → 0.0.14

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: 719dca4a64475d915ca5b53cef7b4f95903260e1e11765832d2436386d2e509b
4
- data.tar.gz: ab91066370020b38b2bd8148ac4e4193a172921ccf4ae25b2d3985d21c40361b
3
+ metadata.gz: b25ea20d7f44acff4ed82e17522a9fb6055791c4f1e0c861075974e5ae37421f
4
+ data.tar.gz: e0c260d2d13926e21f2525c7686a24f8dec2f1fa998efa039db59baf4447cd60
5
5
  SHA512:
6
- metadata.gz: 0d84d33ce8cab73a3f3c3a27990205db98f649fd1f6b8319a75afce8cd73e746f140a67d5c8e70365d24f6a363d3676f9680c4beaf804b838399fcf37890f297
7
- data.tar.gz: 9d31eb60838a4ca692b960aab0d719b734e97597135e0a25070f586750d4f65a46e45514d84badbe7ac827821392b98d9eba107784a8c828dede738899f55eb6
6
+ metadata.gz: da91dbd4f9cc300f2dab9e8f39a73033e642d94272288cbcacf4358eb28f4f9b064f8fbe8301c5c26e1b342cd3cd76179d362029e06379bcac39685c3a050cb2
7
+ data.tar.gz: ac77088e6a6e0256d947f40b26abb9527bb55cff8a3fa39eaaebf91c43746379d5fa2325bd06e049922b2cbc8521f78252bdd79106c6f1ae7f1a0264f4033ab6
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.0.14 - 2026-05-10
4
+
5
+ - **Feat: expose `variant` and `flag_metadata` on `EvaluationDetails` (qfg-9dbl).** OpenFeature's `EvaluationDetails` Ruby return type now carries the variant name and the flag-level metadata hash alongside the resolved value/reason. Brings sdk-ruby to parity with the other SDKs' detail surfaces and lets host apps (incl. the Ruby OpenFeature provider) read variant/metadata without re-fetching the config.
6
+ - **Test: regenerate integration tests from rubocop-clean templates (qfg-vrck).** The integration suite under `test/integration/` is now generated from templates that pass `bundle exec rubocop` on first emit, so future regenerations don't trigger a follow-up autofix commit.
7
+
3
8
  ## 0.0.13 - 2026-05-07
4
9
 
5
10
  - **Feat: `IS_PRESENT` and `IS_NOT_PRESENT` targeting operators (qfg-7jnb.6).** Both take only `propertyName` (no `valueToMatch`). `IS_PRESENT` resolves the dotted path against the merged context and returns true iff the value is non-nil. Type-agnostic — empty string `""`, `0`, and `false` all count as **present**; only `nil` / missing keys (including missing nested paths) are absent. `IS_NOT_PRESENT` is the negation. Implemented explicitly without ActiveSupport's `present?` / `blank?`, which would have given the wrong semantics on `""` and `false`. Matches sdk-node, sdk-go, sdk-python, sdk-ruby, sdk-javascript wire behaviour. Closes the integration-test parity gap that left 7 RSpec/Minitest cases red since the operators landed in `integration-test-data`.
@@ -547,19 +547,25 @@ module Quonfig
547
547
  value: nil,
548
548
  reason: Quonfig::EvaluationDetails::REASON_ERROR,
549
549
  error_code: Quonfig::EvaluationDetails::ERROR_FLAG_NOT_FOUND,
550
- error_message: e.message
550
+ error_message: e.message,
551
+ variant: build_variant(Quonfig::EvaluationDetails::REASON_ERROR, nil, nil),
552
+ flag_metadata: build_flag_metadata(nil, nil, nil, nil, nil)
551
553
  )
552
554
  end
553
555
 
554
556
  if result.nil?
555
557
  return Quonfig::EvaluationDetails.new(
556
558
  value: nil,
557
- reason: Quonfig::EvaluationDetails::REASON_DEFAULT
559
+ reason: Quonfig::EvaluationDetails::REASON_DEFAULT,
560
+ variant: build_variant(Quonfig::EvaluationDetails::REASON_DEFAULT, nil, nil),
561
+ flag_metadata: build_flag_metadata(nil, nil, nil, nil, nil)
558
562
  )
559
563
  end
560
564
 
561
565
  record_evaluation_for_telemetry(result)
562
566
 
567
+ config_id = result.config&.dig('id') || result.config&.dig(:id)
568
+ config_type = result.config&.dig('type') || result.config&.dig(:type)
563
569
  raw_value = result.unwrapped_value
564
570
 
565
571
  begin
@@ -569,23 +575,64 @@ module Quonfig
569
575
  value: nil,
570
576
  reason: Quonfig::EvaluationDetails::REASON_ERROR,
571
577
  error_code: Quonfig::EvaluationDetails::ERROR_TYPE_MISMATCH,
572
- error_message: e.message
578
+ error_message: e.message,
579
+ variant: build_variant(Quonfig::EvaluationDetails::REASON_ERROR, nil, nil),
580
+ flag_metadata: build_flag_metadata(config_id, config_type, nil, nil, nil)
573
581
  )
574
582
  end
575
583
 
584
+ reason = result.of_reason
576
585
  Quonfig::EvaluationDetails.new(
577
586
  value: coerced,
578
- reason: result.of_reason
587
+ reason: reason,
588
+ variant: build_variant(reason, result.rule_index, result.weighted_value_index),
589
+ flag_metadata: build_flag_metadata(
590
+ config_id, config_type, result.rule_index, result.weighted_value_index, reason
591
+ )
579
592
  )
580
593
  rescue StandardError => e
581
594
  Quonfig::EvaluationDetails.new(
582
595
  value: nil,
583
596
  reason: Quonfig::EvaluationDetails::REASON_ERROR,
584
597
  error_code: Quonfig::EvaluationDetails::ERROR_GENERAL,
585
- error_message: e.message
598
+ error_message: e.message,
599
+ variant: build_variant(Quonfig::EvaluationDetails::REASON_ERROR, nil, nil),
600
+ flag_metadata: build_flag_metadata(nil, nil, nil, nil, nil)
586
601
  )
587
602
  end
588
603
 
604
+ # Build the variant string per the cross-SDK spec
605
+ # (project/plans/openfeature-resolution-details.md §2).
606
+ def build_variant(reason, rule_index, weighted_value_index)
607
+ case reason
608
+ when Quonfig::EvaluationDetails::REASON_STATIC
609
+ 'static'
610
+ when Quonfig::EvaluationDetails::REASON_TARGETING_MATCH
611
+ "targeting:#{rule_index || 0}"
612
+ when Quonfig::EvaluationDetails::REASON_SPLIT
613
+ "split:#{weighted_value_index || 0}"
614
+ else
615
+ 'default'
616
+ end
617
+ end
618
+
619
+ # Build the flag_metadata hash per the cross-SDK spec
620
+ # (project/plans/openfeature-resolution-details.md §3) using Ruby's
621
+ # snake_case keys and the wire's snake_case config_type values.
622
+ def build_flag_metadata(config_id, config_type, rule_index, weighted_value_index, reason)
623
+ md = {}
624
+ md['config_id'] = config_id if config_id
625
+ md['config_type'] = config_type if config_type
626
+ env = @options.environment
627
+ md['environment'] = env if env && !env.empty?
628
+ if rule_index && rule_index >= 0 &&
629
+ [Quonfig::EvaluationDetails::REASON_TARGETING_MATCH, Quonfig::EvaluationDetails::REASON_SPLIT].include?(reason)
630
+ md['rule_index'] = rule_index
631
+ end
632
+ md['weighted_value_index'] = weighted_value_index if weighted_value_index && reason == Quonfig::EvaluationDetails::REASON_SPLIT
633
+ md
634
+ end
635
+
589
636
  def typed_get(key, expected_type, default:, context:)
590
637
  jit = context == NO_DEFAULT_PROVIDED ? NO_DEFAULT_PROVIDED : context
591
638
  value = get(key, default, jit)
@@ -28,13 +28,16 @@ module Quonfig
28
28
  ERROR_TYPE_MISMATCH = 'TYPE_MISMATCH'
29
29
  ERROR_GENERAL = 'GENERAL'
30
30
 
31
- attr_reader :value, :reason, :error_code, :error_message
31
+ attr_reader :value, :reason, :error_code, :error_message, :variant, :flag_metadata
32
32
 
33
- def initialize(value:, reason:, error_code: nil, error_message: nil)
33
+ def initialize(value:, reason:, error_code: nil, error_message: nil,
34
+ variant: nil, flag_metadata: nil)
34
35
  @value = value
35
36
  @reason = reason
36
37
  @error_code = error_code
37
38
  @error_message = error_message
39
+ @variant = variant
40
+ @flag_metadata = flag_metadata
38
41
  end
39
42
 
40
43
  def ==(other)
@@ -42,18 +45,22 @@ module Quonfig
42
45
  other.value == @value &&
43
46
  other.reason == @reason &&
44
47
  other.error_code == @error_code &&
45
- other.error_message == @error_message
48
+ other.error_message == @error_message &&
49
+ other.variant == @variant &&
50
+ other.flag_metadata == @flag_metadata
46
51
  end
47
52
  alias eql? ==
48
53
 
49
54
  def hash
50
- [@value, @reason, @error_code, @error_message].hash
55
+ [@value, @reason, @error_code, @error_message, @variant, @flag_metadata].hash
51
56
  end
52
57
 
53
58
  def inspect
54
59
  parts = ["value=#{@value.inspect}", "reason=#{@reason.inspect}"]
55
60
  parts << "error_code=#{@error_code.inspect}" if @error_code
56
61
  parts << "error_message=#{@error_message.inspect}" if @error_message
62
+ parts << "variant=#{@variant.inspect}" if @variant
63
+ parts << "flag_metadata=#{@flag_metadata.inspect}" if @flag_metadata
57
64
  "#<Quonfig::EvaluationDetails #{parts.join(' ')}>"
58
65
  end
59
66
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Quonfig
4
- VERSION = '0.0.13'
4
+ VERSION = '0.0.14'
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: 0.0.13
4
+ version: 0.0.14
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-07 00:00:00.000000000 Z
11
+ date: 2026-05-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport