hyperstack-config 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.
- 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
|