rails_live_reload 0.0.2 → 0.1.2
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 +47 -22
- data/lib/rails_live_reload/checker.rb +3 -9
- data/lib/rails_live_reload/client.rb +48 -9
- data/lib/rails_live_reload/command.rb +12 -6
- data/lib/rails_live_reload/config.rb +51 -0
- data/lib/rails_live_reload/engine.rb +22 -16
- data/lib/rails_live_reload/instrument/metrics_collector.rb +0 -20
- data/lib/rails_live_reload/rails/middleware/base.rb +63 -0
- data/lib/rails_live_reload/rails/middleware/long_polling.rb +51 -0
- data/lib/rails_live_reload/rails/middleware/polling.rb +21 -0
- data/lib/rails_live_reload/version.rb +1 -1
- data/lib/rails_live_reload/watcher.rb +9 -6
- data/lib/rails_live_reload.rb +10 -26
- metadata +7 -4
- data/lib/rails_live_reload/rails/middleware.rb +0 -54
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 04c014926a43e8cba8e586fa5aca93a4e002357fffee35d2a553db0ec88f04a0
|
4
|
+
data.tar.gz: 7c5b64509ab169603cd32b62adc9390eb2f1d939d2e626e9bca960f325d45876
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6e365d7ffad4d351c35c84f8839c4b55318c9769c2191c8aab53cd441253d26950cfabeb98f76c8b556e7bb80d9b329f0b6e5d81ce1c4b0841239d106b38a406
|
7
|
+
data.tar.gz: ec660e96b97075b0c7663e5d0b3be0e59d3a505805feda8c0e7262c2e9c7889f560f0a308edaecf1dc03aaeb7e3a987e709718196ae7b42d47b98df03074449f
|
data/README.md
CHANGED
@@ -1,18 +1,23 @@
|
|
1
1
|
# Rails Live Reload
|
2
2
|
|
3
|
-
|
3
|
+
[](https://www.railsjazz.com)
|
4
4
|
|
5
|
-
|
5
|
+

|
6
|
+
|
7
|
+
This is the simplest and probably the most robust way to add live reloading to your Rails app.
|
8
|
+
|
9
|
+
Just add the gem and thats it, now you have a live reloading. No 3rd party dependencies.
|
6
10
|
|
7
11
|
Works with:
|
8
12
|
|
9
|
-
- views (EBR/HAML/SLIM) (
|
13
|
+
- views (EBR/HAML/SLIM) (the page is reloaded only when changed views which were rendered on the page)
|
10
14
|
- partials
|
11
15
|
- CSS/JS
|
12
|
-
- helpers
|
13
|
-
-
|
16
|
+
- helpers (if configured)
|
17
|
+
- YAML locales (if configured)
|
18
|
+
- on the "crash" page, so it will be reloaded as soon as you make a fix
|
14
19
|
|
15
|
-
|
20
|
+
The page is reloaded fully with `window.location.reload()` to make sure that every chage will be displayed.
|
16
21
|
|
17
22
|
## Usage
|
18
23
|
|
@@ -35,31 +40,49 @@ $ bundle
|
|
35
40
|
|
36
41
|
## Configuration
|
37
42
|
|
38
|
-
|
43
|
+
### There are two modes:
|
44
|
+
1. `:long_polling` - This is a default mode, it uses [long polling](https://javascript.info/long-polling) techunique, client opens a connection that will hang until either change is detected, or timeout happens, if later, a new connection is oppened
|
45
|
+
2. `:polling` - This mode will use regular polling to detect changes, you can configure custom `polling_interval` (default is 100ms). We recommend using `:long_polling` as it makes much less requests to the server.
|
46
|
+
|
47
|
+
### Create initializer `config/initializers/rails_live_reload.rb`:
|
39
48
|
|
40
49
|
|
41
50
|
```ruby
|
42
|
-
RailsLiveReload.
|
43
|
-
config.url = "/rails/live/reload"
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
config.
|
49
|
-
|
51
|
+
RailsLiveReload.configure do |config|
|
52
|
+
# config.url = "/rails/live/reload"
|
53
|
+
# Available modes are: :long_polling (default) and :polling
|
54
|
+
# config.mode = :long_polling
|
55
|
+
|
56
|
+
# This is used with :long_polling mode
|
57
|
+
# config.timeout = 30
|
58
|
+
# config.long_polling_sleep_duration = 0.1
|
59
|
+
|
60
|
+
# This is used with :polling mode
|
61
|
+
# config.polling_interval = 100
|
62
|
+
|
63
|
+
# Default watched folders & files
|
64
|
+
# config.watch %r{app/views/.+\.(erb|haml|slim)$}
|
65
|
+
# config.watch %r{(app|vendor)/(assets|javascript)/\w+/(.+\.(css|js|html|png|jpg|ts|jsx)).*}, reload: :always
|
66
|
+
|
50
67
|
# More examples:
|
51
68
|
# config.watch %r{app/helpers/.+\.rb}, reload: :always
|
52
69
|
# config.watch %r{config/locales/.+\.yml}, reload: :always
|
53
|
-
|
70
|
+
|
71
|
+
# config.enabled = Rails.env.development?
|
72
|
+
end if defined?(RailsLiveReload)
|
54
73
|
```
|
55
74
|
|
56
75
|
## How it works
|
57
76
|
|
58
|
-
There are 3 main
|
77
|
+
There are 3 main parts:
|
59
78
|
|
60
|
-
1) listener
|
79
|
+
1) listener of file changes (using `listen` gem)
|
61
80
|
2) collector of rendered views (see rails instrumentation)
|
62
|
-
3) middleware which is responding to
|
81
|
+
3) middleware which is responding to polling JS calls
|
82
|
+
|
83
|
+
## Notes
|
84
|
+
|
85
|
+
The default configuration assumes that you either use asset pipeline, or that your assets compile quickly (on most applications asset compilation takes around 50-200ms), so it watches for changes in `app/assets` and `app/javascript` folders, this will not be a problem for 99% of users, but in case your asset compilation takes couple of seconds, this might not work propperly, in that case we would recommend you to add configuration to watch output folder.
|
63
86
|
|
64
87
|
## Contributing
|
65
88
|
|
@@ -68,17 +91,19 @@ You are welcome to contribute. See list of `TODO's` below.
|
|
68
91
|
## TODO
|
69
92
|
|
70
93
|
- reload CSS without reloading the whole page?
|
94
|
+
- add `:websocket` mode?
|
71
95
|
- smarter reload if there is a change in helper (check methods from rendered views?)
|
72
96
|
- generator for initializer
|
73
|
-
-
|
74
|
-
- maybe complex rules, e.g. if "user.rb" file is changed - automatically reload all "users" views
|
97
|
+
- more complex rules? e.g. if "user.rb" file is changed - reload all pages with rendered "users" views
|
75
98
|
- check with older Rails versions
|
76
99
|
- tests or specs
|
77
100
|
- CI (github actions)
|
101
|
+
- improve how JS code is injected into HTML
|
102
|
+
- improve work with turbo, turbolinks
|
78
103
|
|
79
104
|
## License
|
80
105
|
|
81
106
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
82
107
|
|
83
108
|
[<img src="https://github.com/igorkasyanchuk/rails_time_travel/blob/main/docs/more_gems.png?raw=true"
|
84
|
-
/>](https://www.railsjazz.com/?utm_source=github&utm_medium=bottom&utm_campaign=rails_live_reload)
|
109
|
+
/>](https://www.railsjazz.com/?utm_source=github&utm_medium=bottom&utm_campaign=rails_live_reload)
|
@@ -5,22 +5,16 @@ module RailsLiveReload
|
|
5
5
|
temp = []
|
6
6
|
|
7
7
|
# all changed files
|
8
|
-
RailsLiveReload.files.each do |file, fdt|
|
8
|
+
RailsLiveReload.watcher.files.each do |file, fdt|
|
9
9
|
temp << file if fdt && fdt > dt
|
10
10
|
end
|
11
11
|
|
12
|
-
# ::Rails.logger.info "Changed: #{temp}"
|
13
|
-
|
14
12
|
result = []
|
15
13
|
|
16
14
|
temp.each do |file|
|
17
|
-
# ::Rails.logger.info "Checking: #{file}"
|
18
15
|
RailsLiveReload.patterns.each do |pattern, rule|
|
19
|
-
|
20
|
-
|
21
|
-
# 2. checking if rendered file
|
22
|
-
rule_1 = file.match(pattern) && rule == :always
|
23
|
-
rule_2 = file.match(pattern) && rendered_files.include?(file)
|
16
|
+
rule_1 = file.match(pattern) && rule == :always # Used for CSS, JS, yaml, helpers, etc.
|
17
|
+
rule_2 = file.match(pattern) && rendered_files.include?(file) # Used to check if view was rendered
|
24
18
|
|
25
19
|
if rule_1 || rule_2
|
26
20
|
result << file
|
@@ -1,8 +1,49 @@
|
|
1
1
|
module RailsLiveReload
|
2
|
-
|
2
|
+
module Client
|
3
3
|
|
4
|
-
def Client.
|
5
|
-
|
4
|
+
def Client.long_polling_js
|
5
|
+
<<~HTML.html_safe
|
6
|
+
<script>
|
7
|
+
(function() {
|
8
|
+
const files = #{CurrentRequest.current.data.to_a.to_json};
|
9
|
+
let retries_count = 0;
|
10
|
+
function poll() {
|
11
|
+
const formData = new FormData();
|
12
|
+
formData.append('dt', #{Time.now.to_i})
|
13
|
+
formData.append('files', JSON.stringify(files))
|
14
|
+
|
15
|
+
fetch(
|
16
|
+
"#{RailsLiveReload.config.url}",
|
17
|
+
{
|
18
|
+
method: "post",
|
19
|
+
headers: { 'Accept': 'application/json', },
|
20
|
+
body: formData
|
21
|
+
}
|
22
|
+
)
|
23
|
+
.then(response => response.json())
|
24
|
+
.then(data => {
|
25
|
+
retries_count = 0;
|
26
|
+
if(data['command'] === 'RELOAD') {
|
27
|
+
window.location.reload();
|
28
|
+
} else {
|
29
|
+
poll();
|
30
|
+
}
|
31
|
+
}).catch(() => {
|
32
|
+
retries_count++;
|
33
|
+
|
34
|
+
if(retries_count < 10) {
|
35
|
+
setTimeout(poll, 5000)
|
36
|
+
}
|
37
|
+
})
|
38
|
+
}
|
39
|
+
poll();
|
40
|
+
})();
|
41
|
+
</script>
|
42
|
+
HTML
|
43
|
+
end
|
44
|
+
|
45
|
+
def Client.polling_js
|
46
|
+
<<~HTML.html_safe
|
6
47
|
<script>
|
7
48
|
const files = #{CurrentRequest.current.data.to_a.to_json};
|
8
49
|
const timer = setInterval(
|
@@ -10,9 +51,8 @@ module RailsLiveReload
|
|
10
51
|
const formData = new FormData();
|
11
52
|
formData.append('dt', #{Time.now.to_i})
|
12
53
|
formData.append('files', JSON.stringify(files))
|
13
|
-
|
14
54
|
fetch(
|
15
|
-
"#{RailsLiveReload.url}",
|
55
|
+
"#{RailsLiveReload.config.url}",
|
16
56
|
{
|
17
57
|
method: "post",
|
18
58
|
headers: { 'Accept': 'application/json', },
|
@@ -26,11 +66,10 @@ module RailsLiveReload
|
|
26
66
|
window.location.reload();
|
27
67
|
}
|
28
68
|
})
|
29
|
-
}, #{RailsLiveReload.
|
69
|
+
}, #{RailsLiveReload.config.polling_interval}
|
30
70
|
)
|
31
71
|
</script>
|
32
|
-
|
72
|
+
HTML
|
33
73
|
end
|
34
|
-
|
35
74
|
end
|
36
|
-
end
|
75
|
+
end
|
@@ -4,16 +4,22 @@ module RailsLiveReload
|
|
4
4
|
|
5
5
|
def initialize(params)
|
6
6
|
@dt = params["dt"].to_i
|
7
|
-
@files = JSON.parse(params["files"])
|
7
|
+
@files = JSON.parse(params["files"]) rescue []
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
11
|
-
|
10
|
+
def changes
|
11
|
+
RailsLiveReload::Checker.scan(dt, files)
|
12
|
+
end
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
14
|
+
def reload?
|
15
|
+
!changes.size.zero?
|
16
|
+
end
|
17
|
+
|
18
|
+
def payload
|
19
|
+
if reload?
|
16
20
|
{ command: "RELOAD" }
|
21
|
+
else
|
22
|
+
{ command: "NO_CHANGES" }
|
17
23
|
end
|
18
24
|
end
|
19
25
|
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module RailsLiveReload
|
2
|
+
class << self
|
3
|
+
def configure
|
4
|
+
yield config
|
5
|
+
end
|
6
|
+
|
7
|
+
def config
|
8
|
+
@_config ||= Config.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def patterns
|
12
|
+
config.patterns
|
13
|
+
end
|
14
|
+
|
15
|
+
def enabled?
|
16
|
+
config.enabled
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Config
|
21
|
+
attr_reader :patterns
|
22
|
+
attr_accessor :mode, :polling_interval, :timeout, :url, :watcher, :files, :enabled, :long_polling_sleep_duration
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
@mode = :long_polling
|
26
|
+
@timeout = 30
|
27
|
+
@long_polling_sleep_duration = 0.1
|
28
|
+
@polling_interval = 100
|
29
|
+
@url = "/rails/live/reload"
|
30
|
+
@watcher = nil
|
31
|
+
@files = {}
|
32
|
+
@enabled = ::Rails.env.development?
|
33
|
+
|
34
|
+
# These configs work for 95% apps, see README for more info
|
35
|
+
@patterns = {
|
36
|
+
%r{app/views/.+\.(erb|haml|slim)$} => :on_change,
|
37
|
+
%r{(app|vendor)/(assets|javascript)/\w+/(.+\.(css|js|html|png|jpg|ts|jsx)).*} => :always
|
38
|
+
}
|
39
|
+
@default_patterns_changed = false
|
40
|
+
end
|
41
|
+
|
42
|
+
def watch(pattern, reload: :on_change)
|
43
|
+
unless @default_patterns_changed
|
44
|
+
@default_patterns_changed = true
|
45
|
+
@patterns = {}
|
46
|
+
end
|
47
|
+
|
48
|
+
patterns[pattern] = reload
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -1,26 +1,32 @@
|
|
1
1
|
module RailsLiveReload
|
2
2
|
class Railtie < ::Rails::Engine
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
4
|
+
if RailsLiveReload.enabled? && defined?(::Rails::Server)
|
5
|
+
initializer "rails_live_reload.middleware" do |app|
|
6
|
+
if ::Rails::VERSION::MAJOR.to_i >= 5
|
7
|
+
app.middleware.insert_after ActionDispatch::Executor, RailsLiveReload.middleware
|
8
|
+
else
|
9
|
+
begin
|
10
|
+
app.middleware.insert_after ActionDispatch::Static, RailsLiveReload.middleware
|
11
|
+
rescue
|
12
|
+
app.middleware.insert_after Rack::SendFile, RailsLiveReload.middleware
|
13
|
+
end
|
12
14
|
end
|
15
|
+
|
16
|
+
RailsLiveReload::Watcher.init
|
13
17
|
end
|
14
18
|
|
15
|
-
|
16
|
-
|
19
|
+
initializer :configure_metrics, after: :initialize_logger do
|
20
|
+
ActiveSupport::Notifications.subscribe(
|
21
|
+
/\.action_view/,
|
22
|
+
RailsLiveReload::Instrument::MetricsCollector.new
|
23
|
+
)
|
24
|
+
end
|
17
25
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
)
|
26
|
+
initializer :reset_current_request, after: :initialize_logger do |app|
|
27
|
+
app.executor.to_run { CurrentRequest.cleanup }
|
28
|
+
app.executor.to_complete { CurrentRequest.cleanup }
|
29
|
+
end
|
23
30
|
end
|
24
|
-
|
25
31
|
end
|
26
32
|
end
|
@@ -7,23 +7,3 @@ module RailsLiveReload
|
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
10
|
-
|
11
|
-
# render_template.action_view
|
12
|
-
# {
|
13
|
-
# identifier: "/Users/adam/projects/notifications/app/views/posts/index.html.erb",
|
14
|
-
# layout: "layouts/application"
|
15
|
-
# }
|
16
|
-
# render_partial.action_view
|
17
|
-
# {
|
18
|
-
# identifier: "/Users/adam/projects/notifications/app/views/posts/_form.html.erb"
|
19
|
-
# }
|
20
|
-
# render_collection.action_view
|
21
|
-
# {
|
22
|
-
# identifier: "/Users/adam/projects/notifications/app/views/posts/_post.html.erb",
|
23
|
-
# count: 3,
|
24
|
-
# cache_hits: 0
|
25
|
-
# }
|
26
|
-
# render_layout.action_view
|
27
|
-
# {
|
28
|
-
# identifier: "/Users/adam/projects/notifications/app/views/layouts/application.html.erb"
|
29
|
-
# }
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module RailsLiveReload
|
2
|
+
module Rails
|
3
|
+
module Middleware
|
4
|
+
class Base
|
5
|
+
def initialize(app)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
dup.call!(env)
|
11
|
+
end
|
12
|
+
|
13
|
+
def call!(env)
|
14
|
+
request = Rack::Request.new(env)
|
15
|
+
|
16
|
+
if env["REQUEST_PATH"] == RailsLiveReload.config.url
|
17
|
+
rails_live_response(request)
|
18
|
+
else
|
19
|
+
@status, @headers, @response = @app.call(env)
|
20
|
+
|
21
|
+
if html? && @response.respond_to?(:[]) && (@status == 500 || (@status.to_s =~ /20./ && request.get?))
|
22
|
+
new_response = make_new_response(@response[0])
|
23
|
+
@headers['Content-Length'] = new_response.bytesize.to_s
|
24
|
+
@response = [new_response]
|
25
|
+
end
|
26
|
+
|
27
|
+
[@status, @headers, @response]
|
28
|
+
end
|
29
|
+
rescue Exception => ex
|
30
|
+
puts ex.message
|
31
|
+
puts ex.backtrace.take(10)
|
32
|
+
raise ex
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def rails_live_response(request)
|
38
|
+
raise NotImplementedError
|
39
|
+
end
|
40
|
+
|
41
|
+
def client_javascript(request)
|
42
|
+
raise NotImplementedError
|
43
|
+
end
|
44
|
+
|
45
|
+
def make_new_response(body)
|
46
|
+
body.sub("</head>", "</head>#{client_javascript}")
|
47
|
+
end
|
48
|
+
|
49
|
+
def html?
|
50
|
+
@headers["Content-Type"].to_s.include?("text/html")
|
51
|
+
end
|
52
|
+
|
53
|
+
def new_thread
|
54
|
+
Thread.new {
|
55
|
+
t2 = Thread.current
|
56
|
+
t2.abort_on_exception = true
|
57
|
+
yield
|
58
|
+
}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module RailsLiveReload
|
2
|
+
module Rails
|
3
|
+
module Middleware
|
4
|
+
class LongPolling < Base
|
5
|
+
private
|
6
|
+
|
7
|
+
def rails_live_response(request)
|
8
|
+
params = request.params
|
9
|
+
body = lambda do |stream|
|
10
|
+
new_thread do
|
11
|
+
counter = 0
|
12
|
+
|
13
|
+
loop do
|
14
|
+
command = RailsLiveReload::Command.new(params)
|
15
|
+
|
16
|
+
if command.reload?
|
17
|
+
stream.write(command.payload.to_json) and break
|
18
|
+
end
|
19
|
+
|
20
|
+
sleep(RailsLiveReload.config.long_polling_sleep_duration)
|
21
|
+
counter += 1
|
22
|
+
|
23
|
+
stream.write(command.payload.to_json) and break if counter >= max_sleeps_count
|
24
|
+
end
|
25
|
+
ensure
|
26
|
+
stream.close
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
[ 200, { 'Content-Type' => 'application/json', 'rack.hijack' => body }, nil ]
|
31
|
+
end
|
32
|
+
|
33
|
+
def client_javascript
|
34
|
+
RailsLiveReload::Client.long_polling_js
|
35
|
+
end
|
36
|
+
|
37
|
+
def max_sleeps_count
|
38
|
+
RailsLiveReload.config.timeout * (1 / RailsLiveReload.config.long_polling_sleep_duration)
|
39
|
+
end
|
40
|
+
|
41
|
+
def new_thread
|
42
|
+
Thread.new {
|
43
|
+
t2 = Thread.current
|
44
|
+
t2.abort_on_exception = true
|
45
|
+
yield
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RailsLiveReload
|
2
|
+
module Rails
|
3
|
+
module Middleware
|
4
|
+
class Polling < Base
|
5
|
+
private
|
6
|
+
|
7
|
+
def rails_live_response(request)
|
8
|
+
[
|
9
|
+
200,
|
10
|
+
{ 'Content-Type' => 'application/json' },
|
11
|
+
[ RailsLiveReload::Command.new(request.params).payload.to_json ]
|
12
|
+
]
|
13
|
+
end
|
14
|
+
|
15
|
+
def client_javascript
|
16
|
+
RailsLiveReload::Client.polling_js
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module RailsLiveReload
|
2
2
|
class Watcher
|
3
|
-
attr_reader :root
|
3
|
+
attr_reader :root, :files
|
4
4
|
|
5
5
|
def Watcher.init
|
6
6
|
watcher = new
|
@@ -9,12 +9,15 @@ module RailsLiveReload
|
|
9
9
|
|
10
10
|
def initialize
|
11
11
|
@root = ::Rails.application.root
|
12
|
+
@files = {}
|
13
|
+
|
12
14
|
puts "Watching: #{root}"
|
13
15
|
RailsLiveReload.patterns.each do |pattern, rule|
|
14
16
|
puts " #{pattern} => #{rule}"
|
15
17
|
end
|
16
|
-
|
17
|
-
|
18
|
+
|
19
|
+
build_tree
|
20
|
+
start_listener
|
18
21
|
end
|
19
22
|
|
20
23
|
def start_listener
|
@@ -22,7 +25,7 @@ module RailsLiveReload
|
|
22
25
|
listener = Listen.to(root) do |modified, added, removed|
|
23
26
|
all = modified + added + removed
|
24
27
|
all.each do |file|
|
25
|
-
|
28
|
+
files[file] = File.mtime(file).to_i rescue nil
|
26
29
|
end
|
27
30
|
end
|
28
31
|
listener.start
|
@@ -31,8 +34,8 @@ module RailsLiveReload
|
|
31
34
|
|
32
35
|
def build_tree
|
33
36
|
Dir.glob(File.join(root, '**', '*')).select{|file| File.file?(file)}.each do |file|
|
34
|
-
|
37
|
+
files[file] = File.mtime(file).to_i rescue nil
|
35
38
|
end
|
36
39
|
end
|
37
40
|
end
|
38
|
-
end
|
41
|
+
end
|
data/lib/rails_live_reload.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
require "listen"
|
2
2
|
require "rails_live_reload/version"
|
3
|
+
require "rails_live_reload/config"
|
3
4
|
require "rails_live_reload/client"
|
4
5
|
require "rails_live_reload/watcher"
|
5
|
-
require "rails_live_reload/rails/middleware"
|
6
|
+
require "rails_live_reload/rails/middleware/base"
|
7
|
+
require "rails_live_reload/rails/middleware/long_polling"
|
8
|
+
require "rails_live_reload/rails/middleware/polling"
|
6
9
|
require "rails_live_reload/instrument/metrics_collector"
|
7
10
|
require "rails_live_reload/thread/current_request"
|
8
11
|
require "rails_live_reload/checker"
|
@@ -10,32 +13,13 @@ require "rails_live_reload/command"
|
|
10
13
|
require "rails_live_reload/engine"
|
11
14
|
|
12
15
|
module RailsLiveReload
|
13
|
-
mattr_accessor :files
|
14
|
-
@@files = {}
|
15
|
-
|
16
16
|
mattr_accessor :watcher
|
17
|
-
@@watcher =
|
18
|
-
|
19
|
-
mattr_accessor :url
|
20
|
-
@@url = "/rails/live/reload"
|
21
|
-
|
22
|
-
mattr_accessor :patterns
|
23
|
-
@@patterns = {}
|
24
|
-
|
25
|
-
mattr_accessor :timeout
|
26
|
-
@@timeout = 100
|
17
|
+
@@watcher = {}
|
27
18
|
|
28
|
-
def self.
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
RailsLiveReload.patterns[pattern] = reload
|
19
|
+
def self.middleware
|
20
|
+
case config.mode
|
21
|
+
when :polling then RailsLiveReload::Rails::Middleware::Polling
|
22
|
+
when :long_polling then RailsLiveReload::Rails::Middleware::LongPolling
|
23
|
+
end
|
34
24
|
end
|
35
25
|
end
|
36
|
-
|
37
|
-
# default watch settings
|
38
|
-
RailsLiveReload.setup do |config|
|
39
|
-
config.watch %r{app/views/.+\.(erb|haml|slim)$}
|
40
|
-
config.watch %r{(app|vendor)/(assets|javascript)/\w+/(.+\.(css|js|html|png|jpg|ts|jsx)).*}, reload: :always
|
41
|
-
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.1.2
|
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-05-
|
12
|
+
date: 2022-05-31 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -125,9 +125,12 @@ files:
|
|
125
125
|
- lib/rails_live_reload/checker.rb
|
126
126
|
- lib/rails_live_reload/client.rb
|
127
127
|
- lib/rails_live_reload/command.rb
|
128
|
+
- lib/rails_live_reload/config.rb
|
128
129
|
- lib/rails_live_reload/engine.rb
|
129
130
|
- lib/rails_live_reload/instrument/metrics_collector.rb
|
130
|
-
- lib/rails_live_reload/rails/middleware.rb
|
131
|
+
- lib/rails_live_reload/rails/middleware/base.rb
|
132
|
+
- lib/rails_live_reload/rails/middleware/long_polling.rb
|
133
|
+
- lib/rails_live_reload/rails/middleware/polling.rb
|
131
134
|
- lib/rails_live_reload/thread/current_request.rb
|
132
135
|
- lib/rails_live_reload/version.rb
|
133
136
|
- lib/rails_live_reload/watcher.rb
|
@@ -150,7 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
150
153
|
- !ruby/object:Gem::Version
|
151
154
|
version: '0'
|
152
155
|
requirements: []
|
153
|
-
rubygems_version: 3.
|
156
|
+
rubygems_version: 3.3.7
|
154
157
|
signing_key:
|
155
158
|
specification_version: 4
|
156
159
|
summary: Ruby on Rails Live Reload
|
@@ -1,54 +0,0 @@
|
|
1
|
-
module RailsLiveReload
|
2
|
-
module Rails
|
3
|
-
class Middleware
|
4
|
-
def initialize(app)
|
5
|
-
@app = app
|
6
|
-
end
|
7
|
-
|
8
|
-
def call(env)
|
9
|
-
dup.call!(env)
|
10
|
-
end
|
11
|
-
|
12
|
-
def call!(env)
|
13
|
-
request = Rack::Request.new(env)
|
14
|
-
|
15
|
-
if env["REQUEST_PATH"] == RailsLiveReload.url
|
16
|
-
rails_live_response(request)
|
17
|
-
else
|
18
|
-
@status, @headers, @response = @app.call(env)
|
19
|
-
|
20
|
-
if html? && @response.respond_to?(:[]) && (@status == 500 || (@status.to_s =~ /20./ && request.get?))
|
21
|
-
new_response = make_new_response(@response[0])
|
22
|
-
@headers['Content-Length'] = new_response.bytesize.to_s
|
23
|
-
@response = [new_response]
|
24
|
-
end
|
25
|
-
|
26
|
-
[@status, @headers, @response]
|
27
|
-
end
|
28
|
-
rescue Exception => ex
|
29
|
-
puts ex.message
|
30
|
-
puts ex.backtrace.take(10)
|
31
|
-
raise ex
|
32
|
-
end
|
33
|
-
|
34
|
-
private
|
35
|
-
|
36
|
-
def rails_live_response(request)
|
37
|
-
[
|
38
|
-
200,
|
39
|
-
{ 'Content-Type' => 'application/json' },
|
40
|
-
[ RailsLiveReload::Command.new(request.params).command.to_json ]
|
41
|
-
]
|
42
|
-
end
|
43
|
-
|
44
|
-
def make_new_response(body)
|
45
|
-
body.sub("</head>", "</head>#{RailsLiveReload::Client.js_code}")
|
46
|
-
end
|
47
|
-
|
48
|
-
def html?
|
49
|
-
@headers["Content-Type"].include?("text/html")
|
50
|
-
end
|
51
|
-
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|