rails_live_reload 0.2.0 → 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 +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
|