zmachine 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/echo_client.rb +71 -0
- data/echo_server.rb +41 -0
- data/lib/zmachine/acceptor.rb +12 -0
- data/lib/zmachine/channel.rb +14 -0
- data/lib/zmachine/connection.rb +186 -0
- data/lib/zmachine/deferrable.rb +207 -0
- data/lib/zmachine/jeromq-0.3.0-SNAPSHOT.jar +0 -0
- data/lib/zmachine/reactor.rb +337 -0
- data/lib/zmachine/tcp_channel.rb +169 -0
- data/lib/zmachine/timers.rb +61 -0
- data/lib/zmachine/zmq_channel.rb +101 -0
- data/lib/zmachine.rb +171 -0
- data/zmachine.gemspec +20 -0
- metadata +102 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Benedikt Böhm
|
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,29 @@
|
|
1
|
+
# Zmachine
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'zmachine'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install zmachine
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/echo_client.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'zmachine'
|
6
|
+
|
7
|
+
java_import org.zeromq.ZMQ
|
8
|
+
java_import org.zeromq.ZMsg
|
9
|
+
java_import org.zeromq.ZFrame
|
10
|
+
|
11
|
+
#set_trace_func proc { |event, file, line, id, binding, classname|
|
12
|
+
# printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
|
13
|
+
#}
|
14
|
+
|
15
|
+
$i = 0
|
16
|
+
|
17
|
+
class ZMQEcho < ZMachine::Connection
|
18
|
+
def connection_completed
|
19
|
+
send_msg
|
20
|
+
end
|
21
|
+
|
22
|
+
def receive_data(data)
|
23
|
+
puts "recv(#{data.to_a.map {|f| String.from_java_bytes(f.data) }.inspect})"
|
24
|
+
send_msg
|
25
|
+
end
|
26
|
+
|
27
|
+
def send_msg
|
28
|
+
msg = ZMsg.new_string_msg($i.to_s)
|
29
|
+
msg.wrap(ZFrame.new("server"))
|
30
|
+
send_data(msg)
|
31
|
+
$i += 1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class TCPEcho < ZMachine::Connection
|
36
|
+
def connection_completed
|
37
|
+
send_data(Time.now.to_s)
|
38
|
+
sleep(0.5)
|
39
|
+
end
|
40
|
+
|
41
|
+
def receive_data(data)
|
42
|
+
puts "receive_data(#{data.inspect})"
|
43
|
+
send_data(Time.now.to_s)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
ZMachine.run {
|
48
|
+
ZMachine.connect("tcp://127.0.0.1:10000", ZMQ::ROUTER, ZMQEcho) do |handler|
|
49
|
+
handler.channel.identity = "client"
|
50
|
+
end
|
51
|
+
#ZMachine.connect("127.0.0.1", 10000, TCPEcho)
|
52
|
+
}
|
53
|
+
|
54
|
+
#ctx = ZContext.new
|
55
|
+
#socket = ctx.create_socket(ZMQ::ROUTER)
|
56
|
+
#socket.connect("tcp://127.0.0.1:10000")
|
57
|
+
#socket.identity = "client".to_java_bytes
|
58
|
+
|
59
|
+
#sleep(1)
|
60
|
+
|
61
|
+
#loop do
|
62
|
+
# msg = ZMsg.new_string_msg($i.to_s)
|
63
|
+
# msg.wrap(ZFrame.new("server"))
|
64
|
+
# msg.java_send(:send, [org.zeromq.ZMQ::Socket], socket)
|
65
|
+
# $i += 1
|
66
|
+
# break if $i > 100
|
67
|
+
# #puts ZMsg.recvMsg(socket).inspect
|
68
|
+
#end
|
69
|
+
|
70
|
+
#socket.close
|
71
|
+
#ctx.destroy
|
data/echo_server.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'zmachine'
|
6
|
+
|
7
|
+
java_import org.zeromq.ZMQ
|
8
|
+
java_import org.zeromq.ZFrame
|
9
|
+
|
10
|
+
#set_trace_func proc { |event, file, line, id, binding, classname|
|
11
|
+
# printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
|
12
|
+
#}
|
13
|
+
|
14
|
+
class EchoServer < ZMachine::Connection
|
15
|
+
def receive_data(msg)
|
16
|
+
origin = msg.unwrap
|
17
|
+
puts "recv(#{msg.to_a.map {|f| String.from_java_bytes(f.data) }.inspect})"
|
18
|
+
msg = ZMsg.new_string_msg("ok")
|
19
|
+
msg.wrap(origin)
|
20
|
+
send_data(msg)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
ZMachine.run do
|
25
|
+
ZMachine.start_server("tcp://*:10000", ZMQ::ROUTER, EchoServer) do |handler|
|
26
|
+
handler.channel.identity = "server"
|
27
|
+
end
|
28
|
+
#ZMachine.start_server("0.0.0.0", 10000, EchoServer)
|
29
|
+
puts "machine running"
|
30
|
+
end
|
31
|
+
|
32
|
+
#ctx = ZContext.new
|
33
|
+
#socket = ctx.create_socket(ZMQ::ROUTER)
|
34
|
+
#socket.bind("tcp://127.0.0.1:10000")
|
35
|
+
#socket.identity = "server".to_java_bytes
|
36
|
+
|
37
|
+
#loop do
|
38
|
+
# puts socket.events.inspect
|
39
|
+
# msg = ZMsg.recvMsg(socket)
|
40
|
+
# puts "recv(#{msg.to_a.map {|f| String.from_java_bytes(f.data) }.inspect})"
|
41
|
+
#end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
module ZMachine
|
2
|
+
class Connection
|
3
|
+
|
4
|
+
attr_accessor :channel
|
5
|
+
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
alias original_method method
|
9
|
+
|
10
|
+
def self.new(channel, *args, &block)
|
11
|
+
allocate.instance_eval do
|
12
|
+
@channel = channel
|
13
|
+
initialize(*args, &block)
|
14
|
+
post_init
|
15
|
+
self
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(*args, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
# callbacks
|
23
|
+
|
24
|
+
def post_init
|
25
|
+
end
|
26
|
+
|
27
|
+
def connection_completed
|
28
|
+
end
|
29
|
+
|
30
|
+
def receive_data(data)
|
31
|
+
end
|
32
|
+
|
33
|
+
def unbind
|
34
|
+
end
|
35
|
+
|
36
|
+
# public API
|
37
|
+
|
38
|
+
def_delegator :@channel, :close_connection
|
39
|
+
|
40
|
+
def close_connection_after_writing
|
41
|
+
close_connection(true)
|
42
|
+
end
|
43
|
+
|
44
|
+
def comm_inactivity_timeout
|
45
|
+
end
|
46
|
+
|
47
|
+
def comm_inactivity_timeout=(value)
|
48
|
+
end
|
49
|
+
|
50
|
+
alias :set_comm_inactivity_timeout :comm_inactivity_timeout=
|
51
|
+
|
52
|
+
def detach
|
53
|
+
_not_implemented
|
54
|
+
end
|
55
|
+
|
56
|
+
def error?
|
57
|
+
errno = ZMachine::report_connection_error_status(@signature)
|
58
|
+
case errno
|
59
|
+
when 0
|
60
|
+
false
|
61
|
+
when -1
|
62
|
+
true
|
63
|
+
else
|
64
|
+
Errno::constants.select do |name|
|
65
|
+
Errno.__send__(:const_get, name)::Errno == errno
|
66
|
+
end.first
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_idle_time
|
71
|
+
_not_implemented
|
72
|
+
end
|
73
|
+
|
74
|
+
def get_peer_cert
|
75
|
+
_not_implemented
|
76
|
+
end
|
77
|
+
|
78
|
+
def get_peername
|
79
|
+
if peer = @channel.peer_name
|
80
|
+
::Socket.pack_sockaddr_in(*peer)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def get_pid
|
85
|
+
_not_implemented
|
86
|
+
end
|
87
|
+
|
88
|
+
def get_proxied_bytes
|
89
|
+
_not_implemented
|
90
|
+
end
|
91
|
+
|
92
|
+
def_delegator :@channel, :get_sock_opt
|
93
|
+
|
94
|
+
def get_sockname
|
95
|
+
if sock_name = @channel.sock_name
|
96
|
+
::Socket.pack_sockaddr_in(*sock_name)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def get_status
|
101
|
+
end
|
102
|
+
|
103
|
+
def notify_readable=(mode)
|
104
|
+
_not_implemented
|
105
|
+
end
|
106
|
+
|
107
|
+
def notify_readable?
|
108
|
+
_not_implemented
|
109
|
+
end
|
110
|
+
|
111
|
+
def notify_writable=(mode)
|
112
|
+
_not_implemented
|
113
|
+
end
|
114
|
+
|
115
|
+
def notify_writable?
|
116
|
+
_not_implemented
|
117
|
+
end
|
118
|
+
|
119
|
+
def pause
|
120
|
+
_not_implemented
|
121
|
+
end
|
122
|
+
|
123
|
+
def paused?
|
124
|
+
_not_implemented
|
125
|
+
end
|
126
|
+
|
127
|
+
def pending_connect_timeout=(value)
|
128
|
+
end
|
129
|
+
|
130
|
+
alias :set_pending_connect_timeout :pending_connect_timeout=
|
131
|
+
|
132
|
+
def proxy_completed
|
133
|
+
_not_implemented
|
134
|
+
end
|
135
|
+
|
136
|
+
def proxy_incoming_to(conn, bufsize = 0)
|
137
|
+
_not_implemented
|
138
|
+
end
|
139
|
+
|
140
|
+
def proxy_target_unbound
|
141
|
+
_not_implemented
|
142
|
+
end
|
143
|
+
|
144
|
+
def resume
|
145
|
+
_not_implemented
|
146
|
+
end
|
147
|
+
|
148
|
+
def_delegator :@channel, :send_data
|
149
|
+
|
150
|
+
def send_datagram(data, recipient_address, recipient_port)
|
151
|
+
_not_implemented
|
152
|
+
end
|
153
|
+
|
154
|
+
def send_file_data(filename)
|
155
|
+
_not_implemented
|
156
|
+
end
|
157
|
+
|
158
|
+
def_delegator :@channel, :set_sock_opt
|
159
|
+
|
160
|
+
def ssl_handshake_completed
|
161
|
+
_not_implemented
|
162
|
+
end
|
163
|
+
|
164
|
+
def ssl_verify_peer(cert)
|
165
|
+
_not_implemented
|
166
|
+
end
|
167
|
+
|
168
|
+
def start_tls(args = {})
|
169
|
+
_not_implemented
|
170
|
+
end
|
171
|
+
|
172
|
+
def stop_proxying
|
173
|
+
_not_implemented
|
174
|
+
end
|
175
|
+
|
176
|
+
def stream_file_data(filename, args = {})
|
177
|
+
_not_implemented
|
178
|
+
end
|
179
|
+
|
180
|
+
private
|
181
|
+
|
182
|
+
def _not_implemented
|
183
|
+
raise RuntimeError.new("API call not implemented!")
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
#--
|
2
|
+
#
|
3
|
+
# Author:: Francis Cianfrocca (gmail: blackhedd)
|
4
|
+
# Homepage:: http://rubyeventmachine.com
|
5
|
+
# Date:: 16 Jul 2006
|
6
|
+
#
|
7
|
+
# See ZMachine and ZMachine::Connection for documentation and
|
8
|
+
# usage examples.
|
9
|
+
#
|
10
|
+
#----------------------------------------------------------------------------
|
11
|
+
#
|
12
|
+
# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
13
|
+
# Gmail: blackhedd
|
14
|
+
#
|
15
|
+
# This program is free software; you can redistribute it and/or modify
|
16
|
+
# it under the terms of either: 1) the GNU General Public License
|
17
|
+
# as published by the Free Software Foundation; either version 2 of the
|
18
|
+
# License, or (at your option) any later version; or 2) Ruby's License.
|
19
|
+
#
|
20
|
+
# See the file COPYING for complete licensing information.
|
21
|
+
#
|
22
|
+
#---------------------------------------------------------------------------
|
23
|
+
#
|
24
|
+
#
|
25
|
+
|
26
|
+
module ZMachine
|
27
|
+
module Deferrable
|
28
|
+
autoload :Pool, 'em/deferrable/pool'
|
29
|
+
|
30
|
+
# Specify a block to be executed if and when the Deferrable object receives
|
31
|
+
# a status of :succeeded. See #set_deferred_status for more information.
|
32
|
+
#
|
33
|
+
# Calling this method on a Deferrable object whose status is not yet known
|
34
|
+
# will cause the callback block to be stored on an internal list.
|
35
|
+
# If you call this method on a Deferrable whose status is :succeeded, the
|
36
|
+
# block will be executed immediately, receiving the parameters given to the
|
37
|
+
# prior #set_deferred_status call.
|
38
|
+
#
|
39
|
+
#--
|
40
|
+
# If there is no status, add a callback to an internal list.
|
41
|
+
# If status is succeeded, execute the callback immediately.
|
42
|
+
# If status is failed, do nothing.
|
43
|
+
#
|
44
|
+
def callback(&block)
|
45
|
+
return unless block
|
46
|
+
@deferred_status ||= :unknown
|
47
|
+
if @deferred_status == :succeeded
|
48
|
+
block.call(*@deferred_args)
|
49
|
+
elsif @deferred_status != :failed
|
50
|
+
@callbacks ||= []
|
51
|
+
@callbacks.unshift(block) # << block
|
52
|
+
end
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
# Cancels an outstanding callback to &block if any. Undoes the action of #callback.
|
57
|
+
#
|
58
|
+
def cancel_callback(block)
|
59
|
+
@callbacks ||= []
|
60
|
+
@callbacks.delete(block)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Specify a block to be executed if and when the Deferrable object receives
|
64
|
+
# a status of :failed. See #set_deferred_status for more information.
|
65
|
+
#--
|
66
|
+
# If there is no status, add an errback to an internal list.
|
67
|
+
# If status is failed, execute the errback immediately.
|
68
|
+
# If status is succeeded, do nothing.
|
69
|
+
#
|
70
|
+
def errback(&block)
|
71
|
+
return unless block
|
72
|
+
@deferred_status ||= :unknown
|
73
|
+
if @deferred_status == :failed
|
74
|
+
block.call(*@deferred_args)
|
75
|
+
elsif @deferred_status != :succeeded
|
76
|
+
@errbacks ||= []
|
77
|
+
@errbacks.unshift(block) # << block
|
78
|
+
end
|
79
|
+
self
|
80
|
+
end
|
81
|
+
|
82
|
+
# Cancels an outstanding errback to &block if any. Undoes the action of #errback.
|
83
|
+
#
|
84
|
+
def cancel_errback(block)
|
85
|
+
@errbacks ||= []
|
86
|
+
@errbacks.delete(block)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Sets the "disposition" (status) of the Deferrable object. See also the large set of
|
90
|
+
# sugarings for this method.
|
91
|
+
# Note that if you call this method without arguments,
|
92
|
+
# no arguments will be passed to the callback/errback.
|
93
|
+
# If the user has coded these with arguments, then the
|
94
|
+
# user code will throw an argument exception.
|
95
|
+
# Implementors of deferrable classes <b>must</b>
|
96
|
+
# document the arguments they will supply to user callbacks.
|
97
|
+
#
|
98
|
+
# OBSERVE SOMETHING VERY SPECIAL here: you may call this method even
|
99
|
+
# on the INSIDE of a callback. This is very useful when a previously-registered
|
100
|
+
# callback wants to change the parameters that will be passed to subsequently-registered
|
101
|
+
# ones.
|
102
|
+
#
|
103
|
+
# You may give either :succeeded or :failed as the status argument.
|
104
|
+
#
|
105
|
+
# If you pass :succeeded, then all of the blocks passed to the object using the #callback
|
106
|
+
# method (if any) will be executed BEFORE the #set_deferred_status method returns. All of the blocks
|
107
|
+
# passed to the object using #errback will be discarded.
|
108
|
+
#
|
109
|
+
# If you pass :failed, then all of the blocks passed to the object using the #errback
|
110
|
+
# method (if any) will be executed BEFORE the #set_deferred_status method returns. All of the blocks
|
111
|
+
# passed to the object using # callback will be discarded.
|
112
|
+
#
|
113
|
+
# If you pass any arguments to #set_deferred_status in addition to the status argument,
|
114
|
+
# they will be passed as arguments to any callbacks or errbacks that are executed.
|
115
|
+
# It's your responsibility to ensure that the argument lists specified in your callbacks and
|
116
|
+
# errbacks match the arguments given in calls to #set_deferred_status, otherwise Ruby will raise
|
117
|
+
# an ArgumentError.
|
118
|
+
#
|
119
|
+
#--
|
120
|
+
# We're shifting callbacks off and discarding them as we execute them.
|
121
|
+
# This is valid because by definition callbacks are executed no more than
|
122
|
+
# once. It also has the magic effect of permitting recursive calls, which
|
123
|
+
# means that a callback can call #set_deferred_status and change the parameters
|
124
|
+
# that will be sent to subsequent callbacks down the chain.
|
125
|
+
#
|
126
|
+
# Changed @callbacks and @errbacks from push/shift to unshift/pop, per suggestion
|
127
|
+
# by Kirk Haines, to work around the memory leak bug that still exists in many Ruby
|
128
|
+
# versions.
|
129
|
+
#
|
130
|
+
# Changed 15Sep07: after processing callbacks or errbacks, CLEAR the other set of
|
131
|
+
# handlers. This gets us a little closer to the behavior of Twisted's "deferred,"
|
132
|
+
# which only allows status to be set once. Prior to making this change, it was possible
|
133
|
+
# to "succeed" a Deferrable (triggering its callbacks), and then immediately "fail" it,
|
134
|
+
# triggering its errbacks! That is clearly undesirable, but it's just as undesirable
|
135
|
+
# to raise an exception is status is set more than once on a Deferrable. The latter
|
136
|
+
# behavior would invalidate the idiom of resetting arguments by setting status from
|
137
|
+
# within a callback or errback, but more seriously it would cause spurious errors
|
138
|
+
# if a Deferrable was timed out and then an attempt was made to succeed it. See the
|
139
|
+
# comments under the new method #timeout.
|
140
|
+
#
|
141
|
+
def set_deferred_status(status, *args)
|
142
|
+
cancel_timeout
|
143
|
+
@errbacks ||= nil
|
144
|
+
@callbacks ||= nil
|
145
|
+
@deferred_status = status
|
146
|
+
@deferred_args = args
|
147
|
+
case @deferred_status
|
148
|
+
when :succeeded
|
149
|
+
if @callbacks
|
150
|
+
while cb = @callbacks.pop
|
151
|
+
cb.call(*@deferred_args)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
@errbacks.clear if @errbacks
|
155
|
+
when :failed
|
156
|
+
if @errbacks
|
157
|
+
while eb = @errbacks.pop
|
158
|
+
eb.call(*@deferred_args)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
@callbacks.clear if @callbacks
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# Setting a timeout on a Deferrable causes it to go into the failed state after
|
166
|
+
# the Timeout expires (passing no arguments to the object's errbacks).
|
167
|
+
# Setting the status at any time prior to a call to the expiration of the timeout
|
168
|
+
# will cause the timer to be cancelled.
|
169
|
+
def timeout(seconds, *args)
|
170
|
+
cancel_timeout
|
171
|
+
me = self
|
172
|
+
@deferred_timeout = ZMachine::Timer.new(seconds) { me.fail(*args) }
|
173
|
+
self
|
174
|
+
end
|
175
|
+
|
176
|
+
# Cancels an outstanding timeout if any. Undoes the action of #timeout.
|
177
|
+
#
|
178
|
+
def cancel_timeout
|
179
|
+
@deferred_timeout ||= nil
|
180
|
+
if @deferred_timeout
|
181
|
+
@deferred_timeout.cancel
|
182
|
+
@deferred_timeout = nil
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Sugar for set_deferred_status(:succeeded, ...)
|
187
|
+
#
|
188
|
+
def succeed(*args)
|
189
|
+
set_deferred_status(:succeeded, *args)
|
190
|
+
end
|
191
|
+
alias set_deferred_success succeed
|
192
|
+
|
193
|
+
# Sugar for set_deferred_status(:failed, ...)
|
194
|
+
#
|
195
|
+
def fail(*args)
|
196
|
+
set_deferred_status(:failed, *args)
|
197
|
+
end
|
198
|
+
alias set_deferred_failure fail
|
199
|
+
end
|
200
|
+
|
201
|
+
# DefaultDeferrable is an otherwise empty class that includes Deferrable.
|
202
|
+
# This is very useful when you just need to return a Deferrable object
|
203
|
+
# as a way of communicating deferred status to some other part of a program.
|
204
|
+
class DefaultDeferrable
|
205
|
+
include Deferrable
|
206
|
+
end
|
207
|
+
end
|
Binary file
|