module Deferable
def defer &block
@defered_methods << block
end
def self.included(mod)
mod.extend ClassMethods
end
module ClassMethods
def deferable method
original_method = instance_method(method)
define_method(method) do |*args|
@@defered_method_stack ||= []
@@defered_method_stack << @defered_methods
@defered_methods = []
begin
original_method.bind(self).(*args)
ensure
@defered_methods.each {|m| m.call }
@defered_methods = @@defered_method_stack.pop
end
end
end
end
end
class Test
include Deferable
def test
defer { puts "world" }
puts "hello"
end
def stacked_methods str
defer { puts "and not broken" }
defer { puts "at all" }
puts str
test
end
def throw_exception
defer { puts "will be executed even if exception is thrown" }
throw Exception.new
end
deferable :test
deferable :stacked_methods
deferable :throw_exception
end
示例调用:
t = Test.new
t.test
# -> Output:
# hello
# world
t.stacked_methods "stacked methods"
# -> Output:
# stacked methods
# hello
# world
# and not broken
# at all
t.throw_exception
# -> Output:
# will be executed even if exception is thrown
# deferable.rb:45:in `throw': uncaught throw #<Exception: Exception> (UncaughtThrowError)
# from deferable.rb:45:in `throw_exception'
# from deferable.rb:18:in `call'
# from deferable.rb:18:in `block in deferable'
# from deferable.rb:59:in `<main>'
2条答案
按热度按时间r8xiu3jd1#
在ruby中没有与
defer
语句等价的语句,但是如果你想确保特定的代码块被执行,你可以使用ensure
语句。不同之处在于,您不能像defer那样堆叠代码块,但结果是相同的。一个街区
方法中
Object#ensure标记开始/结束块的最后一个可选子句,通常在块还包含rescue子句的情况下。确保子句中的代码被保证执行,无论控制是否流向救援块。
ddhy6vgd2#
它没有这样的语句,但是您可以使用元编程来获得这种行为。
示例调用: