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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 999e453ba10fc303a5f317609ad52abbb318972712802ea87606f17c89c2054a
4
- data.tar.gz: 456e9df00f34d48e75a8cdbee655e035e68a0c98c468020946dc3ec6903848bb
3
+ metadata.gz: 88846a7af50ff0f2941eeb26795f7156ef4c92b8397453d9810d0196a27851ba
4
+ data.tar.gz: 70337f1ce69e0fab74a7272633629a565f6106a86e00590e4eb0f2d49e71281e
5
5
  SHA512:
6
- metadata.gz: 9d3e4ddad3250f1cb40b06d0f17c69b537d25c26ebe90b3844f942540af27286c9d2f66d4a29562d2ffd9c725d0286b028da8a645ad43126ac57099a13b567bd
7
- data.tar.gz: a7ac90c951252374f0d6cd357d822c33d8c2a330f2b2b487f3e048551b439168474a4da4dd822228644ea7d1cfdbfdc04e5064efab9957c6a55c8a455b1f1d1f
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 = "/rails/live/reload"
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
- RailsLiveReload.watcher.files.each do |file, fdt|
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, :polling_interval, :timeout, :mode, :long_polling_sleep_duration
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 mode=(mode)
52
- warn "[DEPRECATION] RailsLiveReload 'mode' configuration is deprecated and will be removed in future versions #{caller.first}"
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.middleware
6
+ app.middleware.insert_after ActionDispatch::Executor, RailsLiveReload::Middleware::Base
8
7
  else
9
8
  begin
10
- app.middleware.insert_after ActionDispatch::Static, RailsLiveReload.middleware
9
+ app.middleware.insert_after ActionDispatch::Static, RailsLiveReload::Middleware::Base
11
10
  rescue
12
- app.middleware.insert_after Rack::SendFile, RailsLiveReload.middleware
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 :configure_metrics, after: :initialize_logger do
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 :reset_current_request, after: :initialize_logger do |app|
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.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 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:n,options:i}=JSON.parse(this.optionsNode.textContent);this.files=t,this.time=e,this.url=n,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}}const n=["rails-live-reload-v1-json"];class i extends e{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),n),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",(()=>{i.start()})),i}();
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
- request = Rack::Request.new(env)
14
-
15
- case env["REQUEST_PATH"]
16
- when RailsLiveReload.config.url
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,3 +1,3 @@
1
1
  module RailsLiveReload
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -1,6 +1,10 @@
1
1
  module RailsLiveReload
2
2
  class Watcher
3
- attr_reader :root, :files
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
- RailsLiveReload::Middleware::WebSocket.connections.each(&:reload)
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
@@ -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 self.middleware
33
- case config.mode
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.2.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-09 00:00:00.000000000 Z
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/middleware/long_polling.rb
107
- - lib/rails_live_reload/middleware/polling.rb
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