rails_live_reload 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +16 -10
- data/lib/rails_live_reload/config.rb +37 -5
- data/lib/rails_live_reload/engine.rb +1 -1
- data/lib/rails_live_reload/javascript/long_polling.js +6 -0
- data/lib/rails_live_reload/javascript/polling.js +6 -0
- data/lib/rails_live_reload/javascript/websocket.js +6 -0
- data/lib/rails_live_reload/middleware/base.rb +75 -0
- data/lib/rails_live_reload/middleware/long_polling.rb +45 -0
- data/lib/rails_live_reload/middleware/polling.rb +19 -0
- data/lib/rails_live_reload/middleware/web_socket.rb +51 -0
- data/lib/rails_live_reload/version.rb +1 -1
- data/lib/rails_live_reload/watcher.rb +1 -0
- 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 +20 -12
- metadata +21 -40
- 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 -53
- data/lib/rails_live_reload/rails/middleware/polling.rb +0 -21
@@ -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,11 @@
|
|
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/middleware/base"
|
6
|
+
require "rails_live_reload/middleware/long_polling"
|
7
|
+
require "rails_live_reload/middleware/web_socket"
|
8
|
+
require "rails_live_reload/middleware/polling"
|
9
9
|
require "rails_live_reload/instrument/metrics_collector"
|
10
10
|
require "rails_live_reload/thread/current_request"
|
11
11
|
require "rails_live_reload/checker"
|
@@ -16,16 +16,24 @@ module RailsLiveReload
|
|
16
16
|
mattr_accessor :watcher
|
17
17
|
@@watcher = {}
|
18
18
|
|
19
|
+
INTERNAL = {
|
20
|
+
message_types: {
|
21
|
+
welcome: "welcome",
|
22
|
+
disconnect: "disconnect",
|
23
|
+
ping: "ping",
|
24
|
+
},
|
25
|
+
disconnect_reasons: {
|
26
|
+
invalid_request: "invalid_request",
|
27
|
+
remote: "remote"
|
28
|
+
},
|
29
|
+
protocols: ["rails-live-reload-v1-json"].freeze
|
30
|
+
}
|
31
|
+
|
19
32
|
def self.middleware
|
20
33
|
case config.mode
|
21
|
-
when :polling then RailsLiveReload::
|
22
|
-
when :long_polling then RailsLiveReload::
|
34
|
+
when :polling then RailsLiveReload::Middleware::Polling
|
35
|
+
when :long_polling then RailsLiveReload::Middleware::LongPolling
|
36
|
+
when :websocket then RailsLiveReload::Middleware::WebSocket
|
23
37
|
end
|
24
38
|
end
|
25
39
|
end
|
26
|
-
|
27
|
-
# These configs work for 95% apps, see README for more info
|
28
|
-
RailsLiveReload.configure do |config|
|
29
|
-
config.watch %r{app/views/.+\.(erb|haml|slim)$}
|
30
|
-
config.watch %r{(app|vendor)/(assets|javascript)/\w+/(.+\.(css|js|html|png|jpg|ts|jsx)).*}, reload: :always
|
31
|
-
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.2.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-09 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,26 @@ 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/long_polling.js
|
103
|
+
- lib/rails_live_reload/javascript/polling.js
|
104
|
+
- lib/rails_live_reload/javascript/websocket.js
|
105
|
+
- lib/rails_live_reload/middleware/base.rb
|
106
|
+
- lib/rails_live_reload/middleware/long_polling.rb
|
107
|
+
- lib/rails_live_reload/middleware/polling.rb
|
108
|
+
- lib/rails_live_reload/middleware/web_socket.rb
|
134
109
|
- lib/rails_live_reload/thread/current_request.rb
|
135
110
|
- lib/rails_live_reload/version.rb
|
136
111
|
- lib/rails_live_reload/watcher.rb
|
112
|
+
- lib/rails_live_reload/web_socket/base.rb
|
113
|
+
- lib/rails_live_reload/web_socket/client_socket.rb
|
114
|
+
- lib/rails_live_reload/web_socket/event_loop.rb
|
115
|
+
- lib/rails_live_reload/web_socket/message_buffer.rb
|
116
|
+
- lib/rails_live_reload/web_socket/stream.rb
|
117
|
+
- lib/rails_live_reload/web_socket/wrapper.rb
|
137
118
|
homepage: https://github.com/railsjazz/rails_live_reload
|
138
119
|
licenses:
|
139
120
|
- MIT
|
@@ -153,7 +134,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
153
134
|
- !ruby/object:Gem::Version
|
154
135
|
version: '0'
|
155
136
|
requirements: []
|
156
|
-
rubygems_version: 3.
|
137
|
+
rubygems_version: 3.3.7
|
157
138
|
signing_key:
|
158
139
|
specification_version: 4
|
159
140
|
summary: Ruby on Rails Live Reload
|
@@ -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
|
@@ -1,63 +0,0 @@
|
|
1
|
-
module RailsLiveReload
|
2
|
-
module Rails
|
3
|
-
module Middleware
|
4
|
-
class Base
|
5
|
-
def initialize(app)
|
6
|
-
@app = app
|
7
|
-
end
|
8
|
-
|
9
|
-
def call(env)
|
10
|
-
dup.call!(env)
|
11
|
-
end
|
12
|
-
|
13
|
-
def call!(env)
|
14
|
-
request = Rack::Request.new(env)
|
15
|
-
|
16
|
-
if env["REQUEST_PATH"] == RailsLiveReload.config.url
|
17
|
-
rails_live_response(request)
|
18
|
-
else
|
19
|
-
@status, @headers, @response = @app.call(env)
|
20
|
-
|
21
|
-
if html? && @response.respond_to?(:[]) && (@status == 500 || (@status.to_s =~ /20./ && request.get?))
|
22
|
-
new_response = make_new_response(@response[0])
|
23
|
-
@headers['Content-Length'] = new_response.bytesize.to_s
|
24
|
-
@response = [new_response]
|
25
|
-
end
|
26
|
-
|
27
|
-
[@status, @headers, @response]
|
28
|
-
end
|
29
|
-
rescue Exception => ex
|
30
|
-
puts ex.message
|
31
|
-
puts ex.backtrace.take(10)
|
32
|
-
raise ex
|
33
|
-
end
|
34
|
-
|
35
|
-
private
|
36
|
-
|
37
|
-
def rails_live_response(request)
|
38
|
-
raise NotImplementedError
|
39
|
-
end
|
40
|
-
|
41
|
-
def client_javascript(request)
|
42
|
-
raise NotImplementedError
|
43
|
-
end
|
44
|
-
|
45
|
-
def make_new_response(body)
|
46
|
-
body.sub("</head>", "</head>#{client_javascript}")
|
47
|
-
end
|
48
|
-
|
49
|
-
def html?
|
50
|
-
@headers["Content-Type"].to_s.include?("text/html")
|
51
|
-
end
|
52
|
-
|
53
|
-
def new_thread
|
54
|
-
Thread.new {
|
55
|
-
t2 = Thread.current
|
56
|
-
t2.abort_on_exception = true
|
57
|
-
yield
|
58
|
-
}
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
@@ -1,53 +0,0 @@
|
|
1
|
-
module RailsLiveReload
|
2
|
-
module Rails
|
3
|
-
module Middleware
|
4
|
-
class LongPolling < Base
|
5
|
-
private
|
6
|
-
|
7
|
-
POLLING_SLEEP = 0.1
|
8
|
-
|
9
|
-
def rails_live_response(request)
|
10
|
-
params = request.params
|
11
|
-
body = lambda do |stream|
|
12
|
-
new_thread do
|
13
|
-
counter = 0
|
14
|
-
|
15
|
-
loop do
|
16
|
-
command = RailsLiveReload::Command.new(params)
|
17
|
-
|
18
|
-
if command.reload?
|
19
|
-
stream.write(command.payload.to_json) and break
|
20
|
-
end
|
21
|
-
|
22
|
-
sleep(POLLING_SLEEP)
|
23
|
-
counter += 1
|
24
|
-
|
25
|
-
stream.write(command.payload.to_json) and break if counter >= max_sleeps_count
|
26
|
-
end
|
27
|
-
ensure
|
28
|
-
stream.close
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
[ 200, { 'Content-Type' => 'application/json', 'rack.hijack' => body }, nil ]
|
33
|
-
end
|
34
|
-
|
35
|
-
def client_javascript
|
36
|
-
RailsLiveReload::Client.long_polling_js
|
37
|
-
end
|
38
|
-
|
39
|
-
def max_sleeps_count
|
40
|
-
RailsLiveReload.config.timeout * (1 / POLLING_SLEEP)
|
41
|
-
end
|
42
|
-
|
43
|
-
def new_thread
|
44
|
-
Thread.new {
|
45
|
-
t2 = Thread.current
|
46
|
-
t2.abort_on_exception = true
|
47
|
-
yield
|
48
|
-
}
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
module RailsLiveReload
|
2
|
-
module Rails
|
3
|
-
module Middleware
|
4
|
-
class Polling < Base
|
5
|
-
private
|
6
|
-
|
7
|
-
def rails_live_response(request)
|
8
|
-
[
|
9
|
-
200,
|
10
|
-
{ 'Content-Type' => 'application/json' },
|
11
|
-
[ RailsLiveReload::Command.new(request.params).payload.to_json ]
|
12
|
-
]
|
13
|
-
end
|
14
|
-
|
15
|
-
def client_javascript
|
16
|
-
RailsLiveReload::Client.polling_js
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|