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 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