delayer-deferred 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +165 -0
- data/Rakefile +8 -0
- data/delayer-deferred.gemspec +28 -0
- data/lib/delayer/deferred/deferred.rb +48 -0
- data/lib/delayer/deferred/deferredable.rb +98 -0
- data/lib/delayer/deferred/enumerable.rb +8 -0
- data/lib/delayer/deferred/enumerator.rb +14 -0
- data/lib/delayer/deferred/thread.rb +32 -0
- data/lib/delayer/deferred/version.rb +5 -0
- data/lib/delayer/deferred.rb +69 -0
- data/test/deferred_test.rb +192 -0
- data/test/enumerable_test.rb +58 -0
- data/test/helper.rb +9 -0
- data/test/testutils.rb +11 -0
- data/test/thread_test.rb +99 -0
- metadata +129 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8b050c45092f429e5eb1339abad226b591ede358
|
4
|
+
data.tar.gz: 52c8ce152d6bcb427415b76a37c8f56f35d0a2e2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d77fd8ccdd4de1581d7ac44f4fd0a66be29f21b4ac0d1f306e101a4c5c211187f7a92cd27400d4c006e90b6794cac57ab3d6a6ce542bdca01ed8c05cca8a85d7
|
7
|
+
data.tar.gz: 24b8fa4b0af898330feccb4d0aafae3d717fab27be638b0d015e88a257c1877d03eb5ddd28ae9ecbdfab6a9c0e41facab7b669b4d7807ad0b0f3cd445cb993ad
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Toshiaki Asai
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
# Delayer::Deferred
|
2
|
+
|
3
|
+
Delayerを使って、jsdeferredをRubyに移植したものです。
|
4
|
+
jsdeferredでできること以外に、Thread、Enumeratorを拡張します。
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'delayer-deferred'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install delayer-deferred
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
### The first step
|
24
|
+
rubygemでインストールしたあと、requireします。
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
require "delayer/deferred"
|
28
|
+
```
|
29
|
+
|
30
|
+
`Delayer::Deferred.new` が使えるようになります。ブロックを渡すと、Delayerのように後から(Delayer.runが呼ばれた時に)実行されます。
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
Delayer.default = Delayer.generate_class # Delayerの準備
|
34
|
+
Delayer::Deferred.new {
|
35
|
+
p "defer"
|
36
|
+
}
|
37
|
+
Delayer.run
|
38
|
+
```
|
39
|
+
|
40
|
+
```
|
41
|
+
defer
|
42
|
+
```
|
43
|
+
|
44
|
+
`.next` メソッドで、前のブロックの実行が終わったら、その結果を受け取って次を実行することができます。
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
Delayer.default = Delayer.generate_class # Delayerの準備
|
48
|
+
Delayer::Deferred.new {
|
49
|
+
1 + 1
|
50
|
+
}.next{ |sum|
|
51
|
+
p sum
|
52
|
+
}
|
53
|
+
Delayer.run
|
54
|
+
```
|
55
|
+
|
56
|
+
```
|
57
|
+
2
|
58
|
+
```
|
59
|
+
|
60
|
+
### Error handling
|
61
|
+
|
62
|
+
nextブロックの中で例外が発生した場合、次のtrapブロックまで処理が飛ばされます。
|
63
|
+
trapブロックは、その例外オブジェクトを引数として受け取ります。
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
Delayer.default = Delayer.generate_class # Delayerの準備
|
67
|
+
Delayer::Deferred.new {
|
68
|
+
1 / 0
|
69
|
+
}.next{ |sum|
|
70
|
+
p sum
|
71
|
+
}.trap{ |exception|
|
72
|
+
puts "Error occured!"
|
73
|
+
p exception
|
74
|
+
}
|
75
|
+
Delayer.run
|
76
|
+
```
|
77
|
+
|
78
|
+
```
|
79
|
+
Error occured!
|
80
|
+
\#<ZeroDivisionError: divided by 0>
|
81
|
+
```
|
82
|
+
|
83
|
+
例外が発生すると、以降のnextブロックは無視され、例外が起こったブロック以降の最初のtrapブロックが実行されます。trapブロックの後にnextブロックがあればそれが実行されます。
|
84
|
+
|
85
|
+
`Delayer::Deferred.fail()` を使えば、例外以外のオブジェクトをtrapの引数に渡すこともできます。
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
Delayer.default = Delayer.generate_class # Delayerの準備
|
89
|
+
Delayer::Deferred.new {
|
90
|
+
Delayer::Deferred.fail("test error message")
|
91
|
+
}.trap{ |exception|
|
92
|
+
puts "Error occured!"
|
93
|
+
p exception
|
94
|
+
}
|
95
|
+
Delayer.run
|
96
|
+
```
|
97
|
+
|
98
|
+
```
|
99
|
+
Error occured!
|
100
|
+
"test error message"
|
101
|
+
```
|
102
|
+
|
103
|
+
### Thread
|
104
|
+
Threadには、Delayer::Deferred::Deferredableモジュールがincludeされていて、nextやtrapメソッドが使えます。
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
Delayer.default = Delayer.generate_class # Delayerの準備
|
108
|
+
Thread.new {
|
109
|
+
1 + 1
|
110
|
+
}.next{ |sum|
|
111
|
+
p sum
|
112
|
+
}
|
113
|
+
Delayer.run
|
114
|
+
```
|
115
|
+
|
116
|
+
```
|
117
|
+
2
|
118
|
+
```
|
119
|
+
|
120
|
+
### Automatically Divide a Long Loop
|
121
|
+
`Enumerable#deach`, `Enumerator#deach`はeachの変種で、Delayerのexpireの値よりループに時間がかかったら一旦処理を中断して、続きを実行するDeferredを新たに作ります。
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
complete = false
|
125
|
+
Delayer.default = Delayer.generate_class(expire: 0.1) # Delayerの準備
|
126
|
+
(1..100000).deach{ |digit|
|
127
|
+
p digit
|
128
|
+
}.next{
|
129
|
+
puts "complete"
|
130
|
+
complete = true
|
131
|
+
}.trap{ |exception|
|
132
|
+
p exception
|
133
|
+
complete = true
|
134
|
+
}
|
135
|
+
while !complete
|
136
|
+
Delayer.run
|
137
|
+
puts "divided"
|
138
|
+
end
|
139
|
+
```
|
140
|
+
|
141
|
+
```
|
142
|
+
1
|
143
|
+
2
|
144
|
+
3
|
145
|
+
(中略)
|
146
|
+
25398
|
147
|
+
divided
|
148
|
+
25399
|
149
|
+
(中略)
|
150
|
+
100000
|
151
|
+
complete
|
152
|
+
divided
|
153
|
+
```
|
154
|
+
|
155
|
+
開発している環境では、25398までループした後、0.1秒経過したので一度処理が分断され、Delayer.runから処理が帰ってきています。
|
156
|
+
|
157
|
+
また、このメソッドはDeferredを返すので、ループが終わった後に処理をしたり、エラーを受け取ったりできます。
|
158
|
+
|
159
|
+
## Contributing
|
160
|
+
|
161
|
+
1. Fork it ( https://github.com/toshia/delayer-deferred/fork )
|
162
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
163
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
164
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
165
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'delayer/deferred/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "delayer-deferred"
|
8
|
+
spec.version = Delayer::Deferred::VERSION
|
9
|
+
spec.authors = ["Toshiaki Asai"]
|
10
|
+
spec.email = ["toshi.alternative@gmail.com"]
|
11
|
+
spec.summary = %q{Deferred for Delayer}
|
12
|
+
spec.description = %q{Deferred for Delayer.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.required_ruby_version = '2.0.0'
|
22
|
+
|
23
|
+
spec.add_dependency "delayer", ">= 0.0.2", "< 0.1"
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
26
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
27
|
+
spec.add_development_dependency "minitest", "~> 5.7"
|
28
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module Delayer::Deferred
|
3
|
+
class Deferred
|
4
|
+
include Deferredable
|
5
|
+
|
6
|
+
def self.inherited(subclass)
|
7
|
+
subclass.extend(::Delayer::Deferred)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.Thread
|
11
|
+
@thread_class ||= gen_thread_class end
|
12
|
+
|
13
|
+
def self.gen_thread_class
|
14
|
+
the_delayer = delayer
|
15
|
+
Class.new(Thread) do
|
16
|
+
define_singleton_method(:delayer) do
|
17
|
+
the_delayer end end end
|
18
|
+
|
19
|
+
def self.delayer
|
20
|
+
::Delayer end
|
21
|
+
|
22
|
+
def self.new(*args)
|
23
|
+
deferred = super(*args)
|
24
|
+
if block_given?
|
25
|
+
deferred.next(&Proc.new)
|
26
|
+
else
|
27
|
+
deferred end
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(follow = nil)
|
31
|
+
super()
|
32
|
+
@follow = follow
|
33
|
+
@backtrace = caller if ::Delayer::Deferred.debug end
|
34
|
+
|
35
|
+
alias :deferredable_cancel :cancel
|
36
|
+
def cancel
|
37
|
+
deferredable_cancel
|
38
|
+
@follow.cancel if @follow.is_a? Deferredable end
|
39
|
+
|
40
|
+
def inspect
|
41
|
+
if ::Delayer::Deferred.debug
|
42
|
+
sprintf("#<%s: %p %s follow:%p stat:%s value:%s>".freeze, self.class, object_id, @backtrace.find{|n|not n.include?("delayer/deferred".freeze)}, @follow ? @follow.object_id : 0, @next_call_stat.inspect, @next_call_value.inspect)
|
43
|
+
else
|
44
|
+
sprintf("#<%s: %p stat:%s value:%s>".freeze, self.class, object_id, @next_call_stat.inspect, @next_call_value.inspect)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
# なんでもDeferred
|
4
|
+
module Delayer::Deferred::Deferredable
|
5
|
+
Callback = Struct.new(*%i<ok ng backtrace>)
|
6
|
+
BackTrace = Struct.new(*%i<ok ng>)
|
7
|
+
CallbackDefaultOK = lambda{ |x| x }
|
8
|
+
CallbackDefaultNG = lambda{ |err| Delayer::Deferred.fail(err) }
|
9
|
+
|
10
|
+
# このDeferredが成功した場合の処理を追加する。
|
11
|
+
# 新しいDeferredのインスタンスを返す
|
12
|
+
def next(&proc)
|
13
|
+
_post(:ok, &proc) end
|
14
|
+
alias deferred next
|
15
|
+
|
16
|
+
# このDeferredが失敗した場合の処理を追加する。
|
17
|
+
# 新しいDeferredのインスタンスを返す
|
18
|
+
def trap(&proc)
|
19
|
+
_post(:ng, &proc) end
|
20
|
+
alias error trap
|
21
|
+
|
22
|
+
# Deferredを直ちに実行する
|
23
|
+
def call(value = nil)
|
24
|
+
_call(:ok, value) end
|
25
|
+
|
26
|
+
# Deferredを直ちに失敗させる
|
27
|
+
def fail(exception = nil)
|
28
|
+
_call(:ng, exception) end
|
29
|
+
|
30
|
+
# この一連のDeferredをこれ以上実行しない
|
31
|
+
def cancel
|
32
|
+
@callback = Callback.new(CallbackDefaultOK,
|
33
|
+
CallbackDefaultNG,
|
34
|
+
BackTrace.new(nil, nil).freeze).freeze end
|
35
|
+
|
36
|
+
def callback
|
37
|
+
@callback ||= Callback.new(CallbackDefaultOK,
|
38
|
+
CallbackDefaultNG,
|
39
|
+
BackTrace.new(nil, nil)) end
|
40
|
+
|
41
|
+
# second 秒待って次を実行する
|
42
|
+
# ==== Args
|
43
|
+
# [second] 待つ秒数(second)
|
44
|
+
# ==== Return
|
45
|
+
# Deferred
|
46
|
+
def wait(second)
|
47
|
+
self.next{ Thread.new{ sleep(second) } } end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def delayer
|
52
|
+
self.class.delayer
|
53
|
+
end
|
54
|
+
|
55
|
+
def _call(stat = :ok, value = nil)
|
56
|
+
begin
|
57
|
+
catch(:__deferredable_success) do
|
58
|
+
failed = catch(:__deferredable_fail) do
|
59
|
+
n_value = _execute(stat, value)
|
60
|
+
if n_value.is_a? Delayer::Deferred::Deferredable
|
61
|
+
n_value.next{ |result|
|
62
|
+
@next.call(result)
|
63
|
+
}.trap{ |exception|
|
64
|
+
@next.fail(exception) }
|
65
|
+
else
|
66
|
+
if defined?(@next)
|
67
|
+
delayer.new{ @next.call(n_value) }
|
68
|
+
else
|
69
|
+
register_next_call(:ok, n_value) end end
|
70
|
+
throw :__deferredable_success end
|
71
|
+
_fail_action(failed) end
|
72
|
+
rescue Exception => exception
|
73
|
+
_fail_action(exception) end end
|
74
|
+
|
75
|
+
def _execute(stat, value)
|
76
|
+
callback[stat].call(value) end
|
77
|
+
|
78
|
+
def _post(kind, &proc)
|
79
|
+
@next = delayer.Deferred.new(self)
|
80
|
+
@next.callback[kind] = proc
|
81
|
+
@next.callback.backtrace[kind] = caller(1)
|
82
|
+
if defined?(@next_call_stat) and defined?(@next_call_value)
|
83
|
+
@next.__send__({ok: :call, ng: :fail}[@next_call_stat], @next_call_value)
|
84
|
+
elsif defined?(@follow) and @follow.nil?
|
85
|
+
call end
|
86
|
+
@next end
|
87
|
+
|
88
|
+
def register_next_call(stat, value)
|
89
|
+
@next_call_stat, @next_call_value = stat, value
|
90
|
+
self end
|
91
|
+
|
92
|
+
def _fail_action(err_obj)
|
93
|
+
if defined?(@next)
|
94
|
+
delayer.new{ @next.fail(err_obj) }
|
95
|
+
else
|
96
|
+
register_next_call(:ng, err_obj) end end
|
97
|
+
|
98
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
class Enumerator
|
4
|
+
def deach(delayer=Delayer, &proc)
|
5
|
+
delayer.Deferred.new.next do
|
6
|
+
begin
|
7
|
+
loop do
|
8
|
+
proc.call(self.next())
|
9
|
+
if delayer.expire?
|
10
|
+
break deach(delayer, &proc) end end
|
11
|
+
rescue StopIteration
|
12
|
+
nil end end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
class Thread
|
3
|
+
include ::Delayer::Deferred::Deferredable
|
4
|
+
|
5
|
+
def self.delayer
|
6
|
+
Delayer
|
7
|
+
end
|
8
|
+
|
9
|
+
alias _deferredable_trap initialize
|
10
|
+
def initialize(*args, &proc)
|
11
|
+
_deferredable_trap(*args, &_deferredable_trap_proc(&proc)) end
|
12
|
+
|
13
|
+
alias :deferredable_cancel :cancel
|
14
|
+
def cancel
|
15
|
+
deferredable_cancel
|
16
|
+
kill end
|
17
|
+
|
18
|
+
private
|
19
|
+
def _deferredable_trap_proc
|
20
|
+
proc = Proc.new
|
21
|
+
->(*args) do
|
22
|
+
catch(:__deferredable_success) do
|
23
|
+
failed = catch(:__deferredable_fail) do
|
24
|
+
begin
|
25
|
+
result = proc.call(*args)
|
26
|
+
self.call(result)
|
27
|
+
result
|
28
|
+
rescue Exception => exception
|
29
|
+
self.fail(exception) end
|
30
|
+
throw :__deferredable_success end
|
31
|
+
self.fail(failed) end end end
|
32
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require "delayer"
|
3
|
+
require "delayer/deferred/version"
|
4
|
+
require "delayer/deferred/deferredable"
|
5
|
+
require "delayer/deferred/deferred"
|
6
|
+
require "delayer/deferred/thread"
|
7
|
+
require "delayer/deferred/enumerator"
|
8
|
+
require "delayer/deferred/enumerable"
|
9
|
+
|
10
|
+
module Delayer
|
11
|
+
module Deferred
|
12
|
+
extend self
|
13
|
+
|
14
|
+
#真ならデバッグ情報を集める
|
15
|
+
attr_accessor :debug
|
16
|
+
|
17
|
+
# 実行中のDeferredを失敗させる。raiseと違って、Exception以外のオブジェクトをtrap()に渡すことができる。
|
18
|
+
# Deferredのnextとtrapの中でだけ呼び出すことができる。
|
19
|
+
# ==== Args
|
20
|
+
# [value] trap()に渡す値
|
21
|
+
# ==== Throw
|
22
|
+
# :__deferredable_fail をthrowする
|
23
|
+
def fail(value)
|
24
|
+
throw(:__deferredable_fail, value) end
|
25
|
+
|
26
|
+
# 複数のdeferredを引数に取って、それら全ての実行が終了したら、
|
27
|
+
# その結果を引数の順番通りに格納したArrayを引数に呼ばれるDeferredを返す。
|
28
|
+
# 引数のDeferredが一つでも失敗するとこのメソッドの返すDeferredも失敗する。
|
29
|
+
# ==== Args
|
30
|
+
# [defer] 終了を待つDeferredオブジェクト
|
31
|
+
# [*follow] 他のDeferredオブジェクト
|
32
|
+
# ==== Return
|
33
|
+
# Deferred
|
34
|
+
def when(defer, *follow)
|
35
|
+
raise TypeError, "Argument of Deferred.when must be Delayer::Deferred::Deferredable" unless defer.is_a? Delayer::Deferred::Deferredable
|
36
|
+
if follow.empty?
|
37
|
+
defer.next{|res| [res] }
|
38
|
+
else
|
39
|
+
remain = self.when(*follow)
|
40
|
+
defer.next do |res|
|
41
|
+
remain.next do |follow_res|
|
42
|
+
follow_res.unshift(res) end end end end
|
43
|
+
|
44
|
+
# Kernel#systemを呼び出して、コマンドが成功たら成功するDeferredを返す。
|
45
|
+
# 失敗した場合、trap{}ブロックには $? の値(Process::Status)か、例外が発生した場合それが渡される
|
46
|
+
# ==== Args
|
47
|
+
# [*args] Kernel#system の引数
|
48
|
+
# ==== Return
|
49
|
+
# Deferred
|
50
|
+
def system(*args)
|
51
|
+
delayer.Deferred.Thread.new do
|
52
|
+
if Kernel.system(*args)
|
53
|
+
$?
|
54
|
+
else
|
55
|
+
delayer.Deferred.fail($?) end end end
|
56
|
+
end
|
57
|
+
|
58
|
+
module Extend
|
59
|
+
def Deferred
|
60
|
+
@deferred ||= begin
|
61
|
+
the_delayer = self
|
62
|
+
Class.new(::Delayer::Deferred::Deferred) {
|
63
|
+
define_singleton_method(:delayer) {
|
64
|
+
the_delayer } } end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
Delayer::Deferred.debug = false
|
@@ -0,0 +1,192 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
|
5
|
+
describe(Delayer::Deferred) do
|
6
|
+
include TestUtils
|
7
|
+
|
8
|
+
before do
|
9
|
+
Delayer.default = Delayer.generate_class
|
10
|
+
end
|
11
|
+
|
12
|
+
it "defer with Deferred#next" do
|
13
|
+
succeed = failure = false
|
14
|
+
eval_all_events do
|
15
|
+
Delayer::Deferred::Deferred.new.next{
|
16
|
+
succeed = true
|
17
|
+
}.trap{ |exception|
|
18
|
+
failure = exception } end
|
19
|
+
assert_equal false, failure
|
20
|
+
assert succeed, "Deferred did not executed."
|
21
|
+
end
|
22
|
+
|
23
|
+
it "defer with another Delayer" do
|
24
|
+
succeed = failure = false
|
25
|
+
delayer = Delayer.generate_class
|
26
|
+
eval_all_events(delayer) do
|
27
|
+
delayer.Deferred.new.next {
|
28
|
+
succeed = true
|
29
|
+
}.trap{ |exception|
|
30
|
+
failure = exception } end
|
31
|
+
assert_equal false, failure
|
32
|
+
assert succeed, "Deferred did not executed."
|
33
|
+
end
|
34
|
+
|
35
|
+
it "error handling" do
|
36
|
+
succeed = failure = recover = false
|
37
|
+
uuid = SecureRandom.uuid
|
38
|
+
eval_all_events do
|
39
|
+
Delayer::Deferred::Deferred.new.next {
|
40
|
+
Delayer::Deferred.fail(uuid)
|
41
|
+
}.next {
|
42
|
+
succeed = true
|
43
|
+
}.trap { |value|
|
44
|
+
failure = value
|
45
|
+
}.next {
|
46
|
+
recover = true } end
|
47
|
+
refute succeed, "Raised exception but it was executed successed route."
|
48
|
+
assert_equal uuid, failure, "trap block takes incorrect value"
|
49
|
+
assert recover, "next block did not executed when after trap"
|
50
|
+
end
|
51
|
+
|
52
|
+
it "exception handling" do
|
53
|
+
succeed = failure = recover = false
|
54
|
+
eval_all_events do
|
55
|
+
Delayer::Deferred::Deferred.new.next {
|
56
|
+
raise 'error test'
|
57
|
+
}.next {
|
58
|
+
succeed = true
|
59
|
+
}.trap {
|
60
|
+
failure = true
|
61
|
+
}.next {
|
62
|
+
recover = true } end
|
63
|
+
refute succeed, "Raised exception but it was executed successed route."
|
64
|
+
assert failure, "trap block did not executed"
|
65
|
+
assert recover, "next block did not executed when after trap"
|
66
|
+
end
|
67
|
+
|
68
|
+
it "wait end of Deferredable if Deferredable block returns Deferredable" do
|
69
|
+
result = failure = false
|
70
|
+
delayer = Delayer.generate_class
|
71
|
+
uuid = SecureRandom.uuid
|
72
|
+
eval_all_events(delayer) do
|
73
|
+
delayer.Deferred.new.next{
|
74
|
+
delayer.Deferred.new.next{
|
75
|
+
uuid }
|
76
|
+
}.next{ |value|
|
77
|
+
result = value
|
78
|
+
}.trap{ |exception|
|
79
|
+
failure = exception }
|
80
|
+
end
|
81
|
+
assert_equal uuid, result
|
82
|
+
assert_equal false, failure
|
83
|
+
end
|
84
|
+
|
85
|
+
it "join Deferredable#next after end of previous Deferredable" do
|
86
|
+
succeed = failure = false
|
87
|
+
delayer = Delayer.generate_class
|
88
|
+
deferredable = eval_all_events(delayer) do
|
89
|
+
delayer.Deferred.new.next {
|
90
|
+
true
|
91
|
+
} end
|
92
|
+
eval_all_events(delayer) do
|
93
|
+
deferredable.next{ |value|
|
94
|
+
succeed = value
|
95
|
+
}.trap{ |exception|
|
96
|
+
failure = exception } end
|
97
|
+
assert_equal false, failure
|
98
|
+
assert succeed, "Deferred did not executed."
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "Deferred.when" do
|
102
|
+
it "give 3 deferred" do
|
103
|
+
result = failure = false
|
104
|
+
delayer = Delayer.generate_class
|
105
|
+
eval_all_events(delayer) do
|
106
|
+
delayer.Deferred.when(
|
107
|
+
delayer.Deferred.new.next{ 1 },
|
108
|
+
delayer.Deferred.new.next{ 2 },
|
109
|
+
delayer.Deferred.new.next{ 3 }
|
110
|
+
).next{ |values|
|
111
|
+
result = values
|
112
|
+
}.trap{ |exception|
|
113
|
+
failure = exception } end
|
114
|
+
assert_equal false, failure
|
115
|
+
assert_equal [1,2,3], result
|
116
|
+
end
|
117
|
+
|
118
|
+
it "give that is not Deferredable" do
|
119
|
+
result = failure = false
|
120
|
+
delayer = Delayer.generate_class
|
121
|
+
assert_raises(TypeError) do
|
122
|
+
eval_all_events(delayer) do
|
123
|
+
delayer.Deferred.when(
|
124
|
+
delayer.Deferred.new.next{ 1 },
|
125
|
+
2,
|
126
|
+
delayer.Deferred.new.next{ 3 }
|
127
|
+
).next{ |values|
|
128
|
+
result = values
|
129
|
+
}.trap{ |exception|
|
130
|
+
failure = exception } end end
|
131
|
+
assert_equal false, failure
|
132
|
+
assert_equal false, result
|
133
|
+
end
|
134
|
+
|
135
|
+
it "execute trap block if failed" do
|
136
|
+
result = failure = false
|
137
|
+
delayer = Delayer.generate_class
|
138
|
+
eval_all_events(delayer) do
|
139
|
+
delayer.Deferred.when(
|
140
|
+
delayer.Deferred.new.next{ 1 },
|
141
|
+
delayer.Deferred.new.next{ raise },
|
142
|
+
delayer.Deferred.new.next{ 3 }
|
143
|
+
).next{ |values|
|
144
|
+
result = values
|
145
|
+
}.trap{ |exception|
|
146
|
+
failure = exception } end
|
147
|
+
assert_kind_of RuntimeError, failure
|
148
|
+
assert_equal false, result
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe "cancel" do
|
153
|
+
it "stops deferred chain" do
|
154
|
+
succeed = failure = false
|
155
|
+
delayer = Delayer.generate_class
|
156
|
+
eval_all_events(delayer) do
|
157
|
+
delayer.Deferred.new.next {
|
158
|
+
succeed = true
|
159
|
+
}.trap{ |exception|
|
160
|
+
failure = exception }.cancel end
|
161
|
+
assert_equal false, failure
|
162
|
+
assert_equal false, succeed, "Deferred executed."
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe "Deferredable#system" do
|
167
|
+
it "command successed" do
|
168
|
+
succeed = failure = false
|
169
|
+
delayer = Delayer.generate_class
|
170
|
+
eval_all_events(delayer) do
|
171
|
+
delayer.Deferred.system("ruby", "-e", "exit 0").next{ |value|
|
172
|
+
succeed = value
|
173
|
+
}.trap{ |exception|
|
174
|
+
failure = exception } end
|
175
|
+
assert_equal false, failure
|
176
|
+
assert succeed, "next block called"
|
177
|
+
end
|
178
|
+
|
179
|
+
it "command failed" do
|
180
|
+
succeed = failure = false
|
181
|
+
delayer = Delayer.generate_class
|
182
|
+
eval_all_events(delayer) do
|
183
|
+
delayer.Deferred.system("ruby", "-e", "exit 1").next{ |value|
|
184
|
+
succeed = value
|
185
|
+
}.trap{ |exception|
|
186
|
+
failure = exception } end
|
187
|
+
refute succeed, "next block did not called"
|
188
|
+
assert failure.exited?, "command exited"
|
189
|
+
assert_equal 1, failure.exitstatus, "command exit status is 1"
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
|
5
|
+
describe(Enumerable) do
|
6
|
+
include TestUtils
|
7
|
+
|
8
|
+
before do
|
9
|
+
@delayer = Delayer.generate_class
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "deach" do
|
13
|
+
it "iterate Array" do
|
14
|
+
sum = 0
|
15
|
+
eval_all_events(@delayer) do
|
16
|
+
(1..10000).to_a.deach(@delayer) do |digit|
|
17
|
+
sum += digit
|
18
|
+
end
|
19
|
+
end
|
20
|
+
assert_equal 50005000, sum
|
21
|
+
end
|
22
|
+
|
23
|
+
it "iterate infinite Enumerator" do
|
24
|
+
log = []
|
25
|
+
finish = failure = nil
|
26
|
+
@delayer = Delayer.generate_class(expire: 0.1)
|
27
|
+
fib = Enumerator.new do |yielder|
|
28
|
+
a = 1
|
29
|
+
b = 1
|
30
|
+
loop do
|
31
|
+
c = a + b
|
32
|
+
yielder << a
|
33
|
+
a, b = b, c end end
|
34
|
+
timeout(1) {
|
35
|
+
fib.deach(@delayer) {|digit|
|
36
|
+
log << digit
|
37
|
+
}.next{
|
38
|
+
finish = true
|
39
|
+
}.trap {|exception|
|
40
|
+
failure = exception
|
41
|
+
}
|
42
|
+
@delayer.run
|
43
|
+
}
|
44
|
+
refute failure
|
45
|
+
refute finish, "Enumerable#deach won't call next block"
|
46
|
+
refute log.empty?, "Executed deach block"
|
47
|
+
log_size = log.size
|
48
|
+
sample_size = [156, log_size].min
|
49
|
+
assert_equal fib.take(sample_size), log.take(sample_size), "deach block takes collect arguments"
|
50
|
+
@delayer.run
|
51
|
+
refute failure
|
52
|
+
refute finish, "Enumerable#deach won't call next block"
|
53
|
+
assert log.size > log_size, "Restart iteration if call Delayer#run (first #{log_size} iterations, second #{log.size})"
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
data/test/helper.rb
ADDED
data/test/testutils.rb
ADDED
data/test/thread_test.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
|
5
|
+
describe(Thread) do
|
6
|
+
include TestUtils
|
7
|
+
|
8
|
+
before do
|
9
|
+
Delayer.default = Delayer.generate_class
|
10
|
+
end
|
11
|
+
|
12
|
+
it "defer with Deferred#next" do
|
13
|
+
thread = succeed = result = false
|
14
|
+
uuid = SecureRandom.uuid
|
15
|
+
eval_all_events do
|
16
|
+
Thread.new {
|
17
|
+
thread = true
|
18
|
+
uuid
|
19
|
+
}.next do |param|
|
20
|
+
succeed = true
|
21
|
+
result = param
|
22
|
+
end end
|
23
|
+
assert thread, "Thread did not executed."
|
24
|
+
assert succeed, "next block did not executed."
|
25
|
+
assert_equal uuid, result
|
26
|
+
end
|
27
|
+
|
28
|
+
it "defer with another Delayer" do
|
29
|
+
thread = succeed = failure = result = false
|
30
|
+
uuid = SecureRandom.uuid
|
31
|
+
delayer = Delayer.generate_class
|
32
|
+
eval_all_events(delayer) do
|
33
|
+
delayer.Deferred.Thread.new {
|
34
|
+
thread = true
|
35
|
+
uuid
|
36
|
+
}.next{ |param|
|
37
|
+
succeed = true
|
38
|
+
result = param
|
39
|
+
}.trap{ |exception|
|
40
|
+
failure = exception } end
|
41
|
+
assert_equal false, failure
|
42
|
+
assert thread, "Thread did not executed."
|
43
|
+
assert succeed, "next block did not executed."
|
44
|
+
assert_equal uuid, result
|
45
|
+
end
|
46
|
+
|
47
|
+
it "error handling" do
|
48
|
+
delayer = Delayer.generate_class
|
49
|
+
succeed = failure = recover = false
|
50
|
+
uuid = SecureRandom.uuid
|
51
|
+
eval_all_events(delayer) do
|
52
|
+
delayer.Deferred.Thread.new {
|
53
|
+
Delayer::Deferred.fail(uuid)
|
54
|
+
}.next {
|
55
|
+
succeed = true
|
56
|
+
}.trap { |value|
|
57
|
+
failure = value
|
58
|
+
}.next {
|
59
|
+
recover = true } end
|
60
|
+
refute succeed, "Raised exception but it was executed successed route."
|
61
|
+
assert_equal uuid, failure, "trap block takes incorrect value"
|
62
|
+
assert recover, "next block did not executed when after trap"
|
63
|
+
end
|
64
|
+
|
65
|
+
it "exception handling" do
|
66
|
+
succeed = failure = recover = false
|
67
|
+
delayer = Delayer.generate_class
|
68
|
+
eval_all_events(delayer) do
|
69
|
+
delayer.Deferred.Thread.new {
|
70
|
+
raise 'error test'
|
71
|
+
}.next {
|
72
|
+
succeed = true
|
73
|
+
}.trap {
|
74
|
+
failure = true
|
75
|
+
}.next {
|
76
|
+
recover = true } end
|
77
|
+
refute succeed, "Raised exception but it was executed successed route."
|
78
|
+
assert failure, "trap block did not executed"
|
79
|
+
assert recover, "next block did not executed when after trap"
|
80
|
+
end
|
81
|
+
|
82
|
+
it "wait end of Deferredable if Deferredable block returns Thread" do
|
83
|
+
result = failure = false
|
84
|
+
delayer = Delayer.generate_class
|
85
|
+
uuid = SecureRandom.uuid
|
86
|
+
eval_all_events(delayer) do
|
87
|
+
delayer.Deferred.new.next{
|
88
|
+
delayer.Deferred.Thread.new{
|
89
|
+
uuid }
|
90
|
+
}.next{ |value|
|
91
|
+
result = value
|
92
|
+
}.trap{ |exception|
|
93
|
+
failure = exception }
|
94
|
+
end
|
95
|
+
assert_equal uuid, result
|
96
|
+
assert_equal false, failure
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: delayer-deferred
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Toshiaki Asai
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-08-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: delayer
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.0.2
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0.1'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.0.2
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0.1'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: bundler
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '1.7'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '1.7'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '10.0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '10.0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: minitest
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '5.7'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '5.7'
|
75
|
+
description: Deferred for Delayer.
|
76
|
+
email:
|
77
|
+
- toshi.alternative@gmail.com
|
78
|
+
executables: []
|
79
|
+
extensions: []
|
80
|
+
extra_rdoc_files: []
|
81
|
+
files:
|
82
|
+
- ".gitignore"
|
83
|
+
- Gemfile
|
84
|
+
- LICENSE.txt
|
85
|
+
- README.md
|
86
|
+
- Rakefile
|
87
|
+
- delayer-deferred.gemspec
|
88
|
+
- lib/delayer/deferred.rb
|
89
|
+
- lib/delayer/deferred/deferred.rb
|
90
|
+
- lib/delayer/deferred/deferredable.rb
|
91
|
+
- lib/delayer/deferred/enumerable.rb
|
92
|
+
- lib/delayer/deferred/enumerator.rb
|
93
|
+
- lib/delayer/deferred/thread.rb
|
94
|
+
- lib/delayer/deferred/version.rb
|
95
|
+
- test/deferred_test.rb
|
96
|
+
- test/enumerable_test.rb
|
97
|
+
- test/helper.rb
|
98
|
+
- test/testutils.rb
|
99
|
+
- test/thread_test.rb
|
100
|
+
homepage: ''
|
101
|
+
licenses:
|
102
|
+
- MIT
|
103
|
+
metadata: {}
|
104
|
+
post_install_message:
|
105
|
+
rdoc_options: []
|
106
|
+
require_paths:
|
107
|
+
- lib
|
108
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - '='
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: 2.0.0
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
requirements: []
|
119
|
+
rubyforge_project:
|
120
|
+
rubygems_version: 2.4.5
|
121
|
+
signing_key:
|
122
|
+
specification_version: 4
|
123
|
+
summary: Deferred for Delayer
|
124
|
+
test_files:
|
125
|
+
- test/deferred_test.rb
|
126
|
+
- test/enumerable_test.rb
|
127
|
+
- test/helper.rb
|
128
|
+
- test/testutils.rb
|
129
|
+
- test/thread_test.rb
|