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 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
|
# -*- coding: utf-8 -*-
require "delayer/deferred/deferredable/awaitable"
require "delayer/deferred/deferredable/graph"
require "delayer/deferred/deferredable/node_sequence"
module Delayer::Deferred::Deferredable
module Chainable
include Awaitable
include Graph
include NodeSequence
attr_reader :child
# このDeferredが成功した場合の処理を追加する。
# 新しいDeferredのインスタンスを返す。
# このメソッドはスレッドセーフです。
# TODO: procが空のとき例外を発生させる
def next(&proc)
add_child(Delayer::Deferred::Chain::Next.new(&proc))
end
alias deferred next
# このDeferredが失敗した場合の処理を追加する。
# 新しいDeferredのインスタンスを返す。
# このメソッドはスレッドセーフです。
# TODO: procが空のとき例外を発生させる
def trap(&proc)
add_child(Delayer::Deferred::Chain::Trap.new(&proc))
end
alias error trap
# この一連のDeferredをこれ以上実行しない。
# このメソッドはスレッドセーフです。
def cancel
change_sequence(:genocide) unless spoiled?
end
def has_child?
child ? true : false
end
# 子を追加する。
# _Delayer::Deferred::Chainable_ を直接指定できる。通常外部から呼ぶときは _next_ か _trap_ メソッドを使うこと。
# このメソッドはスレッドセーフです。
# ==== Args
# [chainable] 子となるDeferred
# ==== Return
# 必ず _chainable_ を返す
# ==== Raise
# [Delayer::Deferred::SequenceError]
# 既に子が存在している場合
def add_child(chainable)
change_sequence(:get_child) do
chainable.parent = self
@child = chainable
end
end
# 子が追加された時に一度だけコールバックするオブジェクトを登録する。
# observerと言っているが、実際には _Delayer::Deferred::Worker_ を渡して利用している。
# このメソッドはスレッドセーフです。
# ==== Args
# [observer] pushメソッドを備えているもの。引数に _@child_ の値が渡される
# ==== Return
# self
def add_child_observer(observer)
change_sequence(:gaze) do
@child_observer = observer
end
self
end
def awaited
@awaited ||= [].freeze
end
def has_awaited?
not awaited.empty?
end
def add_awaited(awaitable)
@awaited = [*awaited, awaitable].freeze
self
end
# activateメソッドを呼ぶDelayerジョブを登録する寸前に呼ばれる。
def reserve_activate
change_sequence(:reserve)
end
def enter_pass
change_sequence(:pass)
end
def exit_pass
change_sequence(:resume)
end
protected
# 親を再帰的に辿り、一番最初のノードを返す。
# 親が複数見つかった場合は、それらを返す。
def ancestor
if @parent
@parent.ancestor
else
self
end
end
# cancelとかデバッグ用のコールグラフを得るために親を登録しておく。
# add_childから呼ばれる。
def parent=(chainable)
@parent = chainable
end
private
def call_child_observer
if has_child? and defined?(@child_observer)
change_sequence(:called)
@child_observer.push(@child)
end
end
def on_sequence_changed(old_seq, flow, new_seq)
case new_seq
when NodeSequence::BURST_OUT
call_child_observer
when NodeSequence::GENOCIDE
@parent.cancel if defined?(@parent) and @parent
when NodeSequence::RESERVED_C, NodeSequence::RUN_C, NodeSequence::PASS_C, NodeSequence::AWAIT_C, NodeSequence::GRAFT_C
if !has_child?
notice "child: #{@child.inspect}"
raise Delayer::Deferred::SequenceError.new("Sequence changed `#{old_seq.name}' to `#{flow}', but it has no child")
end
end
end
# ノードの名前。サブクラスでオーバライドし、ノードが定義されたファイルの名前や行数などを入れておく。
def node_name
self.class.to_s
end
def graph_mynode
if defined?(@seq_logger)
label = "#{node_name}\n(#{@seq_logger.map(&:name).join('→')})"
else
label = "#{node_name}\n(#{sequence.name})"
end
"#{__id__} [shape=#{graph_shape},label=#{label.inspect}]"
end
end
end
|