rails_live_reload 0.2.0 → 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 +2 -17
- data/lib/rails_live_reload/checker.rb +9 -3
- data/lib/rails_live_reload/config.rb +7 -23
- data/lib/rails_live_reload/engine.rb +15 -6
- data/lib/rails_live_reload/javascript/websocket.js +2 -2
- data/lib/rails_live_reload/middleware/base.rb +6 -26
- 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 -4
- data/lib/rails_live_reload/web_socket/base.rb +4 -4
- data/lib/rails_live_reload.rb +7 -9
- metadata +4 -7
- data/lib/rails_live_reload/javascript/long_polling.js +0 -6
- data/lib/rails_live_reload/javascript/polling.js +0 -6
- data/lib/rails_live_reload/middleware/long_polling.rb +0 -45
- data/lib/rails_live_reload/middleware/polling.rb +0 -19
- data/lib/rails_live_reload/middleware/web_socket.rb +0 -51
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
@@ -40,28 +40,12 @@ $ bundle
|
|
40
40
|
|
41
41
|
## Configuration
|
42
42
|
|
43
|
-
### There are three modes:
|
44
|
-
1. `:websocket` - This is a default mode which uses websockets to trigger page reloading.
|
45
|
-
2. `:long_polling` - **[Deprecated]** This mode 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.
|
46
|
-
3. `:polling` - **[Deprecated]** 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.
|
47
|
-
|
48
|
-
Keep in mind that `mode` configuration is **deprecated** and will be removed in the future, with `:websocket` be the only one available, in case you think that modes like `:long_polling` or `:polling` should be preserved feel free to open an issue.
|
49
|
-
|
50
43
|
### Create initializer `config/initializers/rails_live_reload.rb`:
|
51
44
|
|
52
45
|
|
53
46
|
```ruby
|
54
47
|
RailsLiveReload.configure do |config|
|
55
|
-
# config.url
|
56
|
-
# Available modes are: :websocket (default), :long_polling and :polling
|
57
|
-
# config.mode = :websocket
|
58
|
-
|
59
|
-
# This is used with :long_polling mode
|
60
|
-
# config.timeout = 30
|
61
|
-
# config.long_polling_sleep_duration = 0.1
|
62
|
-
|
63
|
-
# This is used with :polling mode
|
64
|
-
# config.polling_interval = 100
|
48
|
+
# config.url = "/rails/live/reload"
|
65
49
|
|
66
50
|
# Default watched folders & files
|
67
51
|
# config.watch %r{app/views/.+\.(erb|haml|slim)$}
|
@@ -100,6 +84,7 @@ You are welcome to contribute. See list of `TODO's` below.
|
|
100
84
|
- check with older Rails versions
|
101
85
|
- tests or specs
|
102
86
|
- CI (github actions)
|
87
|
+
- auto reload when rendered controller was changed
|
103
88
|
|
104
89
|
## License
|
105
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
|
@@ -18,14 +18,10 @@ module RailsLiveReload
|
|
18
18
|
end
|
19
19
|
|
20
20
|
class Config
|
21
|
-
attr_reader :patterns
|
21
|
+
attr_reader :patterns
|
22
22
|
attr_accessor :url, :watcher, :files, :enabled
|
23
23
|
|
24
24
|
def initialize
|
25
|
-
@mode = :websocket
|
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 = {}
|
@@ -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
|
@@ -48,24 +48,8 @@ module RailsLiveReload
|
|
48
48
|
patterns[pattern] = reload
|
49
49
|
end
|
50
50
|
|
51
|
-
def
|
52
|
-
|
53
|
-
@mode = mode
|
54
|
-
end
|
55
|
-
|
56
|
-
def polling_interval=(polling_interval)
|
57
|
-
warn "[DEPRECATION] RailsLiveReload 'polling_interval' configuration is deprecated and will be removed in future versions #{caller.first}"
|
58
|
-
@polling_interval = polling_interval
|
59
|
-
end
|
60
|
-
|
61
|
-
def timeout=(timeout)
|
62
|
-
warn "[DEPRECATION] RailsLiveReload 'timeout' configuration is deprecated and will be removed in future versions #{caller.first}"
|
63
|
-
@timeout = timeout
|
64
|
-
end
|
65
|
-
|
66
|
-
def long_polling_sleep_duration=(long_polling_sleep_duration)
|
67
|
-
warn "[DEPRECATION] RailsLiveReload 'long_polling_sleep_duration' configuration is deprecated and will be removed in future versions #{caller.first}"
|
68
|
-
@long_polling_sleep_duration = long_polling_sleep_duration
|
51
|
+
def socket_path
|
52
|
+
root_path.join('tmp/sockets/rails_live_reload.sock')
|
69
53
|
end
|
70
54
|
end
|
71
55
|
end
|
@@ -1,32 +1,41 @@
|
|
1
1
|
module RailsLiveReload
|
2
2
|
class Railtie < ::Rails::Engine
|
3
|
-
|
4
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
|
@@ -1,6 +1,6 @@
|
|
1
1
|
/*!
|
2
|
-
Rails Live Reload 0.
|
2
|
+
Rails Live Reload 0.3.0
|
3
3
|
Copyright © 2022 RailsJazz
|
4
4
|
https://railsjazz.com
|
5
5
|
*/
|
6
|
-
var RailsLiveReload=function(){"use strict";const t="RELOAD";class
|
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}();
|
@@ -10,15 +10,12 @@ module RailsLiveReload
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def call!(env)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
main_rails_live_response(request)
|
18
|
-
when "#{RailsLiveReload.config.url}/script"
|
19
|
-
content = client_javascript
|
20
|
-
[200, {'Content-Type' => 'application/javascript', 'Content-Length' => content.size.to_s, 'Cache-Control' => 'no-store'}, [content]]
|
13
|
+
if env["REQUEST_PATH"].starts_with?(RailsLiveReload.config.url)
|
14
|
+
::Rails.logger.silence do
|
15
|
+
@app.call(env)
|
16
|
+
end
|
21
17
|
else
|
18
|
+
request = Rack::Request.new(env)
|
22
19
|
status, headers, response = @app.call(env)
|
23
20
|
|
24
21
|
if html?(headers) && response.respond_to?(:[]) && (status == 500 || (status.to_s =~ /20./ && request.get?))
|
@@ -29,22 +26,10 @@ module RailsLiveReload
|
|
29
26
|
|
30
27
|
[status, headers, response]
|
31
28
|
end
|
32
|
-
rescue Exception => ex
|
33
|
-
puts ex.message
|
34
|
-
puts ex.backtrace.take(10)
|
35
|
-
raise ex
|
36
29
|
end
|
37
30
|
|
38
31
|
private
|
39
32
|
|
40
|
-
def main_rails_live_response(request)
|
41
|
-
raise NotImplementedError
|
42
|
-
end
|
43
|
-
|
44
|
-
def client_javascript
|
45
|
-
@client_javascript ||= File.open(File.join(File.dirname(__FILE__), "../javascript/#{RailsLiveReload.config.mode}.js")).read
|
46
|
-
end
|
47
|
-
|
48
33
|
def make_new_response(body)
|
49
34
|
body = body.sub("</head>", <<~HTML.html_safe)
|
50
35
|
<script defer type="text/javascript" src="#{RailsLiveReload.config.url}/script"></script>
|
@@ -55,18 +40,13 @@ module RailsLiveReload
|
|
55
40
|
#{{
|
56
41
|
files: CurrentRequest.current.data.to_a,
|
57
42
|
time: Time.now.to_i,
|
58
|
-
url: RailsLiveReload.config.url
|
59
|
-
options: javascript_options
|
43
|
+
url: RailsLiveReload.config.url
|
60
44
|
}.to_json}
|
61
45
|
</script>
|
62
46
|
</body>
|
63
47
|
HTML
|
64
48
|
end
|
65
49
|
|
66
|
-
def javascript_options
|
67
|
-
{}
|
68
|
-
end
|
69
|
-
|
70
50
|
def html?(headers)
|
71
51
|
headers["Content-Type"].to_s.include?("text/html")
|
72
52
|
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,7 +32,7 @@ module RailsLiveReload
|
|
27
32
|
all.each do |file|
|
28
33
|
files[file] = File.mtime(file).to_i rescue nil
|
29
34
|
end
|
30
|
-
|
35
|
+
reload_all
|
31
36
|
end
|
32
37
|
listener.start
|
33
38
|
end
|
@@ -38,5 +43,35 @@ module RailsLiveReload
|
|
38
43
|
files[file] = File.mtime(file).to_i rescue nil
|
39
44
|
end
|
40
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
|
41
76
|
end
|
42
77
|
end
|
@@ -34,15 +34,15 @@ module RailsLiveReload
|
|
34
34
|
when 'setup'
|
35
35
|
setup payload['options']
|
36
36
|
else
|
37
|
-
raise NotImplementedError
|
37
|
+
raise NotImplementedError
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
41
|
def reload
|
42
42
|
return if dt.nil? || files.nil? || RailsLiveReload::Checker.scan(dt, files).size.zero?
|
43
|
-
|
43
|
+
|
44
44
|
transmit({command: "RELOAD"})
|
45
|
-
end
|
45
|
+
end
|
46
46
|
|
47
47
|
def transmit(cable_message)
|
48
48
|
websocket.transmit encode(cable_message)
|
@@ -88,7 +88,7 @@ module RailsLiveReload
|
|
88
88
|
def setup(options)
|
89
89
|
@dt = options['dt']
|
90
90
|
@files = options['files']
|
91
|
-
end
|
91
|
+
end
|
92
92
|
|
93
93
|
def encode(message)
|
94
94
|
message.to_json
|
data/lib/rails_live_reload.rb
CHANGED
@@ -2,10 +2,9 @@ require "listen"
|
|
2
2
|
require "rails_live_reload/version"
|
3
3
|
require "rails_live_reload/config"
|
4
4
|
require "rails_live_reload/watcher"
|
5
|
+
require "rails_live_reload/server/connections"
|
6
|
+
require "rails_live_reload/server/base"
|
5
7
|
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
8
|
require "rails_live_reload/instrument/metrics_collector"
|
10
9
|
require "rails_live_reload/thread/current_request"
|
11
10
|
require "rails_live_reload/checker"
|
@@ -26,14 +25,13 @@ module RailsLiveReload
|
|
26
25
|
invalid_request: "invalid_request",
|
27
26
|
remote: "remote"
|
28
27
|
},
|
28
|
+
socket_events: {
|
29
|
+
reload: 'reload'
|
30
|
+
},
|
29
31
|
protocols: ["rails-live-reload-v1-json"].freeze
|
30
32
|
}
|
31
33
|
|
32
|
-
def
|
33
|
-
|
34
|
-
when :polling then RailsLiveReload::Middleware::Polling
|
35
|
-
when :long_polling then RailsLiveReload::Middleware::LongPolling
|
36
|
-
when :websocket then RailsLiveReload::Middleware::WebSocket
|
37
|
-
end
|
34
|
+
module_function def server
|
35
|
+
@server ||= RailsLiveReload::Server::Base.new
|
38
36
|
end
|
39
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,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2022-07-
|
12
|
+
date: 2022-07-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: railties
|
@@ -99,13 +99,10 @@ files:
|
|
99
99
|
- lib/rails_live_reload/config.rb
|
100
100
|
- lib/rails_live_reload/engine.rb
|
101
101
|
- lib/rails_live_reload/instrument/metrics_collector.rb
|
102
|
-
- lib/rails_live_reload/javascript/long_polling.js
|
103
|
-
- lib/rails_live_reload/javascript/polling.js
|
104
102
|
- lib/rails_live_reload/javascript/websocket.js
|
105
103
|
- lib/rails_live_reload/middleware/base.rb
|
106
|
-
- lib/rails_live_reload/
|
107
|
-
- lib/rails_live_reload/
|
108
|
-
- lib/rails_live_reload/middleware/web_socket.rb
|
104
|
+
- lib/rails_live_reload/server/base.rb
|
105
|
+
- lib/rails_live_reload/server/connections.rb
|
109
106
|
- lib/rails_live_reload/thread/current_request.rb
|
110
107
|
- lib/rails_live_reload/version.rb
|
111
108
|
- lib/rails_live_reload/watcher.rb
|
@@ -1,6 +0,0 @@
|
|
1
|
-
/*!
|
2
|
-
Rails Live Reload 0.2.0
|
3
|
-
Copyright © 2022 RailsJazz
|
4
|
-
https://railsjazz.com
|
5
|
-
*/
|
6
|
-
var RailsLiveReload=function(){"use strict";const t="RELOAD";class e{static _instance;static get instance(){return e._instance||(e._instance=new this),e._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:s,options:i}=JSON.parse(this.optionsNode.textContent);this.files=t,this.time=e,this.url=s,this.options=i}start(){throw"This should be implemented in subclass"}stop(){throw"This should be implemented in subclass"}restart(){this.stop(),this.initialize(),this.start()}fullReload(){window.location.reload()}get optionsNode(){const t=document.getElementById("rails-live-reload-options");if(!t)throw"Unable to find RailsLiveReload options";return t}}class s extends e{start(){this.retriesCount=0,this.timestamp=new Date,this.poll(this.timestamp)}stop(){this.timestamp=void 0}async poll(e){if(this.timestamp===e)try{const s=new FormData;s.append("dt",this.time),s.append("files",JSON.stringify(this.files));const i=await fetch(this.url,{method:"post",headers:{Accept:"application/json"},body:s}),n=await i.json();if(this.timestamp!==e)return;this.retriesCount=0,n.command===t?this.fullReload():this.poll(e)}catch(t){if(this.timestamp!==e)return;this.retriesCount++,this.retriesCount<10?setTimeout((()=>this.poll(e)),5e3):this.stop()}}}return document.addEventListener("DOMContentLoaded",(()=>{s.start()})),s}();
|
@@ -1,6 +0,0 @@
|
|
1
|
-
/*!
|
2
|
-
Rails Live Reload 0.2.0
|
3
|
-
Copyright © 2022 RailsJazz
|
4
|
-
https://railsjazz.com
|
5
|
-
*/
|
6
|
-
var RailsLiveReload=function(){"use strict";const t="RELOAD";class e{static _instance;static get instance(){return e._instance||(e._instance=new this),e._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:i,options:s}=JSON.parse(this.optionsNode.textContent);this.files=t,this.time=e,this.url=i,this.options=s}start(){throw"This should be implemented in subclass"}stop(){throw"This should be implemented in subclass"}restart(){this.stop(),this.initialize(),this.start()}fullReload(){window.location.reload()}get optionsNode(){const t=document.getElementById("rails-live-reload-options");if(!t)throw"Unable to find RailsLiveReload options";return t}}class i extends e{start(){this.interval||(this.interval=setInterval((async()=>{const e=new FormData;e.append("dt",this.time),e.append("files",JSON.stringify(this.files));const i=await fetch(this.url,{method:"post",headers:{Accept:"application/json"},body:e});(await i.json()).command===t&&(this.stop(),this.fullReload())}),this.options.polling_interval))}restart(){this.initialize()}stop(){clearInterval(this.interval),this.interval=void 0}}return document.addEventListener("DOMContentLoaded",(()=>{i.start()})),i}();
|
@@ -1,45 +0,0 @@
|
|
1
|
-
module RailsLiveReload
|
2
|
-
module Middleware
|
3
|
-
class LongPolling < Base
|
4
|
-
private
|
5
|
-
|
6
|
-
def main_rails_live_response(request)
|
7
|
-
params = request.params
|
8
|
-
body = lambda do |stream|
|
9
|
-
new_thread do
|
10
|
-
counter = 0
|
11
|
-
|
12
|
-
loop do
|
13
|
-
command = RailsLiveReload::Command.new(params)
|
14
|
-
|
15
|
-
if command.reload?
|
16
|
-
stream.write(command.payload.to_json) and break
|
17
|
-
end
|
18
|
-
|
19
|
-
sleep(RailsLiveReload.config.long_polling_sleep_duration)
|
20
|
-
counter += 1
|
21
|
-
|
22
|
-
stream.write(command.payload.to_json) and break if counter >= max_sleeps_count
|
23
|
-
end
|
24
|
-
ensure
|
25
|
-
stream.close
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
[ 200, { 'Content-Type' => 'application/json', 'rack.hijack' => body }, nil ]
|
30
|
-
end
|
31
|
-
|
32
|
-
def max_sleeps_count
|
33
|
-
RailsLiveReload.config.timeout * (1 / RailsLiveReload.config.long_polling_sleep_duration)
|
34
|
-
end
|
35
|
-
|
36
|
-
def new_thread
|
37
|
-
Thread.new {
|
38
|
-
t2 = Thread.current
|
39
|
-
t2.abort_on_exception = true
|
40
|
-
yield
|
41
|
-
}
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module RailsLiveReload
|
2
|
-
module Middleware
|
3
|
-
class Polling < Base
|
4
|
-
private
|
5
|
-
|
6
|
-
def main_rails_live_response(request)
|
7
|
-
[
|
8
|
-
200,
|
9
|
-
{ 'Content-Type' => 'application/json' },
|
10
|
-
[ RailsLiveReload::Command.new(request.params).payload.to_json ]
|
11
|
-
]
|
12
|
-
end
|
13
|
-
|
14
|
-
def javascript_options
|
15
|
-
{ polling_interval: RailsLiveReload.config.polling_interval }
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,51 +0,0 @@
|
|
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 Middleware
|
10
|
-
class WebSocket < Base
|
11
|
-
attr_reader :mutex, :event_loop
|
12
|
-
|
13
|
-
delegate :connections, :add_connection, :remove_connection, to: :class
|
14
|
-
|
15
|
-
class << self
|
16
|
-
def connections
|
17
|
-
@connections ||= []
|
18
|
-
end
|
19
|
-
|
20
|
-
def add_connection(connection)
|
21
|
-
connections << connection
|
22
|
-
end
|
23
|
-
|
24
|
-
def remove_connection(connection)
|
25
|
-
connections.delete connection
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def initialize(env)
|
30
|
-
@mutex = Monitor.new
|
31
|
-
@event_loop = nil
|
32
|
-
super
|
33
|
-
end
|
34
|
-
|
35
|
-
def main_rails_live_response(request)
|
36
|
-
setup_heartbeat_timer
|
37
|
-
RailsLiveReload::WebSocket::Base.new(self, request).process
|
38
|
-
end
|
39
|
-
|
40
|
-
def setup_heartbeat_timer
|
41
|
-
@heartbeat_timer ||= event_loop.timer(3) do
|
42
|
-
event_loop.post { connections.each(&:beat) }
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def event_loop
|
47
|
-
@event_loop || @mutex.synchronize { @event_loop ||= RailsLiveReload::WebSocket::EventLoop.new }
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|