Delayer::Deferred

Delayerを使って、jsdeferredをRubyに移植したものです。 jsdeferredでできること以外に、Thread、Enumeratorを拡張します。

Installation

Add this line to your application's Gemfile:

gem 'delayer-deferred'

And then execute:

$ bundle

Or install it yourself as:

$ gem install delayer-deferred

Usage

The first step

rubygemでインストールしたあと、requireします。

require "delayer/deferred"

Delayer::Deferred.new が使えるようになります。ブロックを渡すと、Delayerのように後から(Delayer.runが呼ばれた時に)実行されます。

Delayer.default = Delayer.generate_class  # Delayerの準備
Delayer::Deferred.new {
  p "defer"
}
Delayer.run
defer

.next メソッドで、前のブロックの実行が終わったら、その結果を受け取って次を実行することができます。

Delayer.default = Delayer.generate_class  # Delayerの準備
Delayer::Deferred.new {
  1 + 1
}.next{ |sum|
  p sum
}
Delayer.run
2

Error handling

nextブロックの中で例外が発生した場合、次のtrapブロックまで処理が飛ばされます。 trapブロックは、その例外オブジェクトを引数として受け取ります。

Delayer.default = Delayer.generate_class  # Delayerの準備
Delayer::Deferred.new {
  1 / 0
}.next{ |sum|
  p sum
}.trap{ |exception|
  puts "Error occured!"
  p exception
}
Delayer.run
Error occured!
\#<ZeroDivisionError: divided by 0>

例外が発生すると、以降のnextブロックは無視され、例外が起こったブロック以降の最初のtrapブロックが実行されます。trapブロックの後にnextブロックがあればそれが実行されます。

Delayer::Deferred.fail() を使えば、例外以外のオブジェクトをtrapの引数に渡すこともできます。

Delayer.default = Delayer.generate_class  # Delayerの準備
Delayer::Deferred.new {
  Delayer::Deferred.fail("test error message")
}.trap{ |exception|
  puts "Error occured!"
  p exception
}
Delayer.run
Error occured!
"test error message"

Thread

Threadには、Delayer::Deferred::Deferredableモジュールがincludeされていて、nextやtrapメソッドが使えます。

Delayer.default = Delayer.generate_class  # Delayerの準備
Thread.new {
  1 + 1
}.next{ |sum|
  p sum
}
Delayer.run
2

Automatically Divide a Long Loop

Enumerable#deach, Enumerator#deachはeachの変種で、Delayerのexpireの値よりループに時間がかかったら一旦処理を中断して、続きを実行するDeferredを新たに作ります。

complete = false
Delayer.default = Delayer.generate_class(expire: 0.1)  # Delayerの準備
(1..100000).deach{ |digit|
  p digit
}.next{
  puts "complete"
  complete = true
}.trap{ |exception|
  p exception
  complete = true
}
while !complete
  Delayer.run
  puts "divided"
end
1
2
3
(中略)
25398
divided
25399
(中略)
100000
complete
divided

開発している環境では、25398までループした後、0.1秒経過したので一度処理が分断され、Delayer.runから処理が帰ってきています。

また、このメソッドはDeferredを返すので、ループが終わった後に処理をしたり、エラーを受け取ったりできます。

Contributing

  1. Fork it ( https://github.com/toshia/delayer-deferred/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request