File: deferredable.rb

package info (click to toggle)
mikutter 3.0.7%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 9,396 kB
  • ctags: 1,916
  • sloc: ruby: 16,619; sh: 117; makefile: 27
file content (138 lines) | stat: -rw-r--r-- 4,183 bytes parent folder | download | duplicates (2)
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