rails_live_reload 0.1.1 → 0.3.0
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 +4 -4
- data/README.md +6 -19
- data/lib/rails_live_reload/checker.rb +9 -3
- data/lib/rails_live_reload/config.rb +10 -6
- data/lib/rails_live_reload/engine.rb +16 -7
- data/lib/rails_live_reload/javascript/websocket.js +6 -0
- data/lib/rails_live_reload/middleware/base.rb +55 -0
- data/lib/rails_live_reload/server/base.rb +72 -0
- data/lib/rails_live_reload/server/connections.rb +27 -0
- data/lib/rails_live_reload/version.rb +1 -1
- data/lib/rails_live_reload/watcher.rb +39 -3
- data/lib/rails_live_reload/web_socket/base.rb +116 -0
- data/lib/rails_live_reload/web_socket/client_socket.rb +153 -0
- data/lib/rails_live_reload/web_socket/event_loop.rb +131 -0
- data/lib/rails_live_reload/web_socket/message_buffer.rb +53 -0
- data/lib/rails_live_reload/web_socket/stream.rb +109 -0
- data/lib/rails_live_reload/web_socket/wrapper.rb +27 -0
- data/lib/rails_live_reload.rb +21 -9
- metadata +17 -39
- data/lib/rails_live_reload/client.rb +0 -75
- data/lib/rails_live_reload/rails/middleware/base.rb +0 -63
- data/lib/rails_live_reload/rails/middleware/long_polling.rb +0 -51
- data/lib/rails_live_reload/rails/middleware/polling.rb +0 -21
@@ -0,0 +1,153 @@
|
|
1
|
+
require "websocket/driver"
|
2
|
+
|
3
|
+
module RailsLiveReload
|
4
|
+
module WebSocket
|
5
|
+
# This class is basically copied from ActionCable
|
6
|
+
# https://github.com/rails/rails/blob/v7.0.3/actioncable/lib/action_cable/connection/client_socket.rb
|
7
|
+
class ClientSocket
|
8
|
+
def self.determine_url(env)
|
9
|
+
scheme = secure_request?(env) ? "wss:" : "ws:"
|
10
|
+
"#{ scheme }//#{ env['HTTP_HOST'] }#{ env['REQUEST_URI'] }"
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.secure_request?(env)
|
14
|
+
return true if env["HTTPS"] == "on"
|
15
|
+
return true if env["HTTP_X_FORWARDED_SSL"] == "on"
|
16
|
+
return true if env["HTTP_X_FORWARDED_SCHEME"] == "https"
|
17
|
+
return true if env["HTTP_X_FORWARDED_PROTO"] == "https"
|
18
|
+
return true if env["rack.url_scheme"] == "https"
|
19
|
+
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
23
|
+
CONNECTING = 0
|
24
|
+
OPEN = 1
|
25
|
+
CLOSING = 2
|
26
|
+
CLOSED = 3
|
27
|
+
|
28
|
+
attr_reader :env, :url
|
29
|
+
|
30
|
+
def initialize(env, event_target, event_loop, protocols)
|
31
|
+
@env = env
|
32
|
+
@event_target = event_target
|
33
|
+
@event_loop = event_loop
|
34
|
+
|
35
|
+
@url = ClientSocket.determine_url(@env)
|
36
|
+
|
37
|
+
@driver = @driver_started = nil
|
38
|
+
@close_params = ["", 1006]
|
39
|
+
|
40
|
+
@ready_state = CONNECTING
|
41
|
+
|
42
|
+
@driver = ::WebSocket::Driver.rack(self, protocols: protocols)
|
43
|
+
|
44
|
+
@driver.on(:open) { |e| open }
|
45
|
+
@driver.on(:message) { |e| receive_message(e.data) }
|
46
|
+
@driver.on(:close) { |e| begin_close(e.reason, e.code) }
|
47
|
+
@driver.on(:error) { |e| emit_error(e.message) }
|
48
|
+
|
49
|
+
@stream = RailsLiveReload::WebSocket::Stream.new(@event_loop, self)
|
50
|
+
end
|
51
|
+
|
52
|
+
def start_driver
|
53
|
+
return if @driver.nil? || @driver_started
|
54
|
+
@stream.hijack_rack_socket
|
55
|
+
|
56
|
+
if callback = @env["async.callback"]
|
57
|
+
callback.call([101, {}, @stream])
|
58
|
+
end
|
59
|
+
|
60
|
+
@driver_started = true
|
61
|
+
@driver.start
|
62
|
+
end
|
63
|
+
|
64
|
+
def rack_response
|
65
|
+
start_driver
|
66
|
+
[ -1, {}, [] ]
|
67
|
+
end
|
68
|
+
|
69
|
+
def write(data)
|
70
|
+
@stream.write(data)
|
71
|
+
rescue => e
|
72
|
+
emit_error e.message
|
73
|
+
end
|
74
|
+
|
75
|
+
def transmit(message)
|
76
|
+
return false if @ready_state > OPEN
|
77
|
+
case message
|
78
|
+
when Numeric then @driver.text(message.to_s)
|
79
|
+
when String then @driver.text(message)
|
80
|
+
when Array then @driver.binary(message)
|
81
|
+
else false
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def close(code = nil, reason = nil)
|
86
|
+
code ||= 1000
|
87
|
+
reason ||= ""
|
88
|
+
|
89
|
+
unless code == 1000 || (code >= 3000 && code <= 4999)
|
90
|
+
raise ArgumentError, "Failed to execute 'close' on WebSocket: " \
|
91
|
+
"The code must be either 1000, or between 3000 and 4999. " \
|
92
|
+
"#{code} is neither."
|
93
|
+
end
|
94
|
+
|
95
|
+
@ready_state = CLOSING unless @ready_state == CLOSED
|
96
|
+
@driver.close(reason, code)
|
97
|
+
end
|
98
|
+
|
99
|
+
def parse(data)
|
100
|
+
@driver.parse(data)
|
101
|
+
end
|
102
|
+
|
103
|
+
def client_gone
|
104
|
+
finalize_close
|
105
|
+
end
|
106
|
+
|
107
|
+
def alive?
|
108
|
+
@ready_state == OPEN
|
109
|
+
end
|
110
|
+
|
111
|
+
def protocol
|
112
|
+
@driver.protocol
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def open
|
118
|
+
return unless @ready_state == CONNECTING
|
119
|
+
@ready_state = OPEN
|
120
|
+
|
121
|
+
@event_target.on_open
|
122
|
+
end
|
123
|
+
|
124
|
+
def receive_message(data)
|
125
|
+
return unless @ready_state == OPEN
|
126
|
+
|
127
|
+
@event_target.on_message(data)
|
128
|
+
end
|
129
|
+
|
130
|
+
def emit_error(message)
|
131
|
+
return if @ready_state >= CLOSING
|
132
|
+
|
133
|
+
@event_target.on_error(message)
|
134
|
+
end
|
135
|
+
|
136
|
+
def begin_close(reason, code)
|
137
|
+
return if @ready_state == CLOSED
|
138
|
+
@ready_state = CLOSING
|
139
|
+
@close_params = [reason, code]
|
140
|
+
|
141
|
+
@stream.shutdown if @stream
|
142
|
+
finalize_close
|
143
|
+
end
|
144
|
+
|
145
|
+
def finalize_close
|
146
|
+
return if @ready_state == CLOSED
|
147
|
+
@ready_state = CLOSED
|
148
|
+
|
149
|
+
@event_target.on_close(*@close_params)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require "nio"
|
2
|
+
|
3
|
+
module RailsLiveReload
|
4
|
+
module WebSocket
|
5
|
+
# This class is basically copied from ActionCable
|
6
|
+
# https://github.com/rails/rails/blob/v7.0.3/actioncable/lib/action_cable/connection/stream_event_loop.rb
|
7
|
+
class EventLoop
|
8
|
+
def initialize
|
9
|
+
@nio = @executor = @thread = nil
|
10
|
+
@map = {}
|
11
|
+
@stopping = false
|
12
|
+
@todo = Queue.new
|
13
|
+
@spawn_mutex = Mutex.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def timer(interval, &block)
|
17
|
+
Concurrent::TimerTask.new(execution_interval: interval, &block).tap(&:execute)
|
18
|
+
end
|
19
|
+
|
20
|
+
def post(task = nil, &block)
|
21
|
+
task ||= block
|
22
|
+
|
23
|
+
spawn
|
24
|
+
@executor << task
|
25
|
+
end
|
26
|
+
|
27
|
+
def attach(io, stream)
|
28
|
+
@todo << lambda do
|
29
|
+
@map[io] = @nio.register(io, :r)
|
30
|
+
@map[io].value = stream
|
31
|
+
end
|
32
|
+
wakeup
|
33
|
+
end
|
34
|
+
|
35
|
+
def detach(io, stream)
|
36
|
+
@todo << lambda do
|
37
|
+
@nio.deregister io
|
38
|
+
@map.delete io
|
39
|
+
io.close
|
40
|
+
end
|
41
|
+
wakeup
|
42
|
+
end
|
43
|
+
|
44
|
+
def writes_pending(io)
|
45
|
+
@todo << lambda do
|
46
|
+
if monitor = @map[io]
|
47
|
+
monitor.interests = :rw
|
48
|
+
end
|
49
|
+
end
|
50
|
+
wakeup
|
51
|
+
end
|
52
|
+
|
53
|
+
def stop
|
54
|
+
@stopping = true
|
55
|
+
wakeup if @nio
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def spawn
|
61
|
+
return if @thread && @thread.status
|
62
|
+
|
63
|
+
@spawn_mutex.synchronize do
|
64
|
+
return if @thread && @thread.status
|
65
|
+
|
66
|
+
@nio ||= NIO::Selector.new
|
67
|
+
|
68
|
+
@executor ||= Concurrent::ThreadPoolExecutor.new(
|
69
|
+
min_threads: 1,
|
70
|
+
max_threads: 10,
|
71
|
+
max_queue: 0,
|
72
|
+
)
|
73
|
+
|
74
|
+
@thread = Thread.new { run }
|
75
|
+
|
76
|
+
return true
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def wakeup
|
81
|
+
spawn || @nio.wakeup
|
82
|
+
end
|
83
|
+
|
84
|
+
def run
|
85
|
+
loop do
|
86
|
+
if @stopping
|
87
|
+
@nio.close
|
88
|
+
break
|
89
|
+
end
|
90
|
+
|
91
|
+
until @todo.empty?
|
92
|
+
@todo.pop(true).call
|
93
|
+
end
|
94
|
+
|
95
|
+
next unless monitors = @nio.select
|
96
|
+
|
97
|
+
monitors.each do |monitor|
|
98
|
+
io = monitor.io
|
99
|
+
stream = monitor.value
|
100
|
+
|
101
|
+
begin
|
102
|
+
if monitor.writable?
|
103
|
+
if stream.flush_write_buffer
|
104
|
+
monitor.interests = :r
|
105
|
+
end
|
106
|
+
next unless monitor.readable?
|
107
|
+
end
|
108
|
+
|
109
|
+
incoming = io.read_nonblock(4096, exception: false)
|
110
|
+
case incoming
|
111
|
+
when :wait_readable
|
112
|
+
next
|
113
|
+
when nil
|
114
|
+
stream.close
|
115
|
+
else
|
116
|
+
stream.receive incoming
|
117
|
+
end
|
118
|
+
rescue
|
119
|
+
begin
|
120
|
+
stream.close
|
121
|
+
rescue
|
122
|
+
@nio.deregister io
|
123
|
+
@map.delete io
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module RailsLiveReload
|
2
|
+
module WebSocket
|
3
|
+
# This class is basically copied from ActionCable
|
4
|
+
# https://github.com/rails/rails/blob/v7.0.3/actioncable/lib/action_cable/connection/message_buffer.rb
|
5
|
+
class MessageBuffer
|
6
|
+
def initialize(connection)
|
7
|
+
@connection = connection
|
8
|
+
@buffered_messages = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def append(message)
|
12
|
+
if valid? message
|
13
|
+
if processing?
|
14
|
+
receive message
|
15
|
+
else
|
16
|
+
buffer message
|
17
|
+
end
|
18
|
+
else
|
19
|
+
raise ArgumentError, "Couldn't handle non-string message: #{message.class}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def processing?
|
24
|
+
@processing
|
25
|
+
end
|
26
|
+
|
27
|
+
def process!
|
28
|
+
@processing = true
|
29
|
+
receive_buffered_messages
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :connection, :buffered_messages
|
35
|
+
|
36
|
+
def valid?(message)
|
37
|
+
message.is_a?(String)
|
38
|
+
end
|
39
|
+
|
40
|
+
def receive(message)
|
41
|
+
connection.receive message
|
42
|
+
end
|
43
|
+
|
44
|
+
def buffer(message)
|
45
|
+
buffered_messages << message
|
46
|
+
end
|
47
|
+
|
48
|
+
def receive_buffered_messages
|
49
|
+
receive buffered_messages.shift until buffered_messages.empty?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module RailsLiveReload
|
2
|
+
module WebSocket
|
3
|
+
# This class is basically copied from ActionCable
|
4
|
+
# https://github.com/rails/rails/blob/v7.0.3/actioncable/lib/action_cable/connection/stream.rb
|
5
|
+
class Stream
|
6
|
+
def initialize(event_loop, socket)
|
7
|
+
@event_loop = event_loop
|
8
|
+
@socket_object = socket
|
9
|
+
@stream_send = socket.env["stream.send"]
|
10
|
+
|
11
|
+
@rack_hijack_io = nil
|
12
|
+
@write_lock = Mutex.new
|
13
|
+
|
14
|
+
@write_head = nil
|
15
|
+
@write_buffer = Queue.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def each(&callback)
|
19
|
+
@stream_send ||= callback
|
20
|
+
end
|
21
|
+
|
22
|
+
def close
|
23
|
+
shutdown
|
24
|
+
@socket_object.client_gone
|
25
|
+
end
|
26
|
+
|
27
|
+
def shutdown
|
28
|
+
clean_rack_hijack
|
29
|
+
end
|
30
|
+
|
31
|
+
def write(data)
|
32
|
+
if @stream_send
|
33
|
+
return @stream_send.call(data)
|
34
|
+
end
|
35
|
+
|
36
|
+
if @write_lock.try_lock
|
37
|
+
begin
|
38
|
+
if @write_head.nil? && @write_buffer.empty?
|
39
|
+
written = @rack_hijack_io.write_nonblock(data, exception: false)
|
40
|
+
|
41
|
+
case written
|
42
|
+
when :wait_writable
|
43
|
+
when data.bytesize
|
44
|
+
return data.bytesize
|
45
|
+
else
|
46
|
+
@write_head = data.byteslice(written, data.bytesize)
|
47
|
+
@event_loop.writes_pending @rack_hijack_io
|
48
|
+
|
49
|
+
return data.bytesize
|
50
|
+
end
|
51
|
+
end
|
52
|
+
ensure
|
53
|
+
@write_lock.unlock
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
@write_buffer << data
|
58
|
+
@event_loop.writes_pending @rack_hijack_io
|
59
|
+
|
60
|
+
data.bytesize
|
61
|
+
rescue EOFError, Errno::ECONNRESET
|
62
|
+
@socket_object.client_gone
|
63
|
+
end
|
64
|
+
|
65
|
+
def flush_write_buffer
|
66
|
+
@write_lock.synchronize do
|
67
|
+
loop do
|
68
|
+
if @write_head.nil?
|
69
|
+
return true if @write_buffer.empty?
|
70
|
+
@write_head = @write_buffer.pop
|
71
|
+
end
|
72
|
+
|
73
|
+
written = @rack_hijack_io.write_nonblock(@write_head, exception: false)
|
74
|
+
case written
|
75
|
+
when :wait_writable
|
76
|
+
return false
|
77
|
+
when @write_head.bytesize
|
78
|
+
@write_head = nil
|
79
|
+
else
|
80
|
+
@write_head = @write_head.byteslice(written, @write_head.bytesize)
|
81
|
+
return false
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def receive(data)
|
88
|
+
@socket_object.parse(data)
|
89
|
+
end
|
90
|
+
|
91
|
+
def hijack_rack_socket
|
92
|
+
return unless @socket_object.env["rack.hijack"]
|
93
|
+
|
94
|
+
@rack_hijack_io = @socket_object.env["rack.hijack"].call
|
95
|
+
@rack_hijack_io ||= @socket_object.env["rack.hijack_io"]
|
96
|
+
|
97
|
+
@event_loop.attach(@rack_hijack_io, self)
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def clean_rack_hijack
|
103
|
+
return unless @rack_hijack_io
|
104
|
+
@event_loop.detach(@rack_hijack_io, self)
|
105
|
+
@rack_hijack_io = nil
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "websocket/driver"
|
2
|
+
|
3
|
+
module RailsLiveReload
|
4
|
+
module WebSocket
|
5
|
+
# This class is basically copied from ActionCable
|
6
|
+
# https://github.com/rails/rails/blob/v7.0.3/actioncable/lib/action_cable/connection/web_socket.rb
|
7
|
+
class Wrapper
|
8
|
+
delegate :transmit, :close, :protocol, :rack_response, to: :websocket
|
9
|
+
|
10
|
+
def initialize(env, event_target, event_loop, protocols: RailsLiveReload::INTERNAL[:protocols])
|
11
|
+
@websocket = ::WebSocket::Driver.websocket?(env) ? ClientSocket.new(env, event_target, event_loop, protocols) : nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def possible?
|
15
|
+
websocket
|
16
|
+
end
|
17
|
+
|
18
|
+
def alive?
|
19
|
+
websocket && websocket.alive?
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :websocket
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/rails_live_reload.rb
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
require "listen"
|
2
2
|
require "rails_live_reload/version"
|
3
3
|
require "rails_live_reload/config"
|
4
|
-
require "rails_live_reload/client"
|
5
4
|
require "rails_live_reload/watcher"
|
6
|
-
require "rails_live_reload/
|
7
|
-
require "rails_live_reload/
|
8
|
-
require "rails_live_reload/
|
5
|
+
require "rails_live_reload/server/connections"
|
6
|
+
require "rails_live_reload/server/base"
|
7
|
+
require "rails_live_reload/middleware/base"
|
9
8
|
require "rails_live_reload/instrument/metrics_collector"
|
10
9
|
require "rails_live_reload/thread/current_request"
|
11
10
|
require "rails_live_reload/checker"
|
@@ -16,10 +15,23 @@ module RailsLiveReload
|
|
16
15
|
mattr_accessor :watcher
|
17
16
|
@@watcher = {}
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
INTERNAL = {
|
19
|
+
message_types: {
|
20
|
+
welcome: "welcome",
|
21
|
+
disconnect: "disconnect",
|
22
|
+
ping: "ping",
|
23
|
+
},
|
24
|
+
disconnect_reasons: {
|
25
|
+
invalid_request: "invalid_request",
|
26
|
+
remote: "remote"
|
27
|
+
},
|
28
|
+
socket_events: {
|
29
|
+
reload: 'reload'
|
30
|
+
},
|
31
|
+
protocols: ["rails-live-reload-v1-json"].freeze
|
32
|
+
}
|
33
|
+
|
34
|
+
module_function def server
|
35
|
+
@server ||= RailsLiveReload::Server::Base.new
|
24
36
|
end
|
25
37
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_live_reload
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Igor Kasyanchuk
|
@@ -9,10 +9,10 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2022-
|
12
|
+
date: 2022-07-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
15
|
+
name: railties
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
18
|
- - ">="
|
@@ -40,41 +40,13 @@ dependencies:
|
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '0'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
|
-
name:
|
43
|
+
name: websocket-driver
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
46
|
- - ">="
|
47
47
|
- !ruby/object:Gem::Version
|
48
48
|
version: '0'
|
49
|
-
type: :
|
50
|
-
prerelease: false
|
51
|
-
version_requirements: !ruby/object:Gem::Requirement
|
52
|
-
requirements:
|
53
|
-
- - ">="
|
54
|
-
- !ruby/object:Gem::Version
|
55
|
-
version: '0'
|
56
|
-
- !ruby/object:Gem::Dependency
|
57
|
-
name: pry
|
58
|
-
requirement: !ruby/object:Gem::Requirement
|
59
|
-
requirements:
|
60
|
-
- - ">="
|
61
|
-
- !ruby/object:Gem::Version
|
62
|
-
version: '0'
|
63
|
-
type: :development
|
64
|
-
prerelease: false
|
65
|
-
version_requirements: !ruby/object:Gem::Requirement
|
66
|
-
requirements:
|
67
|
-
- - ">="
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
version: '0'
|
70
|
-
- !ruby/object:Gem::Dependency
|
71
|
-
name: pry-nav
|
72
|
-
requirement: !ruby/object:Gem::Requirement
|
73
|
-
requirements:
|
74
|
-
- - ">="
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
version: '0'
|
77
|
-
type: :development
|
49
|
+
type: :runtime
|
78
50
|
prerelease: false
|
79
51
|
version_requirements: !ruby/object:Gem::Requirement
|
80
52
|
requirements:
|
@@ -82,13 +54,13 @@ dependencies:
|
|
82
54
|
- !ruby/object:Gem::Version
|
83
55
|
version: '0'
|
84
56
|
- !ruby/object:Gem::Dependency
|
85
|
-
name:
|
57
|
+
name: nio4r
|
86
58
|
requirement: !ruby/object:Gem::Requirement
|
87
59
|
requirements:
|
88
60
|
- - ">="
|
89
61
|
- !ruby/object:Gem::Version
|
90
62
|
version: '0'
|
91
|
-
type: :
|
63
|
+
type: :runtime
|
92
64
|
prerelease: false
|
93
65
|
version_requirements: !ruby/object:Gem::Requirement
|
94
66
|
requirements:
|
@@ -123,17 +95,23 @@ files:
|
|
123
95
|
- Rakefile
|
124
96
|
- lib/rails_live_reload.rb
|
125
97
|
- lib/rails_live_reload/checker.rb
|
126
|
-
- lib/rails_live_reload/client.rb
|
127
98
|
- lib/rails_live_reload/command.rb
|
128
99
|
- lib/rails_live_reload/config.rb
|
129
100
|
- lib/rails_live_reload/engine.rb
|
130
101
|
- lib/rails_live_reload/instrument/metrics_collector.rb
|
131
|
-
- lib/rails_live_reload/
|
132
|
-
- lib/rails_live_reload/
|
133
|
-
- lib/rails_live_reload/
|
102
|
+
- lib/rails_live_reload/javascript/websocket.js
|
103
|
+
- lib/rails_live_reload/middleware/base.rb
|
104
|
+
- lib/rails_live_reload/server/base.rb
|
105
|
+
- lib/rails_live_reload/server/connections.rb
|
134
106
|
- lib/rails_live_reload/thread/current_request.rb
|
135
107
|
- lib/rails_live_reload/version.rb
|
136
108
|
- lib/rails_live_reload/watcher.rb
|
109
|
+
- lib/rails_live_reload/web_socket/base.rb
|
110
|
+
- lib/rails_live_reload/web_socket/client_socket.rb
|
111
|
+
- lib/rails_live_reload/web_socket/event_loop.rb
|
112
|
+
- lib/rails_live_reload/web_socket/message_buffer.rb
|
113
|
+
- lib/rails_live_reload/web_socket/stream.rb
|
114
|
+
- lib/rails_live_reload/web_socket/wrapper.rb
|
137
115
|
homepage: https://github.com/railsjazz/rails_live_reload
|
138
116
|
licenses:
|
139
117
|
- MIT
|
@@ -1,75 +0,0 @@
|
|
1
|
-
module RailsLiveReload
|
2
|
-
module Client
|
3
|
-
|
4
|
-
def Client.long_polling_js
|
5
|
-
<<~HTML.html_safe
|
6
|
-
<script>
|
7
|
-
(function() {
|
8
|
-
const files = #{CurrentRequest.current.data.to_a.to_json};
|
9
|
-
let retries_count = 0;
|
10
|
-
function poll() {
|
11
|
-
const formData = new FormData();
|
12
|
-
formData.append('dt', #{Time.now.to_i})
|
13
|
-
formData.append('files', JSON.stringify(files))
|
14
|
-
|
15
|
-
fetch(
|
16
|
-
"#{RailsLiveReload.config.url}",
|
17
|
-
{
|
18
|
-
method: "post",
|
19
|
-
headers: { 'Accept': 'application/json', },
|
20
|
-
body: formData
|
21
|
-
}
|
22
|
-
)
|
23
|
-
.then(response => response.json())
|
24
|
-
.then(data => {
|
25
|
-
retries_count = 0;
|
26
|
-
if(data['command'] === 'RELOAD') {
|
27
|
-
window.location.reload();
|
28
|
-
} else {
|
29
|
-
poll();
|
30
|
-
}
|
31
|
-
}).catch(() => {
|
32
|
-
retries_count++;
|
33
|
-
|
34
|
-
if(retries_count < 10) {
|
35
|
-
setTimeout(poll, 5000)
|
36
|
-
}
|
37
|
-
})
|
38
|
-
}
|
39
|
-
poll();
|
40
|
-
})();
|
41
|
-
</script>
|
42
|
-
HTML
|
43
|
-
end
|
44
|
-
|
45
|
-
def Client.polling_js
|
46
|
-
<<~HTML.html_safe
|
47
|
-
<script>
|
48
|
-
const files = #{CurrentRequest.current.data.to_a.to_json};
|
49
|
-
const timer = setInterval(
|
50
|
-
() => {
|
51
|
-
const formData = new FormData();
|
52
|
-
formData.append('dt', #{Time.now.to_i})
|
53
|
-
formData.append('files', JSON.stringify(files))
|
54
|
-
fetch(
|
55
|
-
"#{RailsLiveReload.config.url}",
|
56
|
-
{
|
57
|
-
method: "post",
|
58
|
-
headers: { 'Accept': 'application/json', },
|
59
|
-
body: formData
|
60
|
-
}
|
61
|
-
)
|
62
|
-
.then(response => response.json())
|
63
|
-
.then(data => {
|
64
|
-
if(data['command'] === 'RELOAD') {
|
65
|
-
clearInterval(timer);
|
66
|
-
window.location.reload();
|
67
|
-
}
|
68
|
-
})
|
69
|
-
}, #{RailsLiveReload.config.polling_interval}
|
70
|
-
)
|
71
|
-
</script>
|
72
|
-
HTML
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|