aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorToshiaki Asai <toshi.alternative@gmail.com>2015-08-26 19:50:38 +0900
committerToshiaki Asai <toshi.alternative@gmail.com>2015-08-26 23:07:39 +0900
commit37555d89ee0e562fc04ba38e7e50a99affe500d5 (patch)
tree326a26005cb048d00aae88805fa32b3b674fab82
parent5551f7360398e46525e9d54553e441a6a713fb6d (diff)
downloadmikutter-37555d89ee0e562fc04ba38e7e50a99affe500d5.tar.gz
delayer-deferred
-rw-r--r--Gemfile1
-rw-r--r--core/boot/delayer.rb30
-rw-r--r--core/lib/deferred.rb4
-rw-r--r--core/lib/deferred/deferred.rb106
-rw-r--r--core/lib/deferred/deferredable.rb138
-rw-r--r--core/lib/mikutwitter/query.rb2
-rw-r--r--core/serialthread.rb3
-rw-r--r--test/core/lib/test_deferred.rb201
-rw-r--r--test/core/lib/test_instance_storage.rb55
9 files changed, 32 insertions, 508 deletions
diff --git a/Gemfile b/Gemfile
index 8a434ab5..24a48f29 100644
--- a/Gemfile
+++ b/Gemfile
@@ -16,6 +16,7 @@ group :default do
gem 'typed-array', '~> 0.1'
gem 'delayer', '~> 0.0'
gem 'pluggaloid', '>= 1.0.1', '< 2.0'
+ gem 'delayer-deferred', '>= 1.0', '< 2.0'
end
group :test do
diff --git a/core/boot/delayer.rb b/core/boot/delayer.rb
index 7365698f..6daa48ae 100644
--- a/core/boot/delayer.rb
+++ b/core/boot/delayer.rb
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
-miquire :lib, "delayer"
+require "delayer"
+require "delayer/deferred"
Delayer.default = Delayer.generate_class(priority: [:ui_response,
:routine_active,
@@ -10,8 +11,33 @@ Delayer.default = Delayer.generate_class(priority: [:ui_response,
default: :routine_passive,
expire: 0.02)
+Deferred = Delayer::Deferred
+
+module Delayer::Deferred::Deferredable
+ # エラーをキャッチして、うまい具合に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
+end
Delayer.register_remain_hook do
Thread.main.wakeup
end
-
diff --git a/core/lib/deferred.rb b/core/lib/deferred.rb
deleted file mode 100644
index 4b7a396a..00000000
--- a/core/lib/deferred.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-# -*- coding: utf-8 -*-
-
-require 'deferred/deferredable'
-require 'deferred/deferred'
diff --git a/core/lib/deferred/deferred.rb b/core/lib/deferred/deferred.rb
deleted file mode 100644
index c15dfa60..00000000
--- a/core/lib/deferred/deferred.rb
+++ /dev/null
@@ -1,106 +0,0 @@
-# -*- coding: utf-8 -*-
-
-class Deferred
- include Deferredable
-
- def initialize(follow = nil)
- @follow = follow
- @backtrace = caller if Mopt.debug
- end
-
- alias :deferredable_cancel :cancel
- def cancel
- deferredable_cancel
- @follow.cancel if @follow.is_a? Deferredable end
-
- class << self
- # 実行中のDeferredを失敗させる。raiseと違って、Exception以外のオブジェクトをtrap()に渡すことができる。
- # Deferredのnextとtrapの中でだけ呼び出すことができる。
- # ==== Args
- # [value] trap()に渡す値
- # ==== Throw
- # :__deferredable_fail をthrowする
- def fail(value)
- throw(:__deferredable_fail, value) end
-
- # 複数のdeferredを引数に取って、それら全ての実行が終了したら、
- # その結果を引数の順番通りに格納したArrayを引数に呼ばれるDeferredを返す。
- # 引数のDeferredが一つでも失敗するとこのメソッドの返すDeferredも失敗する。
- # ==== Args
- # [defer] 終了を待つDeferredオブジェクト
- # [*follow] 他のDeferredオブジェクト
- # ==== Return
- # Deferred
- def when(defer = nil, *follow)
- return deferred{ [] } if defer.nil?
- defer.next{ |res|
- Deferred.when(*follow).next{ |follow_res|
- [res] + follow_res
- }
- }
- end
-
- # Kernel#systemを呼び出して、コマンドが成功たら成功するDeferredを返す。
- # 失敗した場合、trap{}ブロックには $? の値(Process::Status)か、例外が発生した場合それが渡される
- # ==== Args
- # [*args] Kernel#system の引数
- # ==== Return
- # Deferred
- def system(*args)
- promise = Deferred.new(true)
- Thread.new{
- if Kernel.system(*args)
- promise.call(true)
- else
- promise.fail($?) end
- }.trap{ |e|
- promise.fail(e) }
- promise
- end
- end
-
-end
-
-class Thread
- include Deferredable
-
- alias _deferredable_trap initialize
- def initialize(*args, &proc)
- _deferredable_trap(*args){ |*args|
- begin
- result = proc.call(*args)
- self.call(result)
- result
- rescue Exception => e
- self.fail(e)
- raise e
- end
- }
- end
-
- alias :deferredable_cancel :cancel
- def cancel
- deferredable_cancel
- kill end
-
-end
-
-module Enumerable
- # 遅延each。あとで実行されるし、あんまりループに時間がかかるようなら一旦ループを終了する
- def deach(&proc)
- iteratee = to_a
- iteratee = dup if equal?(iteratee)
- deferred{
- result = nil
- while not iteratee.empty?
- item = iteratee.shift
- proc.call(item)
- if Delayer.expire?
- break result = iteratee.deach(&proc) end end
- result }
- end
-end
-
-def deferred(&proc)
- Deferred.new.next(&proc) end
-# ~> -:4: uninitialized constant Deferred::Deferredable (NameError)
diff --git a/core/lib/deferred/deferredable.rb b/core/lib/deferred/deferredable.rb
deleted file mode 100644
index 74c0db1f..00000000
--- a/core/lib/deferred/deferredable.rb
+++ /dev/null
@@ -1,138 +0,0 @@
-# -*- 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
diff --git a/core/lib/mikutwitter/query.rb b/core/lib/mikutwitter/query.rb
index 64a53c05..4d1e7d77 100644
--- a/core/lib/mikutwitter/query.rb
+++ b/core/lib/mikutwitter/query.rb
@@ -5,7 +5,7 @@ require "mikutwitter/connect"
require "mikutwitter/utils"
require "mikutwitter/cache"
require "mikutwitter/error"
-require "deferred"
+require "delayer/deferred"
require "monitor"
miquire :lib, "weakstorage"
diff --git a/core/serialthread.rb b/core/serialthread.rb
index 914643bc..a1af4065 100644
--- a/core/serialthread.rb
+++ b/core/serialthread.rb
@@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
require File.expand_path(File.join(File.dirname(__FILE__), 'utils'))
-miquire :lib, 'delayer', 'deferred'
+require 'delayer'
+require 'delayer/deferred'
require 'set'
require 'thread'
require 'timeout'
diff --git a/test/core/lib/test_deferred.rb b/test/core/lib/test_deferred.rb
deleted file mode 100644
index e7fd7c1f..00000000
--- a/test/core/lib/test_deferred.rb
+++ /dev/null
@@ -1,201 +0,0 @@
-# -*- coding: utf-8 -*-
-
-require File.expand_path(File.dirname(__FILE__) + '/../../helper')
-
-Dir.chdir(File.expand_path(File.dirname(__FILE__) + '/../../core'))
-$LOAD_PATH.push '.'
-require 'utils'
-
-miquire :lib, 'test_unit_extensions'
-miquire :lib, 'deferred'
-
-class TC_Deferred < Test::Unit::TestCase
- def setup
- end
-
- def wait
- while !Delayer.empty? || !(Thread.list - [Thread.current]).empty?
- Delayer.run
- (Thread.list - [Thread.current]).each &:join
- end
- end
-
- must "execute serially" do
- ans = 0
- Deferred.new.next{
- 10
- }.next{ |x|
- ans = x + 2
- }
- wait
- assert_equal(12, ans)
- end
-
- must "trap exception" do
- ans = 0
- exception = Exception.new
- Deferred.new.next{
- 10
- }.next{
- raise exception
- }.next{ |x|
- ans = x + 2
- }.trap{ |e|
- ans = e
- }
- wait
- assert_equal(exception, ans)
- end
-
- must "fail manually" do
- ans = 0
- Deferred.new.next{
- 10
- }.next{
- Deferred.fail "mikutan peropero"
- }.next{ |x|
- ans = x + 2
- }.trap{ |e|
- ans = e
- }
- wait
- assert_equal("mikutan peropero", ans)
- end
-
- must "thread be deferredable" do
- ans = 0
- Thread.new{
- 39
- }.next{ |x|
- x + 1
- }.next{ |x|
- ans = x
- }
- wait
- assert_equal(ans, 40)
- end
-
- must "handle exception" do
- ans = 0
- exception = Exception.new
- th = nil
- Thread.new{
- raise exception
- }.tap{ |t|
- th = t
- }.next{ |x|
- ans = x
- }.trap{ |x|
- ans = x
- }
- assert_raise(exception){ th.join }
- wait
- assert_equal(exception, ans)
- end
-
- must "execute in deterministic order" do
- trace = []
- deferred{
- trace << 1
- }.next{ |x|
- trace << 2
- deferred{
- trace << 3
- }.next{
- Thread.new{
- trace << 4
- }.next{
- trace << 5
- raise
- }.trap{
- trace << 6
- }
- }.trap{
- trace << nil
- }
- }
- trace << 0
- wait
- assert_equal([0, 1, 2, 3, 4, 5, 6], trace)
- end
-
- must "extend chain later" do
- a = []
- d = deferred{ a << 1 }
- wait
- d.next{ a << 2 }.next{ a << 3 }
- wait
- assert_equal([1, 2, 3], a)
- end
-
- must "trap error later" do
- ans = nil
- exception = Exception.new
- d = deferred{
- raise exception
- }
- wait
- d.trap{ |e|
- ans = e
- }
- assert_equal(exception, ans)
- end
-
- must "trap error later (thread)" do
- ans = nil
- exception = Exception.new
- th = Thread.new{
- raise exception
- }
- assert_raise(exception){ th.join }
- th.trap{ |e|
- ans = e
- }
- assert_equal(exception, ans)
- end
-
- must "control evaluation order" do
- ans = nil
- Deferred.when(deferred{ 1 }, deferred{ 2 }, deferred{ 3 }).next{ |res|
- ans = res
- }
- wait
- assert_equal([1, 2, 3], ans)
- end
-
- must "cancel execution" do
- ans = 0
- Thread.new{
- sleep(10)
- 39
- }.next{ |x|
- ans = x
- }.cancel
- wait
- assert_equal(0, ans)
- end
-
- must "deach work" do
- ans = 0
- (1..100000).deach{
- ans += 1
- }
- wait
- assert_equal(100000, ans)
- end
-
- must "give true when #system success" do
- ans = 0
- Deferred.system("ruby", "-e", "exit").next{ |v| ans = v }
- wait
- assert_equal(true, ans)
- end
-
- must "give error with exit code when #system fail" do
- ans = 0
- Deferred.system("ruby", "-e", "abort").trap{ |v| ans = v }
- wait
- assert_kind_of(Process::Status, ans)
- assert_equal(256, ans.to_i)
- end
-end
diff --git a/test/core/lib/test_instance_storage.rb b/test/core/lib/test_instance_storage.rb
deleted file mode 100644
index 3c340aff..00000000
--- a/test/core/lib/test_instance_storage.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-# -*- coding: utf-8 -*-
-
-require File.expand_path(File.dirname(__FILE__) + '/../../helper')
-
-Dir.chdir(File.expand_path(File.dirname(__FILE__) + '/../../core'))
-$LOAD_PATH.push '.'
-require 'utils'
-
-miquire :lib, 'test_unit_extensions'
-miquire :lib, 'instance_storage'
-
-class TC_InstanceStorage < Test::Unit::TestCase
- def setup
- end
-
- must "get and create instance" do
- klass = Class.new do
- include InstanceStorage end
- assert_same(klass[:foo], klass[:foo])
- assert_not_same(klass[:foo], klass[:bar])
- end
-
- must "get all instances" do
- klass = Class.new do
- include InstanceStorage end
- assert_equal([], klass.instances)
- assert_equal([klass[:a], klass[:b]], klass.instances)
- end
-
- must "get all instances name" do
- klass = Class.new do
- include InstanceStorage end
- assert_equal([], klass.instances_name)
- klass[:a]
- klass[:b]
- assert_equal([:a, :b], klass.instances_name)
- end
-
- must "destroy instance" do
- klass = Class.new do
- include InstanceStorage end
- klass[:a]
- assert(klass.instance_exist? :a)
- klass.destroy(:a)
- assert(! klass.instance_exist?(:a))
- end
-
- must "get existing instance" do
- klass = Class.new do
- include InstanceStorage end
- assert_nil(klass.instance(:a))
- assert_equal(klass[:a], klass.instance(:a))
- end
-
-end