Skip to content

Commit d6891d7

Browse files
committed
we can deprecate Rescue with :instance_method and MyHandler.
1 parent 5f5414d commit d6891d7

File tree

2 files changed

+159
-7
lines changed

2 files changed

+159
-7
lines changed

lib/trailblazer/macro/rescue.rb

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module Trailblazer
22
module Macro
3-
NoopHandler = lambda { |*| }
3+
NoopHandler = lambda { |ctx, flow_options, circuit_options, exception:| }
44

55
def self.Rescue(*exceptions, handler: NoopHandler, id: Macro.id_for(nil, macro: :Rescue), &block)
66
exceptions = [StandardError] unless exceptions.any?
@@ -13,14 +13,23 @@ def self.Rescue(*exceptions, handler: NoopHandler, id: Macro.id_for(nil, macro:
1313
handler = Activity::Option.build(handler) do |instance_method|
1414
# this is for :instance_methods
1515
is_instance_method = true
16-
Rescue::InstanceMethodWithCircuitInterfaceAndKwargs.new(instance_method)
16+
callable = Rescue::InstanceMethodWithCircuitInterfaceAndKwargs.new(instance_method)
17+
18+
# assuming we have to deprecate everything.
19+
Rescue::Deprecate::InstanceMethodAtRuntime.new(callable, instance_method) # TODO: remove in 2.3.
1720
end
1821

1922
if is_instance_method
20-
# raise handler.inspect
2123
# deprecate at runtime :D
2224
else
2325
# deprecate now!
26+
if handler.method(:call).arity == -3
27+
# raise handler.inspect
28+
puts "@@@@@ xxx #{handler.inspect}"
29+
Rescue::Deprecate.warning_for_deprecated_callable
30+
# raise "why are we not hitting this from line 291?"
31+
handler = Rescue::Deprecate::Callable.new(handler)
32+
end
2433
end
2534

2635

@@ -35,11 +44,11 @@ def self.Rescue(*exceptions, handler: NoopHandler, id: Macro.id_for(nil, macro:
3544
# This block is evaluated by {Wrap}.
3645
rescue_block = ->(ctx, flow_options, circuit_options, &nested_activity) do
3746
begin
38-
nested_activity.call # FIXME: use yield.
47+
nested_activity.call(ctx, flow_options, circuit_options) # FIXME: use yield.
3948
rescue *exceptions => exception
4049
# DISCUSS: should we deprecate this signature and rather apply the Task API here?
4150
# handler.call(exception, ctx, flow_options, circuit_options) # FIXME: when there's an error here, it shows the wrong exception!
42-
handler.call(exception, ctx, flow_options, circuit_options) # FIXME: when there's an error here, it shows the wrong exception!
51+
handler.call(ctx, flow_options, circuit_options, exception: exception) # FIXME: when there's an error here, it shows the wrong exception!
4352

4453
return ctx, flow_options, Trailblazer::Activity::Left
4554
end
@@ -49,12 +58,52 @@ def self.Rescue(*exceptions, handler: NoopHandler, id: Macro.id_for(nil, macro:
4958
end
5059

5160
module Rescue
61+
module Deprecate
62+
class InstanceMethodAtRuntime < Struct.new(:filter, :instance_method_option)
63+
def call(ctx, flow_options, circuit_options, exception:)
64+
exec_context = circuit_options.fetch(:exec_context)
65+
# hacky, but hey, it's deprecation code!
66+
instance_method = exec_context.method(instance_method_option.instance_variable_get(:@filter))
67+
68+
if instance_method.arity == -3 # this doesn't catch all, but we ignore that.
69+
# Activity::Deprecate.warn(
70+
# Activity::DSL::Linear::Deprecate.dsl_caller_location(after: /forwardable.+Rescue/),
71+
warning = "#{exec_context.class}##{instance_method.name} " + Deprecate.message_for_deprecation_warning
72+
73+
Kernel.warn %([Trailblazer] #{warning}\n) # TODO: allow that in Activity::Deprecate.
74+
end
75+
76+
filter.(ctx, flow_options, circuit_options, exception: exception)
77+
end
78+
end
79+
80+
class Callable < Struct.new(:filter)
81+
def call(ctx, flow_options, circuit_options, exception:)
82+
# Call a user handler with the deprecated interface.
83+
filter.(exception, [ctx, flow_options], **circuit_options)
84+
end
85+
end
86+
87+
def self.message_for_deprecation_warning
88+
%(The Rescue() handler has a new interface, the old `(exception, (ctx), ..), **` signature is deprecated.
89+
Please use (ctx, flow_options, circuit_options, exception:, **) , check ### FIXME _____---------------)
90+
end
91+
92+
def self.warning_for_deprecated_callable
93+
Activity::Deprecate.warn(
94+
Activity::DSL::Linear::Deprecate.dsl_caller_location(after: /forwardable.+Rescue/),
95+
message_for_deprecation_warning
96+
)
97+
end
98+
end
5299
# This is kind of specific to Rescue, but might be useful elsewhere. We want to run a circuit interface
53100
# *instance method* with keyword arguments. in a normal CI-conform callable, this doesn't need any
54101
# wrapping, but instance_methods are different.
55102
class InstanceMethodWithCircuitInterfaceAndKwargs < Struct.new(:instance_method_option) # DISCUSS: move somewhere else?
56-
def call(ctx, flow_options, circuit_options, **kwargs)
57-
instance_method_option.(ctx, flow_options, circuit_options, keyword_arguments: kwargs, **circuit_options)
103+
def call(ctx, flow_options, circuit_options, exception:, **kwargs)
104+
# Call the method the old, weird style with exception as a positional argument
105+
# followed by the composite circuit interface.
106+
instance_method_option.(exception, [ctx, flow_options], keyword_arguments: kwargs, **circuit_options)
58107
end
59108
end
60109
end

test/docs/rescue_test.rb

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,3 +251,106 @@ def validate(ctx, validate: false, seq:, **)
251251
end
252252
end
253253
end
254+
255+
class RescueDeprecateHandlerWithPositionalExceptionArgumentTest < Minitest::Spec
256+
Song = Class.new
257+
module Song::Operation; end
258+
259+
#:rescue-handler-2-1
260+
class MyHandler
261+
def self.call(exception, (ctx), *)
262+
ctx[:seq] << :MyHandler
263+
ctx[:exception_class] = exception.class
264+
end
265+
end
266+
#:rescue-handler-2-1 end
267+
268+
class Song::Operation::Create < Trailblazer::Activity::Railway
269+
step :create_model
270+
step :notify
271+
left :log_error
272+
#~methods
273+
include T.def_steps(:create_model, :upload, :notify, :log_error)
274+
include Rehash
275+
#~methods end
276+
end
277+
278+
it do
279+
activity = nil
280+
_, warnings = capture_io do
281+
activity = Class.new(Trailblazer::Activity::Railway) do
282+
step Rescue(RuntimeError, handler: MyHandler) {
283+
step :rehash
284+
}
285+
include Rehash
286+
end
287+
end
288+
line_number_for_rescue = __LINE__ - 6
289+
290+
# Deprecation warning at compile time.
291+
assert_equal warnings, %([Trailblazer] #{File.realpath(__FILE__)}:#{line_number_for_rescue} The Rescue() handler has a new interface, the old `(exception, (ctx), ..), **` signature is deprecated.
292+
Please use (ctx, flow_options, circuit_options, exception:, **) , check ### FIXME _____---------------
293+
)
294+
295+
_, warnings = capture_io do
296+
assert_invoke activity, seq: "[:rehash]"
297+
end
298+
assert_equal warnings, ""
299+
300+
# _, warnings = capture_io do
301+
assert_invoke activity, rehash_raise: RuntimeError, terminus: :failure, seq: "[:rehash, :MyHandler]", expected_ctx_variables: {exception_class: RuntimeError}
302+
# end
303+
assert_equal warnings, ""
304+
end
305+
end
306+
307+
class RescueDeprecateInstanceMethodHandlerWithPositionalExceptionArgumentTest < Minitest::Spec
308+
Song = Class.new
309+
module Song::Operation; end
310+
311+
312+
class Song::Operation::Create < Trailblazer::Activity::Railway
313+
step Rescue(RuntimeError, handler: :my_handler) {
314+
step :upload
315+
step :rehash
316+
}
317+
318+
#:rescue-handler-instance-method-2-1
319+
def my_handler(exception, (ctx), *)
320+
# def my_handler(ctx, flow_options, *, **)
321+
ctx[:seq] << :my_handler
322+
ctx[:exception_class] = exception.class
323+
end
324+
#:rescue-handler-instance-method-2-1 end
325+
#~methods
326+
include T.def_steps(:upload)
327+
include Rehash
328+
#~methods end
329+
end
330+
331+
it {
332+
_, warnings = capture_io do
333+
assert_invoke Song::Operation::Create, seq: "[:upload, :rehash]"
334+
end
335+
336+
assert_equal warnings, ""
337+
}
338+
339+
it {
340+
_, warnings = capture_io do
341+
assert_invoke Song::Operation::Create, rehash_raise: RuntimeError, terminus: :failure, seq: "[:upload, :rehash, :my_handler]", expected_ctx_variables: {exception_class: RuntimeError}
342+
end
343+
344+
assert_equal warnings, %([Trailblazer] RescueDeprecateInstanceMethodHandlerWithPositionalExceptionArgumentTest::Song::Operation::Create#my_handler The Rescue() handler has a new interface, the old `(exception, (ctx), ..), **` signature is deprecated.
345+
Please use (ctx, flow_options, circuit_options, exception:, **) , check ### FIXME _____---------------\n)
346+
}
347+
end
348+
349+
=begin
350+
Option#call implies we need an :exec_context kwarg, since we want to separate the circuit_options from the Options-specific parameters. E.g. what if the filter
351+
calls another activity and needs the original circuit_options?
352+
353+
that means pure circuit_options filter are called differently from Option ones (the first don't receive kwargs)
354+
=end
355+
356+

0 commit comments

Comments
 (0)