delayer-deferred 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ /vendor/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in delayer-deferred.gemspec
4
+ gemspec
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,8 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.test_files = FileList['test/*_test.rb']
6
+ t.warning = true
7
+ t.verbose = true
8
+ end
@@ -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,8 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Enumerable
4
+ # 遅延each。あとで実行されるし、あんまりループに時間がかかるようなら一旦ループを終了する
5
+ def deach(delayer=Delayer, &proc)
6
+ to_enum.deach(delayer, &proc)
7
+ end
8
+ 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,5 @@
1
+ module Delayer
2
+ module Deferred
3
+ VERSION = "1.0.0"
4
+ end
5
+ 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
@@ -0,0 +1,9 @@
1
+ require 'bundler/setup'
2
+ require 'minitest/autorun'
3
+
4
+ require 'delayer/deferred'
5
+ require 'securerandom'
6
+ require 'set'
7
+ require 'timeout'
8
+
9
+ require_relative 'testutils'
data/test/testutils.rb ADDED
@@ -0,0 +1,11 @@
1
+ module TestUtils
2
+ def eval_all_events(delayer=Delayer)
3
+ native = Thread.list
4
+ result = yield if block_given?
5
+ while not(delayer.empty? and (Thread.list - native).empty?)
6
+ delayer.run
7
+ Thread.pass
8
+ end
9
+ result
10
+ end
11
+ end
@@ -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