1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
# -*- coding: utf-8 -*-
# なんでもDeferred
module Deferredable
# このDeferredが成功した場合の処理を追加する。
# 新しいDeferredのインスタンスを返す
def next(&proc)
_post(:ok, &proc)
end
alias deferred next
# このDeferredが失敗した場合の処理を追加する。
# 新しいDeferredのインスタンスを返す
def trap(&proc)
_post(:ng, &proc)
end
# Deferredを直ちに実行する
def call(value = nil)
_call(:ok, value)
end
# Deferredを直ちに失敗させる
def fail(exception = nil)
_call(:ng, exception)
end
# この一連のDeferredをこれ以上実行しない
def cancel
@callback = {
:backtrace => {},
:ok => lambda{ |x| x },
:ng => Deferred.method(:fail) }
end
def callback
@callback ||= {
:backtrace => {},
:ok => lambda{ |x| x },
:ng => Deferred.method(:fail) } end
# エラーをキャッチして、うまい具合にmikutterに表示する。
# このあとにdeferredをくっつけることもできるが、基本的にはdeferredチェインの終了の時に使う。
# なお、terminateは受け取ったエラーを再度発生させるので、terminateでエラーを処理した後に特別なエラー処理を挟むこともできる
# ==== Args
# [message] 表示用エラーメッセージ。偽ならエラーはユーザに表示しない(コンソールのみ)
# [&message_generator] エラーを引数に呼ばれる。 _message_ を返す
# ==== Return
# Deferred
def terminate(message = nil, &message_generator)
self.trap{ |e|
begin
notice e
message = message_generator.call(e) if message_generator
if(message)
if(e.is_a?(Net::HTTPResponse))
Plugin.activity :error, "#{message} (#{e.code} #{e.body})"
else
e = 'error' if not e.respond_to?(:to_s)
Plugin.activity :error, "#{message} (#{e})", exception: e end end
rescue Exception => inner_error
error inner_error end
Deferred.fail(e) } end
# second 秒待って次を実行する
# ==== Args
# [second] 待つ秒数(second)
# ==== Return
# Deferred
def wait(second)
self.next{ Thread.new{ sleep second } } end
private
def _call(stat = :ok, value = nil)
begin
catch(:__deferredable_success) {
failed = catch(:__deferredable_fail) {
n_value = _execute(stat, value)
if n_value.is_a? Deferredable
n_value.next{ |result|
if defined?(@next)
@next.call(result)
else
@next end
}.trap{ |exception|
if defined?(@next)
@next.fail(exception)
else
@next end }
else
if defined?(@next)
Delayer.new{ @next.call(n_value) }
else
regist_next_call(:ok, n_value) end end
throw :__deferredable_success
}
_fail_action(failed)
}
rescue Exception => e
_fail_action(e) end end
def _execute(stat, value)
if Mopt.debug
r_start = Process.times.utime
result = callback[stat].call(value)
if (r_end = Process.times.utime - r_start) > 0.1
pos = callback[:backtrace][stat].find{|x|not x.include? "deferred"}
pos = callback[:backtrace][stat].first if not pos
Plugin.call(:processtime, :deferred, "#{"%.2f" % r_end},#{pos}") end
result
else
callback[stat].call(value) end end
def _post(kind, &proc)
@next = Deferred.new(self)
@next.callback[kind] = proc
@next.callback[:backtrace][kind] = caller(1)
if defined?(@next_call_stat) and defined?(@next_call_value)
@next.__send__({ok: :call, ng: :fail}[@next_call_stat], @next_call_value)
elsif defined?(@follow) and @follow.nil?
call end
@next
end
def regist_next_call(stat, value)
@next_call_stat, @next_call_value = stat, value
self end
def _fail_action(e)
if defined?(@next)
Delayer.new{ @next.fail(e) }
else
regist_next_call(:ng, e) end
end
end
|