delayer-deferred 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|