bitfab 0.38.1 → 0.38.2

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: 2816ce4e3bcd11bb1335ba7dac6b36376277d17075cf853d26c9befa231a758b
4
- data.tar.gz: 7e923c2efb8dfd38edbdd592972a8f236a3fa81cdfabcbd46b8303ba4160b01e
3
+ metadata.gz: 29901e7611834921e9378870830af40c08e5af02d1ca0708585a02b169b12e6e
4
+ data.tar.gz: b42984a3710e9399159fd44af3aaac7b76e3155bd3121eaa5482aa27570fbb2e
5
5
  SHA512:
6
- metadata.gz: 15a2df82260f61733b90da3c110b195c92373c187c3eddce5faa041f44499bb008ec5a14cd27dab22b63e2973540010cc2e2f10fcc2dcd75481405d018a107f6
7
- data.tar.gz: 13968f6a9f8d3c88ea2845d0d59e0cc1d31ffadc1054311addeedc1e4ba92d94aea504f0f5d11e9891fd0a233e92d285e635e5d8e85e46ebfdf553cdedc45005
6
+ metadata.gz: 576a44cbe7e2fdf7270f14af3d8a98541bb2e15cd2b02289c18d58f033688d6797b2b3a0cfc8153f1ba8adbf0d72f9b9b9241e1bfd5fbd3ffcc687a4cb9adb44
7
+ data.tar.gz: ce3586fbdc541ffad387d87ca4299ee34df5e0c61a5c01a6b5112ee11084cb7900d6616f9e3db45ec0386fc019bc1712f267ea3c5799809880ae3611d3d30cb9
data/lib/bitfab/client.rb CHANGED
@@ -122,14 +122,15 @@ module Bitfab
122
122
  # traces were captured. Receives (args, kwargs, ctx) where ctx is
123
123
  # { original_trace_id:, original_span_id: } (with deprecated
124
124
  # source_trace_id/source_span_id aliases), and returns [new_args, new_kwargs].
125
- # @param mock_override [Hash, Array<Hash>, nil] optional selective mock
126
- # override(s), each a { match:, value: } hash. match is a callable:
125
+ # @param mock_override [Hash, Proc, Array<Hash, Proc>, nil] optional selective
126
+ # mock override(s), as { match:, value: } hashes or global resolvers. match is a callable:
127
127
  # match.call(node) selects spans to substitute (node is
128
128
  # { trace_function_key:, span_name:, type:, original_span_id: }). value is
129
129
  # EITHER a flat value injected directly OR a callable invoked with
130
130
  # { node:, inputs:, kwargs:, get_original_output: }; its result becomes
131
131
  # the span's output (full replacement), so downstream real code runs against it. The
132
- # first matching override wins. Per-call overrides take precedence over
132
+ # first matching override that does not return NO_MOCK_OVERRIDE wins.
133
+ # Per-call overrides take precedence over
133
134
  # those registered via register_mock_override, and both take precedence
134
135
  # over the base mock strategy. The root span is never overridden.
135
136
  # @param db_branch [Boolean, Hash, nil] +true+ or a Hash enables database
@@ -169,10 +170,9 @@ module Bitfab
169
170
  # clear_mock_overrides to reset. Per-call replay(mock_override:) overrides
170
171
  # take precedence, and both take precedence over the base mock strategy.
171
172
  #
172
- # Accepts either the (match, value) positional form or the keyword form
173
- # (a { match:, value: } hash is also accepted). `match` must be callable;
174
- # `value` is either a flat value injected directly or a callable invoked
175
- # with the ctx hash.
173
+ # Accepts the (match, value) positional form, the keyword/hash form, one
174
+ # global resolver callable, or (trace_function_key, override_or_resolver).
175
+ # A resolver can return Bitfab::NO_MOCK_OVERRIDE to decline the span.
176
176
  #
177
177
  # @example keyword form (primary), flat value
178
178
  # client.register_mock_override(
@@ -186,19 +186,43 @@ module Bitfab
186
186
  # ->(ctx) { {label: "refund", inputs: ctx[:inputs]} }
187
187
  # )
188
188
  #
189
- # @param positional [Array] either (match, value) or a single
190
- # { match:, value: } hash
189
+ # @param positional [Array] a (match, value) pair, one { match:, value: }
190
+ # hash, one global resolver, or a trace function key followed by a
191
+ # resolver or override hash
191
192
  # @param match [#call, nil] keyword form matcher
192
193
  # @param value [Object, #call, nil] keyword form injected value or producer
193
194
  # @return [void]
194
195
  def register_mock_override(*positional, match: nil, value: VALUE_UNSET)
195
196
  if match.nil? && value.equal?(VALUE_UNSET)
196
- if positional.length == 1 && positional[0].is_a?(Hash)
197
+ if positional.length == 2 && positional[0].is_a?(String)
198
+ trace_function_key, keyed_override = positional
199
+ if keyed_override.is_a?(Hash) && keyed_override[:match].respond_to?(:call) && keyed_override.key?(:value)
200
+ keyed_match = keyed_override[:match]
201
+ @mock_overrides << {
202
+ match: ->(node) { node[:trace_function_key] == trace_function_key && keyed_match.call(node) },
203
+ value: keyed_override[:value]
204
+ }
205
+ return nil
206
+ end
207
+ if keyed_override.respond_to?(:call)
208
+ @mock_overrides << {
209
+ match: ->(node) { node[:trace_function_key] == trace_function_key },
210
+ value: keyed_override
211
+ }
212
+ return nil
213
+ end
214
+ raise ArgumentError,
215
+ "register_mock_override(trace_function_key, override) requires " \
216
+ "a { match:, value: } hash or callable resolver."
217
+ elsif positional.length == 1 && positional[0].is_a?(Hash)
197
218
  override = positional[0]
198
219
  match = override[:match]
199
220
  # Distinguish an omitted :value from an explicit `value: nil`: the
200
221
  # key's presence, not a nil read, is what marks it provided.
201
222
  value = override.key?(:value) ? override[:value] : VALUE_UNSET
223
+ elsif positional.length == 1 && positional[0].respond_to?(:call)
224
+ @mock_overrides << {match: ->(_) { true }, value: positional[0]}
225
+ return nil
202
226
  elsif positional.length == 2
203
227
  match, value = positional
204
228
  end
@@ -782,8 +806,9 @@ module Bitfab
782
806
  type: span_type,
783
807
  original_span_id: mock_entry && mock_entry[:source_span_id]
784
808
  }
785
- override = overrides.find { |o| o[:match].call(node) }
786
- if override
809
+ overrides.each do |override|
810
+ next unless override[:match].call(node)
811
+
787
812
  value = override[:value]
788
813
  # value is EITHER a flat value (injected directly) OR a callable
789
814
  # invoked with the ctx hash. The result IS the span's output (full
@@ -791,7 +816,10 @@ module Bitfab
791
816
  # override always injects, execute_span short-circuits even when this
792
817
  # is nil (nil != MOCK_REPLAY_MISS). To inject a proc as the literal
793
818
  # output, wrap it in a callable value.
794
- return [value, "override"] unless value.respond_to?(:call)
819
+ unless value.respond_to?(:call)
820
+ return [value, "override"] unless value.equal?(NO_MOCK_OVERRIDE)
821
+ next
822
+ end
795
823
 
796
824
  get_original_output = lambda do
797
825
  unless mock_entry
@@ -799,7 +827,8 @@ module Bitfab
799
827
  end
800
828
  resolve_recorded_output(mock_entry, replay_ctx)
801
829
  end
802
- return [value.call({node:, inputs: args, kwargs:, get_original_output:}), "override"]
830
+ resolved = value.call({node:, inputs: args, kwargs:, get_original_output:})
831
+ return [resolved, "override"] unless resolved.equal?(NO_MOCK_OVERRIDE)
803
832
  end
804
833
  end
805
834
 
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bitfab
4
+ # Return from an override resolver to continue to the next override and then
5
+ # the replay's base mock strategy.
6
+ NO_MOCK_OVERRIDE = Object.new.freeze
7
+
4
8
  # Selective mock overrides for replay. Mirrors the TypeScript SDK's
5
9
  # mockOverride feature.
6
10
  #
@@ -10,8 +14,10 @@ module Bitfab
10
14
  # substituted output. This is a third mock mode alongside "run real code" and
11
15
  # "replay recorded output" (see MOCK_STRATEGIES in replay.rb).
12
16
  #
13
- # An override is a (match, value) pair, represented as a symbol-keyed hash
14
- # { match:, value: }:
17
+ # An override is a (match, value) pair represented as { match:, value: }, or
18
+ # one global resolver invoked for every child span. A resolver can route on
19
+ # ctx[:node][:trace_function_key] and return Bitfab::NO_MOCK_OVERRIDE to
20
+ # decline the current span.
15
21
  #
16
22
  # - match is a callable (proc/lambda): match.call(node) -> boolean. node is a
17
23
  # structural-metadata hash { trace_function_key:, span_name:, type:,
@@ -38,9 +44,8 @@ module Bitfab
38
44
  module MockOverride
39
45
  module_function
40
46
 
41
- # Normalize the per-call `mock_override` option (a single override hash, an
42
- # array of them, or nil) into an array. First match wins downstream, so
43
- # order is preserved.
47
+ # Normalize the per-call `mock_override` option (an override hash, a global
48
+ # resolver callable, an array, or nil) into an ordered override array.
44
49
  #
45
50
  # Each override is validated: it must be a hash with a callable `:match` and
46
51
  # must include a `:value` key. A forgotten `:value` raises rather than
@@ -49,12 +54,15 @@ module Bitfab
49
54
  # `register_mock_override`, and the Python/TypeScript SDKs, where the per-call
50
55
  # override object requires a value.
51
56
  #
52
- # @param mock_override [Hash, Array<Hash>, nil]
57
+ # @param mock_override [Hash, Proc, Array<Hash, Proc>, nil]
53
58
  # @return [Array<Hash>]
54
59
  def normalize(mock_override)
55
60
  return [] if mock_override.nil?
56
61
 
57
62
  overrides = mock_override.is_a?(Array) ? mock_override : [mock_override]
63
+ overrides = overrides.map do |override|
64
+ override.respond_to?(:call) ? {match: ->(_) { true }, value: override} : override
65
+ end
58
66
  overrides.each do |override|
59
67
  unless override.is_a?(Hash) && override[:match].respond_to?(:call)
60
68
  raise ArgumentError,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bitfab
4
- VERSION = "0.38.1"
4
+ VERSION = "0.38.2"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bitfab
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.38.1
4
+ version: 0.38.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Harvest Team