hyperstack-config 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.travis.yml +29 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +303 -0
- data/README.md +73 -0
- data/Rakefile +11 -0
- data/bin/hyperstack-hotloader +22 -0
- data/hyperstack-config.gemspec +45 -0
- data/lib/hyperstack-config.rb +45 -0
- data/lib/hyperstack-hotloader-config.js.erb +7 -0
- data/lib/hyperstack-loader-application.rb.erb +1 -0
- data/lib/hyperstack-loader-system-code.rb.erb +1 -0
- data/lib/hyperstack-loader.js +6 -0
- data/lib/hyperstack-prerender-loader-application.rb.erb +1 -0
- data/lib/hyperstack-prerender-loader-system-code.rb.erb +1 -0
- data/lib/hyperstack-prerender-loader.js +4 -0
- data/lib/hyperstack/active_support_string_inquirer.rb +32 -0
- data/lib/hyperstack/autoloader.rb +140 -0
- data/lib/hyperstack/autoloader_starter.rb +15 -0
- data/lib/hyperstack/boot.rb +41 -0
- data/lib/hyperstack/client_readers.rb +19 -0
- data/lib/hyperstack/client_stubs.rb +12 -0
- data/lib/hyperstack/config/version.rb +5 -0
- data/lib/hyperstack/config_settings.rb +49 -0
- data/lib/hyperstack/context.rb +36 -0
- data/lib/hyperstack/deprecation_warning.rb +10 -0
- data/lib/hyperstack/env.rb +10 -0
- data/lib/hyperstack/environment/development/hyperstack_env.rb +5 -0
- data/lib/hyperstack/environment/production/hyperstack_env.rb +5 -0
- data/lib/hyperstack/environment/staging/hyperstack_env.rb +5 -0
- data/lib/hyperstack/environment/test/hyperstack_env.rb +5 -0
- data/lib/hyperstack/hotloader.rb +129 -0
- data/lib/hyperstack/hotloader/add_error_boundry.rb +31 -0
- data/lib/hyperstack/hotloader/css_reloader.rb +41 -0
- data/lib/hyperstack/hotloader/server.rb +296 -0
- data/lib/hyperstack/hotloader/short_cut.js +9 -0
- data/lib/hyperstack/hotloader/socket.rb +136 -0
- data/lib/hyperstack/hotloader/stack-trace.js +2977 -0
- data/lib/hyperstack/hotloader/stub.rb +10 -0
- data/lib/hyperstack/imports.rb +104 -0
- data/lib/hyperstack/js_imports.rb +18 -0
- data/lib/hyperstack/on_client.rb +5 -0
- data/lib/hyperstack/on_error.rb +5 -0
- data/lib/hyperstack/rail_tie.rb +87 -0
- data/lib/hyperstack/string.rb +6 -0
- metadata +350 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
module Hyperstack
|
2
|
+
# Allows interactive systems to reset context to the state at boot. Any
|
3
|
+
# modules or classes that set context instance variables to hold things like
|
4
|
+
# call backs should use Hyperstack::Context.set_var(self, :@var_name) { .... }
|
5
|
+
# the provided block will be rerun and the instance variable re-initialized
|
6
|
+
# when the reset! method is called
|
7
|
+
module Context
|
8
|
+
# Replace @foo ||= ... with
|
9
|
+
# Context.set_var(self, :@foo) { ... }
|
10
|
+
# If reset! has been called then the instance variable will be record, and
|
11
|
+
# will be reset on the next call to reset!
|
12
|
+
# If you want to record the current value of the instance variable then set
|
13
|
+
# force to true.
|
14
|
+
def self.set_var(ctx, var, force: nil)
|
15
|
+
inst_value_b4 = ctx.instance_variable_get(var)
|
16
|
+
if @context && !@context[ctx].key?(var) && (force || !inst_value_b4)
|
17
|
+
@context[ctx][var] = (inst_value_b4 && inst_value_b4.dup)
|
18
|
+
end
|
19
|
+
inst_value_b4 || ctx.instance_variable_set(var, yield)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.reset!(reboot = true)
|
23
|
+
# if @context is already initialized then reset all the instance
|
24
|
+
# vars using their corresponding blocks. Otherwise initialize
|
25
|
+
# @context.
|
26
|
+
if @context
|
27
|
+
@context.each do |ctx, vars|
|
28
|
+
vars.each { |var, init| ctx.instance_variable_set(var, init.dup) }
|
29
|
+
end
|
30
|
+
Hyperstack::Application::Boot.run if reboot
|
31
|
+
else
|
32
|
+
@context = Hash.new { |h, k| h[k] = {} }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Hyperstack
|
2
|
+
def self.deprecation_warning(name, message)
|
3
|
+
return if env.production?
|
4
|
+
@deprecation_messages ||= []
|
5
|
+
message = "Warning: Deprecated feature used in #{name}. #{message}"
|
6
|
+
return if @deprecation_messages.include? message
|
7
|
+
@deprecation_messages << message
|
8
|
+
`console.warn.apply(console, [message])`
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Hyperstack
|
2
|
+
def self.env
|
3
|
+
@environment ||= begin
|
4
|
+
env = Rails.env if defined? Rails
|
5
|
+
env ||= ENV['RACK_ENV']
|
6
|
+
env = 'development' unless %w[development production test staging].include? env
|
7
|
+
ActiveSupport::StringInquirer.new(env)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'hyperstack/hotloader/add_error_boundry'
|
2
|
+
require 'hyperstack/hotloader/stack-trace.js'
|
3
|
+
require 'hyperstack/hotloader/css_reloader'
|
4
|
+
require 'opal-parser' # gives me 'eval', for hot-loading code
|
5
|
+
|
6
|
+
require 'json'
|
7
|
+
require 'hyperstack/hotloader/short_cut.js'
|
8
|
+
|
9
|
+
# Opal client to support hot reloading
|
10
|
+
$eval_proc = proc do |file_name, s|
|
11
|
+
$_hyperstack_reloader_file_name = file_name
|
12
|
+
eval s
|
13
|
+
end
|
14
|
+
|
15
|
+
module Hyperstack
|
16
|
+
|
17
|
+
class Hotloader
|
18
|
+
def self.callbackmaps
|
19
|
+
@@callbackmaps ||= Hash.new { |h, k| h[k] = Hash.new { |h1, k1| h1[k1] = Hash.new { |h2, k2| h2[k2] = Array.new }}}
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.record(klass, instance_var, depth, *items)
|
23
|
+
if $_hyperstack_reloader_file_name
|
24
|
+
callbackmaps[$_hyperstack_reloader_file_name][klass][instance_var].concat items
|
25
|
+
else
|
26
|
+
callback = lambda do |stack_frames|
|
27
|
+
file_name = `#{stack_frames[depth]}.fileName`
|
28
|
+
match = /^(.+\/assets\/)(.+\/)\2/.match(file_name)
|
29
|
+
if match
|
30
|
+
file_name = file_name.gsub(match[1]+match[2], '')
|
31
|
+
callbackmaps[file_name][klass][instance_var].concat items
|
32
|
+
end
|
33
|
+
end
|
34
|
+
error = lambda do |err|
|
35
|
+
`console.error(#{"hyperstack hot loader could not find source file for callback: #{err}"})`
|
36
|
+
end
|
37
|
+
`StackTrace.get().then(#{callback}).catch(#{error})`
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.remove(file_name)
|
42
|
+
callbackmaps[file_name].each do |klass, instance_vars|
|
43
|
+
instance_vars.each do |instance_var, items|
|
44
|
+
klass.instance_variable_get(instance_var).reject! { |item| items.include? item }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def connect_to_websocket(port)
|
50
|
+
host = `window.location.host`.sub(/:\d+/, '')
|
51
|
+
host = '127.0.0.1' if host == ''
|
52
|
+
protocol = `window.location.protocol` == 'https:' ? 'wss:' : 'ws:'
|
53
|
+
ws_url = "#{host}:#{port}"
|
54
|
+
puts "Hot-Reloader connecting to #{ws_url}"
|
55
|
+
ws = `new WebSocket(#{protocol} + '//' + #{ws_url})`
|
56
|
+
`#{ws}.onmessage = #{lambda { |e| reload(e) }}`
|
57
|
+
`setInterval(function() { #{ws}.send('') }, #{@ping * 1000})` if @ping
|
58
|
+
end
|
59
|
+
|
60
|
+
def notify_error(reload_request)
|
61
|
+
msg = "Hotloader #{reload_request[:filename]} RELOAD ERROR:\n\n#{$!}"
|
62
|
+
puts msg
|
63
|
+
alert msg if use_alert?
|
64
|
+
end
|
65
|
+
|
66
|
+
@@USE_ALERT = true
|
67
|
+
def self.alerts_on!
|
68
|
+
@@USE_ALERT = true
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.alerts_off!
|
72
|
+
@@USE_ALERT = false
|
73
|
+
end
|
74
|
+
|
75
|
+
def use_alert?
|
76
|
+
@@USE_ALERT
|
77
|
+
end
|
78
|
+
|
79
|
+
def reload(e)
|
80
|
+
reload_request = JSON.parse(`e.data`)
|
81
|
+
if reload_request[:type] == "ruby"
|
82
|
+
puts "Reloading #{reload_request[:filename]} (asset_path: #{reload_request[:asset_path]})"
|
83
|
+
begin
|
84
|
+
#Hyperstack::Context.reset! false
|
85
|
+
file_name = reload_request[:asset_path] #.gsub(/.+hyperstack\//, '')
|
86
|
+
Hotloader.remove(file_name)
|
87
|
+
$eval_proc.call file_name, reload_request[:source_code]
|
88
|
+
rescue
|
89
|
+
notify_error(reload_request)
|
90
|
+
end
|
91
|
+
if @reload_post_callback
|
92
|
+
@reload_post_callback.call
|
93
|
+
else
|
94
|
+
puts "no reloading callback to call"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
if reload_request[:type] == "css"
|
98
|
+
@css_reloader.reload(reload_request, `document`)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# @param port [Integer] opal hot reloader port to connect to
|
103
|
+
# @param reload_post_callback [Proc] optional callback to be called after re evaluating a file for example in react.rb files we want to do a React::Component.force_update!
|
104
|
+
def initialize(port=25222, ping=nil, &reload_post_callback)
|
105
|
+
@port = port
|
106
|
+
@reload_post_callback = reload_post_callback
|
107
|
+
@css_reloader = CssReloader.new
|
108
|
+
@ping = ping
|
109
|
+
end
|
110
|
+
# Opens a websocket connection that evaluates new files and runs the optional @reload_post_callback
|
111
|
+
def listen
|
112
|
+
connect_to_websocket(@port)
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.listen(port=25222, ping=nil)
|
116
|
+
::Hyperstack::Internal::Component::TopLevelRailsComponent.include AddErrorBoundry
|
117
|
+
@server = Hotloader.new(port, ping) do
|
118
|
+
# TODO: check this out when Operations are integrated
|
119
|
+
# if defined?(Hyperloop::Internal::Operation::ClientDrivers) &&
|
120
|
+
# Hyperloop::ClientDrivers.respond_to?(:initialize_client_drivers_on_boot)
|
121
|
+
# Hyperloop::ClientDrivers.initialize_client_drivers_on_boot
|
122
|
+
# end
|
123
|
+
Hyperstack::Component.force_update!
|
124
|
+
end
|
125
|
+
@server.listen
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Hyperstack
|
2
|
+
class Hotloader
|
3
|
+
module AddErrorBoundry
|
4
|
+
def self.included(base)
|
5
|
+
base.after_error do |*err|
|
6
|
+
@err = err
|
7
|
+
Hyperstack::Component.force_update!
|
8
|
+
end
|
9
|
+
base.define_method :render do
|
10
|
+
@err ? parse_display_and_clear_error : top_level_render
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse_display_and_clear_error
|
15
|
+
e = @err[0]
|
16
|
+
component_stack = @err[1]['componentStack'].split("\n ")
|
17
|
+
@err = nil
|
18
|
+
display_error(e, component_stack)
|
19
|
+
end
|
20
|
+
|
21
|
+
def display_error(e, component_stack)
|
22
|
+
DIV do
|
23
|
+
DIV { "Uncaught error: #{e}" }
|
24
|
+
component_stack.each do |line|
|
25
|
+
DIV { line }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'native'
|
2
|
+
module Hyperstack
|
3
|
+
class Hotloader
|
4
|
+
class CssReloader
|
5
|
+
|
6
|
+
def reload(reload_request, document)
|
7
|
+
url = reload_request[:url]
|
8
|
+
puts "Reloading CSS: #{url}"
|
9
|
+
to_append = "t_hot_reload=#{Time.now.to_i}"
|
10
|
+
links = Native(`document.getElementsByTagName("link")`)
|
11
|
+
(0..links.length-1).each { |i|
|
12
|
+
link = links[i]
|
13
|
+
if link.rel == 'stylesheet' && is_matching_stylesheet?(link.href, url)
|
14
|
+
if link.href !~ /\?/
|
15
|
+
link.href += "?#{to_append}"
|
16
|
+
else
|
17
|
+
if link.href !~ /t_hot_reload/
|
18
|
+
link.href += "&#{to_append}"
|
19
|
+
else
|
20
|
+
link.href = link.href.sub(/t_hot_reload=\d+/, to_append)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def is_matching_stylesheet?(href, url)
|
28
|
+
# straight match, like in Rack::Sass::Plugin
|
29
|
+
if href.index(url)
|
30
|
+
true
|
31
|
+
else
|
32
|
+
# Rails asset pipeline match
|
33
|
+
url_base = File.basename(url).sub(/\.s?css+/, '').sub(/\.s?css+/, '')
|
34
|
+
href_base = File.basename(href).sub(/\.self-?.*.css.+/, '')
|
35
|
+
url_base == href_base
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,296 @@
|
|
1
|
+
require 'websocket'
|
2
|
+
require 'socket'
|
3
|
+
require 'fiber'
|
4
|
+
require 'listen'
|
5
|
+
require 'optparse'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
module Hyperstack
|
9
|
+
class Hotloader
|
10
|
+
# Most of this lifted from https://github.com/saward/Rubame
|
11
|
+
class Server
|
12
|
+
|
13
|
+
attr_reader :directories
|
14
|
+
def initialize(options)
|
15
|
+
Socket.do_not_reverse_lookup
|
16
|
+
@hostname = '0.0.0.0'
|
17
|
+
@port = options[:port]
|
18
|
+
setup_directories(options)
|
19
|
+
|
20
|
+
@reading = []
|
21
|
+
@writing = []
|
22
|
+
|
23
|
+
@clients = {} # Socket as key, and Client as value
|
24
|
+
|
25
|
+
@socket = TCPServer.new(@hostname, @port)
|
26
|
+
@reading.push @socket
|
27
|
+
end
|
28
|
+
|
29
|
+
# adds known directories automatically if they exist
|
30
|
+
# - rails js app/assets/javascripts app/assets/stylesheets
|
31
|
+
# - reactrb rails defaults app/views/components
|
32
|
+
# - you tell me and I'll add them
|
33
|
+
def setup_directories(options)
|
34
|
+
@directories = options[:directories] || []
|
35
|
+
[
|
36
|
+
'app/assets/javascripts',
|
37
|
+
'app/assets/stylesheets',
|
38
|
+
'app/views/components'
|
39
|
+
].each { |known_dir|
|
40
|
+
if !@directories.include?(known_dir) && File.exists?(known_dir)
|
41
|
+
@directories << known_dir
|
42
|
+
end
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def accept
|
47
|
+
socket = @socket.accept_nonblock
|
48
|
+
@reading.push socket
|
49
|
+
handshake = WebSocket::Handshake::Server.new
|
50
|
+
client = Client.new(socket, handshake, self)
|
51
|
+
|
52
|
+
while line = socket.gets
|
53
|
+
client.handshake << line
|
54
|
+
break if client.handshake.finished?
|
55
|
+
end
|
56
|
+
if client.handshake.valid?
|
57
|
+
@clients[socket] = client
|
58
|
+
client.write handshake.to_s
|
59
|
+
client.opened = true
|
60
|
+
return client
|
61
|
+
else
|
62
|
+
close(client)
|
63
|
+
end
|
64
|
+
return nil
|
65
|
+
end
|
66
|
+
|
67
|
+
def send_updated_file(modified_file)
|
68
|
+
if modified_file =~ /\.rb(\.erb)?$/
|
69
|
+
if File.exist?(modified_file)
|
70
|
+
file_contents = File.read(modified_file).force_encoding(Encoding::UTF_8)
|
71
|
+
end
|
72
|
+
relative_path = Pathname.new(modified_file).relative_path_from(Pathname.new(Dir.pwd)).to_s
|
73
|
+
asset_path = nil
|
74
|
+
@directories.each do |directory|
|
75
|
+
next unless relative_path =~ /^#{directory}/
|
76
|
+
asset_path = relative_path.gsub("#{directory}/", '')
|
77
|
+
end
|
78
|
+
update = {
|
79
|
+
type: 'ruby',
|
80
|
+
filename: modified_file,
|
81
|
+
asset_path: asset_path,
|
82
|
+
source_code: file_contents || ''
|
83
|
+
}.to_json
|
84
|
+
end
|
85
|
+
if modified_file =~ /\.s?[ac]ss$/ && File.exist?(modified_file)
|
86
|
+
# Don't know how to remove css definitions at the moment
|
87
|
+
# TODO: Switch from hard-wired path assumptions to using SASS/sprockets config
|
88
|
+
relative_path = Pathname.new(modified_file).relative_path_from(Pathname.new(Dir.pwd))
|
89
|
+
url = relative_path.to_s
|
90
|
+
.sub('public/','')
|
91
|
+
.sub('/sass/','/')
|
92
|
+
.sub(/\.s[ac]ss/, '.css')
|
93
|
+
update = {
|
94
|
+
type: 'css',
|
95
|
+
filename: modified_file,
|
96
|
+
url: url
|
97
|
+
}.to_json
|
98
|
+
end
|
99
|
+
if update
|
100
|
+
@clients.each { |socket, client| client.send(update) }
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
PROGRAM = 'opal-hot-reloader'
|
105
|
+
def loop
|
106
|
+
listener = Listen.to(*@directories, only: %r{\.(rb(\.erb)?|s?[ac]ss)$}) do |modified, added, removed|
|
107
|
+
(removed + modified + added).each { |file| send_updated_file(file) }
|
108
|
+
puts "removed absolute path: #{removed}"
|
109
|
+
puts "modified absolute path: #{modified}"
|
110
|
+
puts "added absolute path: #{added}"
|
111
|
+
end
|
112
|
+
listener.start
|
113
|
+
|
114
|
+
puts "#{PROGRAM}: starting..."
|
115
|
+
while (!$quit)
|
116
|
+
run do |client|
|
117
|
+
client.onopen do
|
118
|
+
puts "#{PROGRAM}: client open"
|
119
|
+
end
|
120
|
+
client.onmessage do |mess|
|
121
|
+
puts "PROGRAM: message received: #{mess}" unless mess == ''
|
122
|
+
end
|
123
|
+
client.onclose do
|
124
|
+
puts "#{PROGRAM}: client closed"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
sleep 0.2
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def read(client)
|
132
|
+
|
133
|
+
pairs = client.socket.recvfrom(2000)
|
134
|
+
messages = []
|
135
|
+
|
136
|
+
if pairs[0].length == 0
|
137
|
+
close(client)
|
138
|
+
else
|
139
|
+
client.frame << pairs[0]
|
140
|
+
|
141
|
+
while f = client.frame.next
|
142
|
+
if (f.type == :close)
|
143
|
+
close(client)
|
144
|
+
return messages
|
145
|
+
else
|
146
|
+
messages.push f
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
return messages
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
def close(client)
|
157
|
+
@reading.delete client.socket
|
158
|
+
@clients.delete client.socket
|
159
|
+
begin
|
160
|
+
client.socket.close
|
161
|
+
rescue
|
162
|
+
end
|
163
|
+
client.closed = true
|
164
|
+
end
|
165
|
+
|
166
|
+
def run(time = 0, &blk)
|
167
|
+
readable, writable = IO.select(@reading, @writing, nil, 0)
|
168
|
+
|
169
|
+
if readable
|
170
|
+
readable.each do |socket|
|
171
|
+
client = @clients[socket]
|
172
|
+
if socket == @socket
|
173
|
+
client = accept
|
174
|
+
else
|
175
|
+
msg = read(client)
|
176
|
+
client.messaged = msg
|
177
|
+
end
|
178
|
+
|
179
|
+
blk.call(client) if client and blk
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# Check for lazy send items
|
184
|
+
timer_start = Time.now
|
185
|
+
time_passed = 0
|
186
|
+
begin
|
187
|
+
@clients.each do |s, c|
|
188
|
+
c.send_some_lazy(5)
|
189
|
+
end
|
190
|
+
time_passed = Time.now - timer_start
|
191
|
+
end while time_passed < time
|
192
|
+
end
|
193
|
+
|
194
|
+
def stop
|
195
|
+
@socket.close
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
class Client
|
200
|
+
attr_accessor :socket, :handshake, :frame, :opened, :messaged, :closed
|
201
|
+
|
202
|
+
def initialize(socket, handshake, server)
|
203
|
+
@socket = socket
|
204
|
+
@handshake = handshake
|
205
|
+
@frame = WebSocket::Frame::Incoming::Server.new(:version => @handshake.version)
|
206
|
+
@opened = false
|
207
|
+
@messaged = []
|
208
|
+
@lazy_queue = []
|
209
|
+
@lazy_current_queue = nil
|
210
|
+
@closed = false
|
211
|
+
@server = server
|
212
|
+
end
|
213
|
+
|
214
|
+
def write(data)
|
215
|
+
@socket.write data
|
216
|
+
end
|
217
|
+
|
218
|
+
def send(data)
|
219
|
+
frame = WebSocket::Frame::Outgoing::Server.new(:version => @handshake.version, :data => data, :type => :text)
|
220
|
+
begin
|
221
|
+
@socket.write frame
|
222
|
+
@socket.flush
|
223
|
+
rescue
|
224
|
+
@server.close(self) unless @closed
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def lazy_send(data)
|
229
|
+
@lazy_queue.push data
|
230
|
+
end
|
231
|
+
|
232
|
+
def get_lazy_fiber
|
233
|
+
# Create the fiber if needed
|
234
|
+
if @lazy_fiber == nil or !@lazy_fiber.alive?
|
235
|
+
@lazy_fiber = Fiber.new do
|
236
|
+
@lazy_current_queue.each do |data|
|
237
|
+
send(data)
|
238
|
+
Fiber.yield unless @lazy_current_queue[-1] == data
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
return @lazy_fiber
|
244
|
+
end
|
245
|
+
|
246
|
+
def send_some_lazy(count)
|
247
|
+
# To save on cpu cycles, we don't want to be chopping and changing arrays, which could get quite large. Instead,
|
248
|
+
# we iterate over an array which we are sure won't change out from underneath us.
|
249
|
+
unless @lazy_current_queue
|
250
|
+
@lazy_current_queue = @lazy_queue
|
251
|
+
@lazy_queue = []
|
252
|
+
end
|
253
|
+
|
254
|
+
completed = 0
|
255
|
+
begin
|
256
|
+
get_lazy_fiber.resume
|
257
|
+
completed += 1
|
258
|
+
end while (@lazy_queue.count > 0 or @lazy_current_queue.count > 0) and completed < count
|
259
|
+
|
260
|
+
end
|
261
|
+
|
262
|
+
def onopen(&blk)
|
263
|
+
if @opened
|
264
|
+
begin
|
265
|
+
blk.call
|
266
|
+
ensure
|
267
|
+
@opened = false
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def onmessage(&blk)
|
273
|
+
if @messaged.size > 0
|
274
|
+
begin
|
275
|
+
@messaged.each do |x|
|
276
|
+
blk.call(x.to_s)
|
277
|
+
end
|
278
|
+
ensure
|
279
|
+
@messaged = []
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def onclose(&blk)
|
285
|
+
if @closed
|
286
|
+
begin
|
287
|
+
blk.call
|
288
|
+
ensure
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
line = 0
|
295
|
+
end
|
296
|
+
end
|