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
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
|