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