rails_live_reload 0.1.1 → 0.3.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 +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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 88846a7af50ff0f2941eeb26795f7156ef4c92b8397453d9810d0196a27851ba
|
4
|
+
data.tar.gz: 70337f1ce69e0fab74a7272633629a565f6106a86e00590e4eb0f2d49e71281e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c4a756e0c3cd05ae4e8d75c5d221d2bd18ec17bd36d70ad8b720244f849a058d3788ec401d5f53eb88e99d0cfe8c5f09afd8610acaaf60e194b162465eb6bcf
|
7
|
+
data.tar.gz: e23b0b4b83ed08589cd9a11066419e91fb58f4d3cf2dff4724de276408877b328829b96c598edaf77f9c90b251d8993486472bdde4a7d1bd88f5ed7c27c5e68c
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
|
7
7
|
This is the simplest and probably the most robust way to add live reloading to your Rails app.
|
8
8
|
|
9
|
-
Just add the gem and thats it, now you have a live reloading.
|
9
|
+
Just add the gem and thats it, now you have a live reloading. **You don't need anything other than this gem for live reloading to work**.
|
10
10
|
|
11
11
|
Works with:
|
12
12
|
|
@@ -40,25 +40,12 @@ $ bundle
|
|
40
40
|
|
41
41
|
## Configuration
|
42
42
|
|
43
|
-
### There are two modes:
|
44
|
-
1. `:long_polling` - This is a default mode, it uses [long polling](https://javascript.info/long-polling) techunique, client opens a connection that will hang until either change is detected, or timeout happens, if later, a new connection is oppened
|
45
|
-
2. `:polling` - This mode will use regular polling to detect changes, you can configure custom `polling_interval` (default is 100ms). We recommend using `:long_polling` as it makes much less requests to the server.
|
46
|
-
|
47
43
|
### Create initializer `config/initializers/rails_live_reload.rb`:
|
48
44
|
|
49
45
|
|
50
46
|
```ruby
|
51
47
|
RailsLiveReload.configure do |config|
|
52
|
-
# config.url
|
53
|
-
# Available modes are: :long_polling (default) and :polling
|
54
|
-
# config.mode = :long_polling
|
55
|
-
|
56
|
-
# This is used with :long_polling mode
|
57
|
-
# config.timeout = 30
|
58
|
-
# config.long_polling_sleep_duration = 0.1
|
59
|
-
|
60
|
-
# This is used with :polling mode
|
61
|
-
# config.polling_interval = 100
|
48
|
+
# config.url = "/rails/live/reload"
|
62
49
|
|
63
50
|
# Default watched folders & files
|
64
51
|
# config.watch %r{app/views/.+\.(erb|haml|slim)$}
|
@@ -67,6 +54,8 @@ RailsLiveReload.configure do |config|
|
|
67
54
|
# More examples:
|
68
55
|
# config.watch %r{app/helpers/.+\.rb}, reload: :always
|
69
56
|
# config.watch %r{config/locales/.+\.yml}, reload: :always
|
57
|
+
|
58
|
+
# config.enabled = Rails.env.development?
|
70
59
|
end if defined?(RailsLiveReload)
|
71
60
|
```
|
72
61
|
|
@@ -76,7 +65,7 @@ There are 3 main parts:
|
|
76
65
|
|
77
66
|
1) listener of file changes (using `listen` gem)
|
78
67
|
2) collector of rendered views (see rails instrumentation)
|
79
|
-
3)
|
68
|
+
3) JavaScript client that communicates with server and triggers reloading when needed
|
80
69
|
|
81
70
|
## Notes
|
82
71
|
|
@@ -89,15 +78,13 @@ You are welcome to contribute. See list of `TODO's` below.
|
|
89
78
|
## TODO
|
90
79
|
|
91
80
|
- reload CSS without reloading the whole page?
|
92
|
-
- add `:websocket` mode?
|
93
81
|
- smarter reload if there is a change in helper (check methods from rendered views?)
|
94
82
|
- generator for initializer
|
95
83
|
- more complex rules? e.g. if "user.rb" file is changed - reload all pages with rendered "users" views
|
96
84
|
- check with older Rails versions
|
97
85
|
- tests or specs
|
98
86
|
- CI (github actions)
|
99
|
-
-
|
100
|
-
- improve work with turbo, turbolinks
|
87
|
+
- auto reload when rendered controller was changed
|
101
88
|
|
102
89
|
## License
|
103
90
|
|
@@ -1,11 +1,18 @@
|
|
1
1
|
module RailsLiveReload
|
2
2
|
class Checker
|
3
|
+
def self.files
|
4
|
+
@files
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.files=(files)
|
8
|
+
@files = files
|
9
|
+
end
|
3
10
|
|
4
11
|
def self.scan(dt, rendered_files)
|
5
12
|
temp = []
|
6
13
|
|
7
14
|
# all changed files
|
8
|
-
|
15
|
+
files.each do |file, fdt|
|
9
16
|
temp << file if fdt && fdt > dt
|
10
17
|
end
|
11
18
|
|
@@ -25,6 +32,5 @@ module RailsLiveReload
|
|
25
32
|
|
26
33
|
result
|
27
34
|
end
|
28
|
-
|
29
35
|
end
|
30
|
-
end
|
36
|
+
end
|
@@ -19,17 +19,13 @@ module RailsLiveReload
|
|
19
19
|
|
20
20
|
class Config
|
21
21
|
attr_reader :patterns
|
22
|
-
attr_accessor :
|
22
|
+
attr_accessor :url, :watcher, :files, :enabled
|
23
23
|
|
24
24
|
def initialize
|
25
|
-
@mode = :long_polling
|
26
|
-
@timeout = 30
|
27
|
-
@long_polling_sleep_duration = 0.1
|
28
|
-
@polling_interval = 100
|
29
25
|
@url = "/rails/live/reload"
|
30
26
|
@watcher = nil
|
31
27
|
@files = {}
|
32
|
-
@enabled = ::Rails.env.development?
|
28
|
+
@enabled = ::Rails.env.development?
|
33
29
|
|
34
30
|
# These configs work for 95% apps, see README for more info
|
35
31
|
@patterns = {
|
@@ -39,6 +35,10 @@ module RailsLiveReload
|
|
39
35
|
@default_patterns_changed = false
|
40
36
|
end
|
41
37
|
|
38
|
+
def root_path
|
39
|
+
@root_path ||= ::Rails.application.root
|
40
|
+
end
|
41
|
+
|
42
42
|
def watch(pattern, reload: :on_change)
|
43
43
|
unless @default_patterns_changed
|
44
44
|
@default_patterns_changed = true
|
@@ -47,5 +47,9 @@ module RailsLiveReload
|
|
47
47
|
|
48
48
|
patterns[pattern] = reload
|
49
49
|
end
|
50
|
+
|
51
|
+
def socket_path
|
52
|
+
root_path.join('tmp/sockets/rails_live_reload.sock')
|
53
|
+
end
|
50
54
|
end
|
51
55
|
end
|
@@ -1,32 +1,41 @@
|
|
1
1
|
module RailsLiveReload
|
2
2
|
class Railtie < ::Rails::Engine
|
3
|
-
|
4
|
-
if RailsLiveReload.enabled?
|
3
|
+
if RailsLiveReload.enabled? && defined?(::Rails::Server)
|
5
4
|
initializer "rails_live_reload.middleware" do |app|
|
6
5
|
if ::Rails::VERSION::MAJOR.to_i >= 5
|
7
|
-
app.middleware.insert_after ActionDispatch::Executor, RailsLiveReload
|
6
|
+
app.middleware.insert_after ActionDispatch::Executor, RailsLiveReload::Middleware::Base
|
8
7
|
else
|
9
8
|
begin
|
10
|
-
app.middleware.insert_after ActionDispatch::Static, RailsLiveReload
|
9
|
+
app.middleware.insert_after ActionDispatch::Static, RailsLiveReload::Middleware::Base
|
11
10
|
rescue
|
12
|
-
app.middleware.insert_after Rack::SendFile, RailsLiveReload
|
11
|
+
app.middleware.insert_after Rack::SendFile, RailsLiveReload::Middleware::Base
|
13
12
|
end
|
14
13
|
end
|
14
|
+
end
|
15
15
|
|
16
|
+
initializer "rails_live_reload.watcher" do
|
16
17
|
RailsLiveReload::Watcher.init
|
17
18
|
end
|
18
19
|
|
19
|
-
initializer
|
20
|
+
initializer "rails_live_reload.configure_metrics", after: :initialize_logger do
|
20
21
|
ActiveSupport::Notifications.subscribe(
|
21
22
|
/\.action_view/,
|
22
23
|
RailsLiveReload::Instrument::MetricsCollector.new
|
23
24
|
)
|
24
25
|
end
|
25
26
|
|
26
|
-
initializer
|
27
|
+
initializer "rails_live_reload.reset_current_request", after: :initialize_logger do |app|
|
27
28
|
app.executor.to_run { CurrentRequest.cleanup }
|
28
29
|
app.executor.to_complete { CurrentRequest.cleanup }
|
29
30
|
end
|
31
|
+
|
32
|
+
initializer "rails_live_reload.routes" do
|
33
|
+
config.after_initialize do |app|
|
34
|
+
app.routes.prepend do
|
35
|
+
mount RailsLiveReload.server => RailsLiveReload.config.url, internal: true, anchor: true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
30
39
|
end
|
31
40
|
end
|
32
41
|
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
/*!
|
2
|
+
Rails Live Reload 0.3.0
|
3
|
+
Copyright © 2022 RailsJazz
|
4
|
+
https://railsjazz.com
|
5
|
+
*/
|
6
|
+
var RailsLiveReload=function(){"use strict";const t="RELOAD",e=["rails-live-reload-v1-json"];class n{static _instance;static get instance(){return n._instance||(n._instance=new this),n._instance}static start(){this.instance.start()}constructor(){this.initialize(),document.addEventListener("turbo:render",(()=>{document.documentElement.hasAttribute("data-turbo-preview")||this.restart()})),document.addEventListener("turbolinks:render",(()=>{document.documentElement.hasAttribute("data-turbolinks-preview")||this.restart()}))}initialize(){const{files:t,time:e,url:n}=JSON.parse(this.optionsNode.textContent);this.files=t,this.time=e,this.url=n}fullReload(){window.location.reload()}get optionsNode(){const t=document.getElementById("rails-live-reload-options");if(!t)throw"Unable to find RailsLiveReload options";return t}start(){this.connection||(this.connection=new WebSocket(function(t){if("function"==typeof t&&(t=t()),t&&!/^wss?:/i.test(t)){const e=document.createElement("a");return e.href=t,e.href=e.href,e.protocol=e.protocol.replace("http","ws"),e.href}return t}(this.url),e),this.connection.onmessage=this.handleMessage.bind(this),this.connection.onopen=this.handleConnectionOpen.bind(this),this.connection.onclose=this.handleConnectionClosed.bind(this))}stop(){this.connection.close()}restart(){this.initialize(),this.setupConnection()}setupConnection(){this.connection.send(JSON.stringify({event:"setup",options:{files:this.files,dt:this.time}}))}handleConnectionOpen(t){this.retriesCount=0,this.setupConnection()}handleMessage(e){JSON.parse(e.data).command===t&&this.fullReload()}handleConnectionClosed(t){this.connection=void 0,!t.wasClean&&this.retriesCount<=10&&(this.retriesCount++,setTimeout((()=>{this.start()}),1e3*this.retriesCount))}}return document.addEventListener("DOMContentLoaded",(()=>{n.start()})),n}();
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module RailsLiveReload
|
2
|
+
module Middleware
|
3
|
+
class Base
|
4
|
+
def initialize(app)
|
5
|
+
@app = app
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
dup.call!(env)
|
10
|
+
end
|
11
|
+
|
12
|
+
def call!(env)
|
13
|
+
if env["REQUEST_PATH"].starts_with?(RailsLiveReload.config.url)
|
14
|
+
::Rails.logger.silence do
|
15
|
+
@app.call(env)
|
16
|
+
end
|
17
|
+
else
|
18
|
+
request = Rack::Request.new(env)
|
19
|
+
status, headers, response = @app.call(env)
|
20
|
+
|
21
|
+
if html?(headers) && 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
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def make_new_response(body)
|
34
|
+
body = body.sub("</head>", <<~HTML.html_safe)
|
35
|
+
<script defer type="text/javascript" src="#{RailsLiveReload.config.url}/script"></script>
|
36
|
+
</head>
|
37
|
+
HTML
|
38
|
+
body.sub("</body>", <<~HTML.html_safe)
|
39
|
+
<script id="rails-live-reload-options" type="application/json">
|
40
|
+
#{{
|
41
|
+
files: CurrentRequest.current.data.to_a,
|
42
|
+
time: Time.now.to_i,
|
43
|
+
url: RailsLiveReload.config.url
|
44
|
+
}.to_json}
|
45
|
+
</script>
|
46
|
+
</body>
|
47
|
+
HTML
|
48
|
+
end
|
49
|
+
|
50
|
+
def html?(headers)
|
51
|
+
headers["Content-Type"].to_s.include?("text/html")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'rails_live_reload/web_socket/event_loop'
|
2
|
+
require 'rails_live_reload/web_socket/message_buffer'
|
3
|
+
require 'rails_live_reload/web_socket/wrapper'
|
4
|
+
require 'rails_live_reload/web_socket/client_socket'
|
5
|
+
require 'rails_live_reload/web_socket/stream'
|
6
|
+
require 'rails_live_reload/web_socket/base'
|
7
|
+
|
8
|
+
module RailsLiveReload
|
9
|
+
module Server
|
10
|
+
# This class is based on ActionCable
|
11
|
+
# https://github.com/rails/rails/blob/v7.0.3/actioncable/lib/action_cable/server/base.rb
|
12
|
+
class Base
|
13
|
+
include RailsLiveReload::Server::Connections
|
14
|
+
|
15
|
+
attr_reader :mutex
|
16
|
+
|
17
|
+
def reload_all
|
18
|
+
@mutex.synchronize do
|
19
|
+
connections.each(&:reload)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@mutex = Monitor.new
|
25
|
+
@event_loop = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
# Called by Rack to set up the server.
|
29
|
+
def call(env)
|
30
|
+
case env["REQUEST_PATH"]
|
31
|
+
when RailsLiveReload.config.url
|
32
|
+
setup_socket
|
33
|
+
setup_heartbeat_timer
|
34
|
+
request = Rack::Request.new(env)
|
35
|
+
RailsLiveReload::WebSocket::Base.new(self, request).process
|
36
|
+
when "#{RailsLiveReload.config.url}/script"
|
37
|
+
content = client_javascript
|
38
|
+
[200, {'Content-Type' => 'application/javascript', 'Content-Length' => content.size.to_s, 'Cache-Control' => 'no-store'}, [content]]
|
39
|
+
else
|
40
|
+
raise ActionController::RoutingError, 'Not found'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def client_javascript
|
45
|
+
@client_javascript || @mutex.synchronize { @client_javascript ||= File.open(File.join(File.dirname(__FILE__), "../javascript/websocket.js")).read }
|
46
|
+
end
|
47
|
+
|
48
|
+
def event_loop
|
49
|
+
@event_loop || @mutex.synchronize { @event_loop ||= RailsLiveReload::WebSocket::EventLoop.new }
|
50
|
+
end
|
51
|
+
|
52
|
+
def setup_socket
|
53
|
+
@socket ||= UNIXSocket.open(RailsLiveReload.config.socket_path).tap do |socket|
|
54
|
+
Thread.new do
|
55
|
+
loop do
|
56
|
+
data = JSON.parse socket.readline
|
57
|
+
|
58
|
+
case data["event"]
|
59
|
+
when RailsLiveReload::INTERNAL[:socket_events][:reload]
|
60
|
+
RailsLiveReload::Checker.files = data['files']
|
61
|
+
|
62
|
+
reload_all
|
63
|
+
else
|
64
|
+
raise NotImplementedError
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module RailsLiveReload
|
2
|
+
module Server
|
3
|
+
# This class is strongly based on ActionCable
|
4
|
+
# https://github.com/rails/rails/blob/v7.0.3/actioncable/lib/action_cable/server/connections.rb
|
5
|
+
module Connections
|
6
|
+
BEAT_INTERVAL = 3
|
7
|
+
|
8
|
+
def connections
|
9
|
+
@connections || @mutex.synchronize { @connections ||= Concurrent::Array.new }
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_connection(connection)
|
13
|
+
connections << connection
|
14
|
+
end
|
15
|
+
|
16
|
+
def remove_connection(connection)
|
17
|
+
connections.delete connection
|
18
|
+
end
|
19
|
+
|
20
|
+
def setup_heartbeat_timer
|
21
|
+
@heartbeat_timer ||= event_loop.timer(BEAT_INTERVAL) do
|
22
|
+
event_loop.post { connections.each(&:beat) }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -1,6 +1,10 @@
|
|
1
1
|
module RailsLiveReload
|
2
2
|
class Watcher
|
3
|
-
attr_reader :
|
3
|
+
attr_reader :files, :sockets
|
4
|
+
|
5
|
+
def root
|
6
|
+
RailsLiveReload.config.root_path
|
7
|
+
end
|
4
8
|
|
5
9
|
def Watcher.init
|
6
10
|
watcher = new
|
@@ -8,15 +12,16 @@ module RailsLiveReload
|
|
8
12
|
end
|
9
13
|
|
10
14
|
def initialize
|
11
|
-
@root = ::Rails.application.root
|
12
15
|
@files = {}
|
16
|
+
@sockets = []
|
13
17
|
|
14
18
|
puts "Watching: #{root}"
|
15
19
|
RailsLiveReload.patterns.each do |pattern, rule|
|
16
|
-
puts " #{pattern} => #{rule}"
|
20
|
+
puts " #{pattern} => #{rule}"
|
17
21
|
end
|
18
22
|
|
19
23
|
build_tree
|
24
|
+
start_socket
|
20
25
|
start_listener
|
21
26
|
end
|
22
27
|
|
@@ -27,6 +32,7 @@ module RailsLiveReload
|
|
27
32
|
all.each do |file|
|
28
33
|
files[file] = File.mtime(file).to_i rescue nil
|
29
34
|
end
|
35
|
+
reload_all
|
30
36
|
end
|
31
37
|
listener.start
|
32
38
|
end
|
@@ -37,5 +43,35 @@ module RailsLiveReload
|
|
37
43
|
files[file] = File.mtime(file).to_i rescue nil
|
38
44
|
end
|
39
45
|
end
|
46
|
+
|
47
|
+
def reload_all
|
48
|
+
data = {
|
49
|
+
event: RailsLiveReload::INTERNAL[:socket_events][:reload],
|
50
|
+
files: files
|
51
|
+
}.to_json
|
52
|
+
|
53
|
+
@sockets.each do |socket, _|
|
54
|
+
socket.puts data
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def start_socket
|
59
|
+
Thread.new do
|
60
|
+
Socket.unix_server_socket(RailsLiveReload.config.socket_path.to_s) do |sock|
|
61
|
+
loop do
|
62
|
+
socket, _ = sock.accept
|
63
|
+
sockets << socket
|
64
|
+
Thread.new do
|
65
|
+
begin
|
66
|
+
socket.eof
|
67
|
+
ensure
|
68
|
+
socket.close
|
69
|
+
sockets.delete socket
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
40
76
|
end
|
41
77
|
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module RailsLiveReload
|
2
|
+
module WebSocket
|
3
|
+
# This class is strongly based on ActionCable
|
4
|
+
# https://github.com/rails/rails/blob/v7.0.3/actioncable/lib/action_cable/connection/base.rb
|
5
|
+
class Base
|
6
|
+
attr_reader :server, :env, :protocol, :request
|
7
|
+
attr_reader :dt, :files
|
8
|
+
|
9
|
+
delegate :event_loop, to: :server
|
10
|
+
|
11
|
+
def initialize(server, request)
|
12
|
+
@server, @request = server, request
|
13
|
+
@env = request.env
|
14
|
+
@websocket = RailsLiveReload::WebSocket::Wrapper.new(env, self, event_loop)
|
15
|
+
@message_buffer = RailsLiveReload::WebSocket::MessageBuffer.new(self)
|
16
|
+
end
|
17
|
+
|
18
|
+
def process
|
19
|
+
if websocket.possible?
|
20
|
+
respond_to_successful_request
|
21
|
+
else
|
22
|
+
respond_to_invalid_request
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def receive(websocket_message)
|
27
|
+
if websocket.alive?
|
28
|
+
handle_channel_command decode(websocket_message)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def handle_channel_command(payload)
|
33
|
+
case payload['event']
|
34
|
+
when 'setup'
|
35
|
+
setup payload['options']
|
36
|
+
else
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def reload
|
42
|
+
return if dt.nil? || files.nil? || RailsLiveReload::Checker.scan(dt, files).size.zero?
|
43
|
+
|
44
|
+
transmit({command: "RELOAD"})
|
45
|
+
end
|
46
|
+
|
47
|
+
def transmit(cable_message)
|
48
|
+
websocket.transmit encode(cable_message)
|
49
|
+
end
|
50
|
+
|
51
|
+
def close(reason: nil)
|
52
|
+
transmit({
|
53
|
+
type: RailsLiveReload::INTERNAL[:message_types][:disconnect],
|
54
|
+
reason: reason
|
55
|
+
})
|
56
|
+
websocket.close
|
57
|
+
end
|
58
|
+
|
59
|
+
def beat
|
60
|
+
transmit type: RailsLiveReload::INTERNAL[:message_types][:ping], message: Time.now.to_i
|
61
|
+
end
|
62
|
+
|
63
|
+
def on_open
|
64
|
+
@protocol = websocket.protocol
|
65
|
+
send_welcome_message
|
66
|
+
|
67
|
+
message_buffer.process!
|
68
|
+
server.add_connection(self)
|
69
|
+
end
|
70
|
+
|
71
|
+
def on_message(message)
|
72
|
+
message_buffer.append message
|
73
|
+
end
|
74
|
+
|
75
|
+
def on_error(message)
|
76
|
+
raise message
|
77
|
+
end
|
78
|
+
|
79
|
+
def on_close(reason, code)
|
80
|
+
server.remove_connection(self)
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
attr_reader :websocket
|
86
|
+
attr_reader :message_buffer
|
87
|
+
|
88
|
+
def setup(options)
|
89
|
+
@dt = options['dt']
|
90
|
+
@files = options['files']
|
91
|
+
end
|
92
|
+
|
93
|
+
def encode(message)
|
94
|
+
message.to_json
|
95
|
+
end
|
96
|
+
|
97
|
+
def decode(message)
|
98
|
+
JSON.parse message
|
99
|
+
end
|
100
|
+
|
101
|
+
def send_welcome_message
|
102
|
+
transmit type: RailsLiveReload::INTERNAL[:message_types][:welcome]
|
103
|
+
end
|
104
|
+
|
105
|
+
def respond_to_successful_request
|
106
|
+
websocket.rack_response
|
107
|
+
end
|
108
|
+
|
109
|
+
def respond_to_invalid_request
|
110
|
+
close(reason: RailsLiveReload::INTERNAL[:disconnect_reasons][:invalid_request]) if websocket.alive?
|
111
|
+
|
112
|
+
[ 404, { "Content-Type" => "text/plain" }, [ "Page not found" ] ]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|