spade-runtime 0.1.0.1
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.
- data/.gitignore +2 -0
- data/bin/spaderun +9 -0
- data/lib/spade-runtime.rb +1 -0
- data/lib/spade/runtime.rb +20 -0
- data/lib/spade/runtime/bundle.rb +173 -0
- data/lib/spade/runtime/cli.rb +7 -0
- data/lib/spade/runtime/cli/base.rb +181 -0
- data/lib/spade/runtime/compiler.rb +34 -0
- data/lib/spade/runtime/console.rb +39 -0
- data/lib/spade/runtime/context.rb +114 -0
- data/lib/spade/runtime/exports.rb +86 -0
- data/lib/spade/runtime/loader.rb +209 -0
- data/lib/spade/runtime/reactor.rb +159 -0
- data/lib/spade/runtime/server.rb +66 -0
- data/lib/spade/runtime/shell.rb +36 -0
- data/lib/spade/runtime/version.rb +5 -0
- data/spade-runtime.gemspec +36 -0
- data/spec/cli/update_spec.rb +64 -0
- data/spec/javascript/async-test.js +123 -0
- data/spec/javascript/compiler/javascript.js +13 -0
- data/spec/javascript/compiler/ruby.js +14 -0
- data/spec/javascript/loader-test.js +64 -0
- data/spec/javascript/normalize-test.js +73 -0
- data/spec/javascript/packages-test.js +44 -0
- data/spec/javascript/relative-require-test.js +72 -0
- data/spec/javascript/require-test.js +117 -0
- data/spec/javascript/sandbox/compile.js +37 -0
- data/spec/javascript/sandbox/creation.js +44 -0
- data/spec/javascript/sandbox/format.js +79 -0
- data/spec/javascript/sandbox/misc.js +57 -0
- data/spec/javascript/sandbox/preprocessor.js +81 -0
- data/spec/javascript/sandbox/require.js +48 -0
- data/spec/javascript/sandbox/run-command.js +21 -0
- data/spec/javascript/spade/externs.js +14 -0
- data/spec/javascript/spade/load-factory.js +15 -0
- data/spec/javascript/spade/misc.js +23 -0
- data/spec/javascript/spade/ready.js +12 -0
- data/spec/javascript/spade/register.js +13 -0
- data/spec/javascript_spec.rb +7 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/cli.rb +109 -0
- data/spec/support/core_test.rb +61 -0
- data/spec/support/matchers.rb +12 -0
- data/spec/support/path.rb +62 -0
- metadata +218 -0
@@ -0,0 +1,114 @@
|
|
1
|
+
# ==========================================================================
|
2
|
+
# Project: Spade - CommonJS Runtime
|
3
|
+
# Copyright: ©2010 Strobe Inc. All rights reserved.
|
4
|
+
# License: Licened under MIT license (see LICENSE)
|
5
|
+
# ==========================================================================
|
6
|
+
|
7
|
+
require 'v8'
|
8
|
+
require 'spade/runtime/compiler'
|
9
|
+
require 'spade/runtime/console'
|
10
|
+
require 'spade/runtime/loader'
|
11
|
+
require 'spade/runtime/reactor'
|
12
|
+
|
13
|
+
module Spade::Runtime
|
14
|
+
|
15
|
+
# Creates a basic context suitable for running modules. The environments
|
16
|
+
# setup in this context will mimic a browser worker thread context,
|
17
|
+
# including timeouts and a console. A navigator object is also defined
|
18
|
+
# that provides some general information about the context.
|
19
|
+
class Context < V8::Context
|
20
|
+
|
21
|
+
attr_reader :reactor
|
22
|
+
attr_reader :verbose
|
23
|
+
|
24
|
+
def require(mod_name)
|
25
|
+
self.eval("require('#{mod_name}');");
|
26
|
+
end
|
27
|
+
|
28
|
+
# Load the spade and racer-loader.
|
29
|
+
def initialize(opts={})
|
30
|
+
@reactor = opts[:reactor]
|
31
|
+
@verbose = opts[:verbose]
|
32
|
+
super(opts) do |ctx|
|
33
|
+
ctx['reactor'] = @reactor
|
34
|
+
ctx['console'] = Console.new
|
35
|
+
ctx['window'] = ctx.scope
|
36
|
+
ctx.eval %[
|
37
|
+
(function() {
|
38
|
+
var r = reactor;
|
39
|
+
setTimeout = function(c,i) { return r.set_timeout(c,i); };
|
40
|
+
setInterval = function(c,i) { return r.set_interval(c,i); };
|
41
|
+
clearTimeout = function(t) { return r.clear_timeout(t); };
|
42
|
+
clearInterval = function(t) { return r.clear_interval(t); };
|
43
|
+
navigator = {
|
44
|
+
appName: 'spade',
|
45
|
+
appVersion: "#{Spade::Runtime::VERSION}",
|
46
|
+
platform: "#{RUBY_PLATFORM}",
|
47
|
+
userAgent: 'spade #{Spade::Runtime::VERSION}; #{RUBY_PLATFORM}'
|
48
|
+
}
|
49
|
+
|
50
|
+
exit = function(status) { return r.exit(status || 0); };
|
51
|
+
})();
|
52
|
+
]
|
53
|
+
|
54
|
+
ctx['reactor'] = nil
|
55
|
+
|
56
|
+
yield(ctx) if block_given?
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
# The primary context created when running spade exec or spade console.
|
63
|
+
# This context will also automatically start a reactor loop.
|
64
|
+
class MainContext < Context
|
65
|
+
|
66
|
+
attr_accessor :rootdir
|
67
|
+
attr_accessor :caller_id
|
68
|
+
|
69
|
+
# Load the spade and racer-loader.
|
70
|
+
def initialize(opts={})
|
71
|
+
env = opts[:env] || ENV
|
72
|
+
@rootdir = opts[:rootdir] || opts['rootdir']
|
73
|
+
@caller_id = opts[:caller_id] || opts['caller_id']
|
74
|
+
@reactor = opts[:reactor] ||= Reactor.new(self)
|
75
|
+
lang = opts[:language] ||= (env['LANG']||'en_US').gsub(/\..*/, '')
|
76
|
+
lang = lang.gsub '_', '-'
|
77
|
+
|
78
|
+
|
79
|
+
super(opts) do |ctx|
|
80
|
+
ctx['ENV'] = env.to_hash
|
81
|
+
ctx['ENV']['SPADE_PLATFORM'] = { 'ENGINE' => 'spade' }
|
82
|
+
ctx['ENV']['LANG'] = lang
|
83
|
+
|
84
|
+
ctx['ARGV'] = opts[:argv] || ARGV
|
85
|
+
|
86
|
+
# Load spade and patch in compiler and loader plugins
|
87
|
+
ctx.load(Spade::JSPATH)
|
88
|
+
ctx['rubyLoader'] = Loader.new(self)
|
89
|
+
ctx['rubyCompiler'] = Compiler.new(self)
|
90
|
+
|
91
|
+
ctx.eval %[
|
92
|
+
spade.loader = rubyLoader;
|
93
|
+
spade.compiler = rubyCompiler;
|
94
|
+
spade.defaultSandbox.rootdir = #{@rootdir.to_json};
|
95
|
+
spade.globalize();
|
96
|
+
]
|
97
|
+
|
98
|
+
ctx.eval("spade.defaultSandbox.callerId = #{@caller_id.to_json};") if @caller_id
|
99
|
+
|
100
|
+
ctx['rubyLoader'] = ctx['rubyCompiler'] = nil
|
101
|
+
|
102
|
+
@reactor.start do
|
103
|
+
yield(self) if block_given?
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
|
114
|
+
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# ==========================================================================
|
2
|
+
# Project: Spade - CommonJS Runtime
|
3
|
+
# Copyright: ©2010 Strobe Inc. All rights reserved.
|
4
|
+
# License: Licened under MIT license (see LICENSE)
|
5
|
+
# ==========================================================================
|
6
|
+
|
7
|
+
module Spade
|
8
|
+
module Runtime
|
9
|
+
module Namespace
|
10
|
+
|
11
|
+
def [](name)
|
12
|
+
begin
|
13
|
+
self.class.const_defined?(name) ? self.class.const_get(name) : yield
|
14
|
+
rescue NameError => e
|
15
|
+
yield
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
class Exports
|
22
|
+
|
23
|
+
attr_reader :context
|
24
|
+
|
25
|
+
def initialize(ctx)
|
26
|
+
@context = ctx
|
27
|
+
end
|
28
|
+
|
29
|
+
def [](name)
|
30
|
+
|
31
|
+
begin
|
32
|
+
if self.class.const_defined?(name)
|
33
|
+
ret = self.class.const_get(name)
|
34
|
+
|
35
|
+
# If we are returning a class, create a custom subclass the first
|
36
|
+
# time that also exposes the current context.
|
37
|
+
if ret.instance_of? Class
|
38
|
+
@klass_cache ||= {}
|
39
|
+
unless @klass_cache[name]
|
40
|
+
|
41
|
+
proc1 = proc { @context }
|
42
|
+
@klass_cache[name] = Class.new(ret) do
|
43
|
+
|
44
|
+
@context = proc1.call
|
45
|
+
|
46
|
+
def self.context
|
47
|
+
@context
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
@klass_cache[name]
|
55
|
+
else
|
56
|
+
ret
|
57
|
+
end
|
58
|
+
|
59
|
+
else
|
60
|
+
yield
|
61
|
+
end
|
62
|
+
|
63
|
+
rescue NameError => e
|
64
|
+
yield
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
module Namespace
|
72
|
+
include Runtime::Namespace
|
73
|
+
|
74
|
+
def self.included(base)
|
75
|
+
warn("Spade::Namespace is deprecated. Use Spade::Runtime::Namespace instead. From #{base.name}")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class Exports < Runtime::Exports
|
80
|
+
def self.inherited(subclass)
|
81
|
+
warn("Spade::Exports is deprecated. Used Spade::Runtime::Exports instead. From #{subclass.name}")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
@@ -0,0 +1,209 @@
|
|
1
|
+
# ==========================================================================
|
2
|
+
# Project: Spade - CommonJS Runtime
|
3
|
+
# Copyright: ©2010 Strobe Inc. All rights reserved.
|
4
|
+
# License: Licened under MIT license (see LICENSE)
|
5
|
+
# ==========================================================================
|
6
|
+
|
7
|
+
require 'json'
|
8
|
+
|
9
|
+
|
10
|
+
module Spade::Runtime
|
11
|
+
|
12
|
+
class Loader
|
13
|
+
|
14
|
+
def initialize(ctx)
|
15
|
+
@ctx = ctx
|
16
|
+
end
|
17
|
+
|
18
|
+
def discoverRoot(path)
|
19
|
+
Spade.discover_root path
|
20
|
+
end
|
21
|
+
|
22
|
+
def root(path=nil)
|
23
|
+
return @ctx.rootdir if path.nil?
|
24
|
+
@ctx.rootdir = path
|
25
|
+
@packages = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
# exposed to JS. Find the JS file on disk and register the module
|
29
|
+
def loadFactory(spade, id, formats, done=nil)
|
30
|
+
formats = formats.to_a # We may get a V8::Array, we want a normal one
|
31
|
+
|
32
|
+
# load individual files
|
33
|
+
if id =~ /^file:\//
|
34
|
+
js_path = id[5..-1]
|
35
|
+
if File.exists? js_path
|
36
|
+
load_module id, js_path, ['js'], js_path
|
37
|
+
end
|
38
|
+
return nil
|
39
|
+
end
|
40
|
+
|
41
|
+
parts = id.split '/'
|
42
|
+
package_name = parts.shift
|
43
|
+
package_info = packages[package_name]
|
44
|
+
skip_module = false
|
45
|
+
|
46
|
+
return nil if package_info.nil?
|
47
|
+
|
48
|
+
if parts.size==1 && parts[0] == '~package'
|
49
|
+
skip_module = true
|
50
|
+
elsif parts.size==1 && parts[0] == 'main'
|
51
|
+
parts = (package_info[:json]['main'] || 'lib/main').split('/')
|
52
|
+
dirname = parts[0...-1]
|
53
|
+
parts = [parts[-1]]
|
54
|
+
else
|
55
|
+
dirname = extract_dirname(parts, package_info)
|
56
|
+
end
|
57
|
+
|
58
|
+
# register the package first - also make sure dependencies are
|
59
|
+
# registered since they are needed for loading plugins
|
60
|
+
unless package_info[:registered]
|
61
|
+
package_info[:registered] = true
|
62
|
+
@ctx.eval "spade.register('#{package_name}', #{package_info[:json].to_json});"
|
63
|
+
|
64
|
+
deps = package_info[:json]['dependencies'] || [];
|
65
|
+
deps.each do |dep_name, ignored|
|
66
|
+
dep_package_info = packages[dep_name]
|
67
|
+
next unless dep_package_info && !dep_package_info[:registered]
|
68
|
+
dep_package_info[:registered] = true
|
69
|
+
@ctx.eval "spade.register('#{dep_name}', #{dep_package_info[:json].to_json});"
|
70
|
+
|
71
|
+
# Add new formats if they are specified in our dependencies
|
72
|
+
dep_formats = dep_package_info[:json]['plugin:formats']
|
73
|
+
formats.unshift(*dep_formats.keys).uniq! if dep_formats
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
unless skip_module
|
79
|
+
filename = parts.pop
|
80
|
+
base_path = package_info[:path]
|
81
|
+
formats << ['js'] if formats.empty?
|
82
|
+
formats.each do |fmt|
|
83
|
+
cur_path = File.join(base_path, dirname, parts, filename+'.'+fmt)
|
84
|
+
if File.exist? cur_path
|
85
|
+
load_module id, cur_path, fmt, cur_path
|
86
|
+
return nil
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
rb_path = File.join(package_info[:path],dirname,parts, filename+'.rb')
|
91
|
+
if File.exists? rb_path
|
92
|
+
load_ruby id, rb_path
|
93
|
+
return nil
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
return nil
|
99
|
+
end
|
100
|
+
|
101
|
+
# exposed to JS. Determines if the named id exists in the system
|
102
|
+
def exists(spade, id, formats)
|
103
|
+
|
104
|
+
# individual files
|
105
|
+
return File.exists?(id[5..-1]) if id =~ /^file:\//
|
106
|
+
|
107
|
+
parts = id.split '/'
|
108
|
+
package_name = parts.shift
|
109
|
+
package_info = packages[package_name]
|
110
|
+
|
111
|
+
return false if package_info.nil?
|
112
|
+
return true if parts.size==1 && parts[0] == '~package'
|
113
|
+
|
114
|
+
dirname = extract_dirname(parts, package_info)
|
115
|
+
|
116
|
+
|
117
|
+
filename = parts.pop
|
118
|
+
base_path = package_info[:path]
|
119
|
+
formats = ['js'] if formats.nil?
|
120
|
+
formats.each do |fmt|
|
121
|
+
cur_path = File.join(base_path, dirname, parts, filename+'.'+fmt)
|
122
|
+
return true if File.exist? cur_path
|
123
|
+
end
|
124
|
+
|
125
|
+
rb_path = File.join(package_info[:path],dirname,parts, filename+'.rb')
|
126
|
+
return File.exists? rb_path
|
127
|
+
end
|
128
|
+
|
129
|
+
def load_module(id, module_path, format, path)
|
130
|
+
module_contents = File.read(module_path).to_json # encode as string
|
131
|
+
@ctx.eval("spade.register('#{id}',#{module_contents}, { format: #{format.to_s.to_json}, filename: #{path.to_s.to_json} });")
|
132
|
+
nil
|
133
|
+
end
|
134
|
+
|
135
|
+
def load_ruby(id, rb_path)
|
136
|
+
|
137
|
+
klass = Spade.exports_for rb_path
|
138
|
+
exports = klass.nil? ? {} : klass.new(@ctx)
|
139
|
+
@ctx['$__rb_exports__'] = exports
|
140
|
+
|
141
|
+
@ctx.eval(%[(function() {
|
142
|
+
var exp = $__rb_exports__;
|
143
|
+
spade.register('#{id}', function(r,e,m) { m.exports = exp; });
|
144
|
+
})();])
|
145
|
+
|
146
|
+
@ctx['$__rb_exports__'] = nil
|
147
|
+
end
|
148
|
+
|
149
|
+
def packages
|
150
|
+
@packages unless @packages.nil?
|
151
|
+
@packages = {}
|
152
|
+
|
153
|
+
if defined?(Spade::Packager)
|
154
|
+
package_paths = Dir.glob(File.join(LibGems.dir, 'gems', '*'))
|
155
|
+
package_paths.each{|path| add_package(path) }
|
156
|
+
end
|
157
|
+
|
158
|
+
# in reverse order of precedence
|
159
|
+
%w[.spade/packages vendor/cache vendor/packages packages].each do |p|
|
160
|
+
package_paths = Dir.glob File.join(@ctx.rootdir, p.split('/'), '*')
|
161
|
+
package_paths.each { |path| add_package(path) }
|
162
|
+
end
|
163
|
+
|
164
|
+
# add self
|
165
|
+
add_package @ctx.rootdir
|
166
|
+
|
167
|
+
@packages
|
168
|
+
end
|
169
|
+
|
170
|
+
private
|
171
|
+
|
172
|
+
def extract_dirname(parts, package_info)
|
173
|
+
if parts.size>0 && parts[0].chars.first == '~'
|
174
|
+
dirname = parts.shift[1..-1]
|
175
|
+
if package_info[:directories][dirname]
|
176
|
+
dirname = package_info[:directories][dirname]
|
177
|
+
else
|
178
|
+
dirname = dirname
|
179
|
+
end
|
180
|
+
else
|
181
|
+
dirname = package_info[:directories]['lib']
|
182
|
+
dirname = 'lib' if dirname.nil?
|
183
|
+
end
|
184
|
+
|
185
|
+
dirname
|
186
|
+
end
|
187
|
+
|
188
|
+
def add_package(path)
|
189
|
+
json_package = File.join(path, 'package.json')
|
190
|
+
return unless File.exists?(json_package)
|
191
|
+
|
192
|
+
json = JSON.load(File.read(json_package)) rescue nil
|
193
|
+
return if json.nil?
|
194
|
+
|
195
|
+
directories = json["directories"] || { "lib" => "lib" }
|
196
|
+
json["root"] = "file:/"+File.split(path).join('/')
|
197
|
+
@packages[json["name"]] = {
|
198
|
+
:registered => false,
|
199
|
+
:path => path,
|
200
|
+
:directories => directories,
|
201
|
+
:json => json
|
202
|
+
}
|
203
|
+
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
# ==========================================================================
|
2
|
+
# Project: Spade - CommonJS Runtime
|
3
|
+
# Copyright: ©2010 Strobe Inc. All rights reserved.
|
4
|
+
# License: Licened under MIT license (see LICENSE)
|
5
|
+
# ==========================================================================
|
6
|
+
|
7
|
+
require 'eventmachine'
|
8
|
+
|
9
|
+
module Spade::Runtime
|
10
|
+
|
11
|
+
# The reactor exposes some API
|
12
|
+
class Reactor
|
13
|
+
|
14
|
+
attr_reader :timers, :running, :holds, :stopped
|
15
|
+
|
16
|
+
def initialize(ctx)
|
17
|
+
@timers = []
|
18
|
+
@running = false
|
19
|
+
@stopped = false
|
20
|
+
@holds = 0
|
21
|
+
end
|
22
|
+
|
23
|
+
# Starts the event loop
|
24
|
+
def start
|
25
|
+
return if @running
|
26
|
+
|
27
|
+
@running = true
|
28
|
+
add_hold # release when finish is called
|
29
|
+
|
30
|
+
EventMachine.run do
|
31
|
+
@running = true
|
32
|
+
@timers.each { |t| t.start unless t.running }
|
33
|
+
|
34
|
+
yield if block_given?
|
35
|
+
|
36
|
+
@stopped = true
|
37
|
+
release_hold
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
def exit(status=0)
|
43
|
+
stop_loop
|
44
|
+
Kernel.exit(status)
|
45
|
+
end
|
46
|
+
|
47
|
+
def next_tick(&block)
|
48
|
+
if @running
|
49
|
+
add_hold
|
50
|
+
EventMachine.next_tick do
|
51
|
+
yield
|
52
|
+
release_hold
|
53
|
+
end
|
54
|
+
else
|
55
|
+
add_timer(0) { yield }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
######################################################
|
60
|
+
## Holds
|
61
|
+
##
|
62
|
+
## Prevents the loop from exiting
|
63
|
+
|
64
|
+
def add_hold
|
65
|
+
@holds = @holds + 1
|
66
|
+
end
|
67
|
+
|
68
|
+
def release_hold
|
69
|
+
@holds = @holds - 1
|
70
|
+
stop_loop if @stopped && @holds <= 0
|
71
|
+
end
|
72
|
+
|
73
|
+
def stop_loop
|
74
|
+
@running = false
|
75
|
+
EventMachine.stop_event_loop
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
######################################################
|
80
|
+
## Timers
|
81
|
+
##
|
82
|
+
|
83
|
+
def add_timer(interval, periodic = false, &block)
|
84
|
+
add_hold
|
85
|
+
Timer.new(self).tap do |timer|
|
86
|
+
timer.periodic = periodic
|
87
|
+
timer.interval = interval
|
88
|
+
timer.callback = block
|
89
|
+
@timers << timer
|
90
|
+
timer.start if @running
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def remove_timer(timer)
|
95
|
+
'remove_timer'
|
96
|
+
release_hold if @timers.delete(timer)
|
97
|
+
end
|
98
|
+
|
99
|
+
def set_timeout(callback, interval)
|
100
|
+
add_timer interval, false do
|
101
|
+
callback.methodcall(self)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def set_interval(callback, interval)
|
106
|
+
add_timer interval, true do
|
107
|
+
callback.methodcall(self)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def clear_timeout(timer)
|
112
|
+
timer.cancel
|
113
|
+
end
|
114
|
+
|
115
|
+
def clear_interval(timer)
|
116
|
+
timer.cancel
|
117
|
+
end
|
118
|
+
|
119
|
+
class Timer
|
120
|
+
|
121
|
+
attr_accessor :periodic, :interval, :callback
|
122
|
+
attr_reader :running
|
123
|
+
|
124
|
+
def initialize(reactor)
|
125
|
+
@interval = 0
|
126
|
+
@periodic = false
|
127
|
+
@callback = nil
|
128
|
+
@reactor = reactor
|
129
|
+
@running = false
|
130
|
+
end
|
131
|
+
|
132
|
+
def start
|
133
|
+
@running = true
|
134
|
+
if @periodic
|
135
|
+
@timer = EventMachine.add_periodic_timer(@interval.to_f / 1000) do
|
136
|
+
@callback.call
|
137
|
+
end
|
138
|
+
else
|
139
|
+
@timer = EventMachine.add_timer(@interval.to_f / 1000) do
|
140
|
+
@callback.call
|
141
|
+
@timer = nil
|
142
|
+
@reactor.remove_timer(self)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def cancel
|
148
|
+
EventMachine.cancel_timer(@timer) if @timer
|
149
|
+
@timer = nil
|
150
|
+
@reactor.remove_timer(self)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|