guard-livereload 2.4.0 → 2.5.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/.hound.yml +6 -0
- data/.hound/defaults.yml +265 -0
- data/.hound/overrides.yml +19 -0
- data/.rspec +2 -0
- data/.rubocop.yml +8 -0
- data/.rubocop_merged_for_hound.yml +41 -0
- data/.rubocop_todo.yml +11 -0
- data/Gemfile +7 -3
- data/Guardfile +13 -4
- data/README.md +18 -2
- data/Rakefile +19 -3
- data/guard-livereload.gemspec +3 -4
- data/js/{livereload.js → livereload.js.erb} +4 -3
- data/lib/guard/livereload.rb +9 -4
- data/lib/guard/livereload/reactor.rb +17 -12
- data/lib/guard/livereload/snippet.rb +23 -0
- data/lib/guard/livereload/templates/Guardfile +34 -4
- data/lib/guard/livereload/version.rb +1 -1
- data/lib/guard/livereload/websocket.rb +27 -10
- data/spec/lib/guard/livereload/reactor_spec.rb +21 -17
- data/spec/lib/guard/livereload/snippet_spec.rb +45 -0
- data/spec/lib/guard/livereload/template_spec.rb +67 -0
- data/spec/lib/guard/livereload_spec.rb +77 -25
- data/spec/spec_helper.rb +26 -3
- metadata +30 -32
data/Rakefile
CHANGED
@@ -1,5 +1,21 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
|
3
|
-
require
|
4
|
-
|
5
|
-
|
3
|
+
require "rspec/core/rake_task"
|
4
|
+
|
5
|
+
def ci?
|
6
|
+
ENV["CI"] == "true"
|
7
|
+
end
|
8
|
+
|
9
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
10
|
+
t.verbose = ci?
|
11
|
+
end
|
12
|
+
|
13
|
+
default_tasks = [:spec]
|
14
|
+
|
15
|
+
unless ci?
|
16
|
+
require "rubocop/rake_task"
|
17
|
+
RuboCop::RakeTask.new(:rubocop)
|
18
|
+
default_tasks << :rubocop
|
19
|
+
end
|
20
|
+
|
21
|
+
task default: default_tasks
|
data/guard-livereload.gemspec
CHANGED
@@ -4,24 +4,23 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'guard/livereload/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
|
-
s.name =
|
7
|
+
s.name = 'guard-livereload'
|
8
8
|
s.version = Guard::LiveReloadVersion::VERSION
|
9
9
|
s.author = 'Thibaud Guillaume-Gentil'
|
10
10
|
s.email = 'thibaud@thibaud.me'
|
11
11
|
s.summary = 'Guard plugin for livereload'
|
12
12
|
s.description = "Guard::LiveReload automatically reloads your browser when 'view' files are modified."
|
13
13
|
s.homepage = 'https://rubygems.org/gems/guard-livereload'
|
14
|
-
s.license =
|
14
|
+
s.license = 'MIT'
|
15
15
|
|
16
16
|
s.files = `git ls-files`.split($/)
|
17
17
|
s.test_files = s.files.grep(%r{^spec/})
|
18
18
|
s.require_path = 'lib'
|
19
19
|
|
20
20
|
s.add_dependency 'guard', '~> 2.8'
|
21
|
+
s.add_dependency 'guard-compat', '~> 1.0'
|
21
22
|
s.add_dependency 'em-websocket', '~> 0.5'
|
22
23
|
s.add_dependency 'multi_json', '~> 1.8'
|
23
24
|
|
24
25
|
s.add_development_dependency 'bundler', '>= 1.3.5'
|
25
|
-
s.add_development_dependency 'rake'
|
26
|
-
s.add_development_dependency 'rspec'
|
27
26
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
// <%= "guard-livereload options: #{options.inspect}" %>
|
1
2
|
(function() {
|
2
3
|
var __customevents = {}, __protocol = {}, __connector = {}, __timer = {}, __options = {}, __reloader = {}, __livereload = {}, __less = {}, __startup = {};
|
3
4
|
|
@@ -732,9 +733,9 @@
|
|
732
733
|
return this.waitUntilCssLoads(clone, function() {
|
733
734
|
var additionalWaitingTime;
|
734
735
|
if (/AppleWebKit/.test(navigator.userAgent)) {
|
735
|
-
|
736
|
+
additionalWaitingTime = <%= options[:js_apple_webkit_extra_wait_time] || 5 %>;
|
736
737
|
} else {
|
737
|
-
additionalWaitingTime = 200
|
738
|
+
additionalWaitingTime = <%= options[:js_default_extra_wait_time] || 200 %>;
|
738
739
|
}
|
739
740
|
return _this.Timer.start(additionalWaitingTime, function() {
|
740
741
|
var _ref;
|
@@ -1052,4 +1053,4 @@
|
|
1052
1053
|
CustomEvents.bind(document, 'LiveReloadShutDown', function() {
|
1053
1054
|
return LiveReload.shutDown();
|
1054
1055
|
});
|
1055
|
-
})();
|
1056
|
+
})();
|
data/lib/guard/livereload.rb
CHANGED
@@ -1,22 +1,28 @@
|
|
1
|
-
require 'guard'
|
2
|
-
require 'guard/plugin'
|
1
|
+
require 'guard/compat/plugin'
|
3
2
|
|
4
3
|
module Guard
|
5
4
|
class LiveReload < Plugin
|
6
5
|
require 'guard/livereload/websocket'
|
7
6
|
require 'guard/livereload/reactor'
|
7
|
+
require 'guard/livereload/snippet'
|
8
8
|
|
9
9
|
attr_accessor :reactor, :options
|
10
10
|
|
11
11
|
def initialize(options = {})
|
12
12
|
super
|
13
|
+
|
14
|
+
js_path = File.expand_path('../../../js/livereload.js.erb', __FILE__)
|
13
15
|
@options = {
|
14
16
|
host: '0.0.0.0',
|
15
17
|
port: '35729',
|
16
18
|
apply_css_live: true,
|
17
19
|
override_url: false,
|
18
|
-
grace_period: 0
|
20
|
+
grace_period: 0,
|
21
|
+
js_template: js_path
|
19
22
|
}.merge(options)
|
23
|
+
|
24
|
+
js_path = @options[:js_template]
|
25
|
+
@options[:livereload_js_path] = Snippet.new(js_path, @options).path
|
20
26
|
end
|
21
27
|
|
22
28
|
def start
|
@@ -31,6 +37,5 @@ module Guard
|
|
31
37
|
sleep options[:grace_period]
|
32
38
|
reactor.reload_browser(paths)
|
33
39
|
end
|
34
|
-
|
35
40
|
end
|
36
41
|
end
|
@@ -18,19 +18,19 @@ module Guard
|
|
18
18
|
|
19
19
|
def reload_browser(paths = [])
|
20
20
|
msg = "Reloading browser: #{paths.join(' ')}"
|
21
|
-
UI.info msg
|
21
|
+
Compat::UI.info msg
|
22
22
|
if options[:notify]
|
23
|
-
|
23
|
+
Compat::UI.notify(msg, title: 'Reloading browser', image: :success)
|
24
24
|
end
|
25
25
|
|
26
26
|
paths.each do |path|
|
27
27
|
data = _data(path)
|
28
|
-
UI.debug(data)
|
28
|
+
Compat::UI.debug(data)
|
29
29
|
web_sockets.each { |ws| ws.send(MultiJson.encode(data)) }
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
|
33
|
+
private
|
34
34
|
|
35
35
|
def _data(path)
|
36
36
|
data = {
|
@@ -45,20 +45,26 @@ module Guard
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def _start_reactor
|
48
|
-
EventMachine.epoll
|
48
|
+
EventMachine.epoll if EventMachine.epoll?
|
49
|
+
EventMachine.kqueue if EventMachine.kqueue?
|
49
50
|
EventMachine.run do
|
50
|
-
EventMachine.start_server(
|
51
|
+
EventMachine.start_server(
|
52
|
+
options[:host],
|
53
|
+
options[:port],
|
54
|
+
WebSocket,
|
55
|
+
options
|
56
|
+
) do |ws|
|
51
57
|
ws.onopen { _connect(ws) }
|
52
58
|
ws.onclose { _disconnect(ws) }
|
53
59
|
ws.onmessage { |msg| _print_message(msg) }
|
54
60
|
end
|
55
|
-
UI.info
|
61
|
+
Compat::UI.info 'LiveReload is waiting for a browser to connect.'
|
56
62
|
end
|
57
63
|
end
|
58
64
|
|
59
65
|
def _connect(ws)
|
60
66
|
@connections_count += 1
|
61
|
-
UI.info
|
67
|
+
Compat::UI.info 'Browser connected.' if connections_count == 1
|
62
68
|
|
63
69
|
ws.send MultiJson.encode(
|
64
70
|
command: 'hello',
|
@@ -67,8 +73,8 @@ module Guard
|
|
67
73
|
)
|
68
74
|
@web_sockets << ws
|
69
75
|
rescue
|
70
|
-
UI.error $!
|
71
|
-
UI.error $!.backtrace
|
76
|
+
Compat::UI.error $!
|
77
|
+
Compat::UI.error $!.backtrace
|
72
78
|
end
|
73
79
|
|
74
80
|
def _disconnect(ws)
|
@@ -77,9 +83,8 @@ module Guard
|
|
77
83
|
|
78
84
|
def _print_message(message)
|
79
85
|
message = MultiJson.decode(message)
|
80
|
-
UI.info "Browser URL: #{message['url']}" if message['command'] == 'url'
|
86
|
+
Compat::UI.info "Browser URL: #{message['url']}" if message['command'] == 'url'
|
81
87
|
end
|
82
|
-
|
83
88
|
end
|
84
89
|
end
|
85
90
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
require 'guard/compat/plugin'
|
5
|
+
|
6
|
+
module Guard
|
7
|
+
class LiveReload < Plugin
|
8
|
+
class Snippet
|
9
|
+
attr_reader :path
|
10
|
+
attr_reader :options
|
11
|
+
|
12
|
+
def initialize(template, options)
|
13
|
+
@options = options # for ERB context
|
14
|
+
tmpfile = Tempfile.new('livereload.js')
|
15
|
+
source = IO.read(template)
|
16
|
+
data = ERB.new(source).result(binding)
|
17
|
+
tmpfile.write(data)
|
18
|
+
tmpfile.close
|
19
|
+
@path = tmpfile.path
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,8 +1,38 @@
|
|
1
1
|
guard 'livereload' do
|
2
|
-
|
2
|
+
extensions = {
|
3
|
+
css: :css,
|
4
|
+
scss: :css,
|
5
|
+
sass: :css,
|
6
|
+
js: :js,
|
7
|
+
coffee: :js,
|
8
|
+
html: :html,
|
9
|
+
png: :png,
|
10
|
+
gif: :gif,
|
11
|
+
jpg: :jpg,
|
12
|
+
jpeg: :jpeg,
|
13
|
+
# less: :less, # uncomment if you want LESS stylesheets done in browser
|
14
|
+
}
|
15
|
+
|
16
|
+
rails_view_exts = %w(erb haml slim)
|
17
|
+
|
18
|
+
# file types LiveReload may optimize refresh for
|
19
|
+
compiled_exts = extensions.values.uniq
|
20
|
+
watch(%r{public/.+\.(#{compiled_exts * '|'})})
|
21
|
+
|
22
|
+
extensions.each do |ext, type|
|
23
|
+
watch(%r{
|
24
|
+
(?:app|vendor)
|
25
|
+
(?:/assets/\w+/(?<path>[^.]+) # path+base without extension
|
26
|
+
(?<ext>\.#{ext})) # matching extension (must be first encountered)
|
27
|
+
(?:\.\w+|$) # other extensions
|
28
|
+
}x) do |m|
|
29
|
+
path = m[1]
|
30
|
+
"/assets/#{path}.#{type}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# file needing a full reload of the page anyway
|
35
|
+
watch(%r{app/views/.+\.(#{rails_view_exts * '|'})$})
|
3
36
|
watch(%r{app/helpers/.+\.rb})
|
4
|
-
watch(%r{public/.+\.(css|js|html)})
|
5
37
|
watch(%r{config/locales/.+\.yml})
|
6
|
-
# Rails Assets Pipeline
|
7
|
-
watch(%r{(app|vendor)(/assets/\w+/(.+\.(css|js|html|png|jpg))).*}) { |m| "/assets/#{m[3]}" }
|
8
38
|
end
|
@@ -6,6 +6,10 @@ require 'uri'
|
|
6
6
|
module Guard
|
7
7
|
class LiveReload
|
8
8
|
class WebSocket < EventMachine::WebSocket::Connection
|
9
|
+
def initialize(options)
|
10
|
+
@livereload_js_path = options[:livereload_js_path]
|
11
|
+
super
|
12
|
+
end
|
9
13
|
|
10
14
|
def dispatch(data)
|
11
15
|
parser = Http::Parser.new
|
@@ -14,14 +18,9 @@ module Guard
|
|
14
18
|
request_path = '.' + URI.parse(parser.request_url).path
|
15
19
|
request_path += '/index.html' if File.directory? request_path
|
16
20
|
if parser.http_method != 'GET' || parser.upgrade?
|
17
|
-
super #pass the request to websocket
|
18
|
-
elsif request_path == './livereload.js'
|
19
|
-
_serve_file(_livereload_js_file)
|
20
|
-
elsif File.readable?(request_path) && !File.directory?(request_path)
|
21
|
-
_serve_file(request_path)
|
21
|
+
super # pass the request to websocket
|
22
22
|
else
|
23
|
-
|
24
|
-
close_connection_after_writing
|
23
|
+
_serve(request_path)
|
25
24
|
end
|
26
25
|
end
|
27
26
|
|
@@ -29,7 +28,15 @@ module Guard
|
|
29
28
|
|
30
29
|
def _serve_file(path)
|
31
30
|
UI.debug "Serving file #{path}"
|
32
|
-
|
31
|
+
|
32
|
+
data = [
|
33
|
+
'HTTP/1.1 200 OK',
|
34
|
+
'Content-Type: %s',
|
35
|
+
'Content-Length: %s',
|
36
|
+
'',
|
37
|
+
'']
|
38
|
+
data = format(data * "\r\n", _content_type(path), File.size(path))
|
39
|
+
send_data(data)
|
33
40
|
stream_file_data(path).callback { close_connection_after_writing }
|
34
41
|
end
|
35
42
|
|
@@ -45,10 +52,20 @@ module Guard
|
|
45
52
|
end
|
46
53
|
end
|
47
54
|
|
48
|
-
def
|
49
|
-
|
55
|
+
def _livereload_js_path
|
56
|
+
@livereload_js_path
|
50
57
|
end
|
51
58
|
|
59
|
+
def _serve(path)
|
60
|
+
return _serve_file(_livereload_js_path) if path == './livereload.js'
|
61
|
+
return _serve_file(path) if _readable_file(path)
|
62
|
+
send_data("HTTP/1.1 404 Not Found\r\nContent-Type: text/plain\r\nContent-Length: 13\r\n\r\n404 Not Found")
|
63
|
+
close_connection_after_writing
|
64
|
+
end
|
65
|
+
|
66
|
+
def _readable_file(path)
|
67
|
+
File.readable?(path) && !File.directory?(path)
|
68
|
+
end
|
52
69
|
end
|
53
70
|
end
|
54
71
|
end
|
@@ -1,28 +1,32 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
describe Guard::LiveReload::Reactor do
|
1
|
+
RSpec.describe Guard::LiveReload::Reactor do
|
4
2
|
let(:paths) { %w[stylesheets/layout.css stylesheets/style.css] }
|
5
|
-
before { allow(Guard::UI).to receive(:info) }
|
6
3
|
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
before do
|
5
|
+
allow(Guard::Compat::UI).to receive(:info)
|
6
|
+
allow(Guard::Compat::UI).to receive(:debug)
|
7
|
+
allow(Guard::Compat::UI).to receive(:error)
|
8
|
+
allow(Guard::Compat::UI).to receive(:warning)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#reload_browser(paths = [])' do
|
12
|
+
it 'displays a message' do
|
13
|
+
expect(Guard::Compat::UI).to receive(:info).
|
10
14
|
with('Reloading browser: stylesheets/layout.css stylesheets/style.css')
|
11
15
|
new_live_reactor.reload_browser(paths)
|
12
16
|
end
|
13
17
|
|
14
18
|
it 'by default does not send notification' do
|
15
|
-
expect(
|
19
|
+
expect(Guard::Compat::UI).to_not receive(:notify)
|
16
20
|
new_live_reactor.reload_browser(paths)
|
17
21
|
end
|
18
22
|
|
19
23
|
it 'optionally pushes notification' do
|
20
|
-
expect(
|
24
|
+
expect(Guard::Compat::UI).to receive(:notify).
|
21
25
|
with(kind_of(String), have_key(:title))
|
22
26
|
new_live_reactor(notify: true).reload_browser(paths)
|
23
27
|
end
|
24
28
|
|
25
|
-
it
|
29
|
+
it 'each web socket receives send with data containing default options for each path modified' do
|
26
30
|
reactor = new_live_reactor
|
27
31
|
paths.each do |path|
|
28
32
|
reactor.web_sockets.each do |ws|
|
@@ -32,7 +36,7 @@ describe Guard::LiveReload::Reactor do
|
|
32
36
|
reactor.reload_browser(paths)
|
33
37
|
end
|
34
38
|
|
35
|
-
it
|
39
|
+
it 'each web socket receives send with data containing custom options for each path modified' do
|
36
40
|
reactor = new_live_reactor(apply_css_live: false, apply_js_live: false)
|
37
41
|
paths.each do |path|
|
38
42
|
reactor.web_sockets.each do |ws|
|
@@ -43,20 +47,20 @@ describe Guard::LiveReload::Reactor do
|
|
43
47
|
end
|
44
48
|
end
|
45
49
|
|
46
|
-
describe
|
50
|
+
describe '#_connect(ws)' do
|
47
51
|
let(:ws) { double.as_null_object }
|
48
52
|
let(:reactor) { new_live_reactor }
|
49
53
|
|
50
|
-
it
|
51
|
-
expect(Guard::UI).to receive(:info).with(
|
54
|
+
it 'displays a message once' do
|
55
|
+
expect(Guard::Compat::UI).to receive(:info).with('Browser connected.').once
|
52
56
|
reactor.send(:_connect, ws)
|
53
57
|
reactor.send(:_connect, ws)
|
54
58
|
end
|
55
59
|
|
56
|
-
it
|
57
|
-
expect
|
60
|
+
it 'increments the connection count' do
|
61
|
+
expect do
|
58
62
|
reactor.send(:_connect, ws)
|
59
|
-
|
63
|
+
end.to change { reactor.connections_count }.from(0).to 1
|
60
64
|
end
|
61
65
|
end
|
62
66
|
|
@@ -0,0 +1,45 @@
|
|
1
|
+
RSpec.describe Guard::LiveReload::Snippet do
|
2
|
+
let(:options) { { foo: :bar } }
|
3
|
+
|
4
|
+
let(:template) { '/foo/livereload.js.erb' }
|
5
|
+
let(:contents) { '// <%= options[:foo] %>' }
|
6
|
+
|
7
|
+
subject { described_class.new(template, options) }
|
8
|
+
|
9
|
+
let(:tmpfile) { instance_double(Tempfile) }
|
10
|
+
|
11
|
+
before do
|
12
|
+
allow(File).to receive(:expand_path) do |*args|
|
13
|
+
fail "stub called for File.expand_path(#{args.map(&:inspect) * ','})"
|
14
|
+
end
|
15
|
+
|
16
|
+
allow(IO).to receive(:read) do |*args|
|
17
|
+
fail "stub called for IO.read(#{args.map(&:inspect) * ','})"
|
18
|
+
end
|
19
|
+
|
20
|
+
allow(IO).to receive(:read).with(template).and_return(contents)
|
21
|
+
|
22
|
+
allow(Tempfile).to receive(:new).and_return(tmpfile)
|
23
|
+
allow(tmpfile).to receive(:path).and_return('/tmp/livereload-123')
|
24
|
+
allow(tmpfile).to receive(:write)
|
25
|
+
allow(tmpfile).to receive(:close)
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#initialize' do
|
29
|
+
it 'evaluates the js snippet file' do
|
30
|
+
expect(tmpfile).to receive(:write).with('// bar')
|
31
|
+
subject
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'closes the tmpfile' do
|
35
|
+
expect(tmpfile).to receive(:close)
|
36
|
+
subject
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#path' do
|
41
|
+
it 'is set to a tmpfile with the ERB result' do
|
42
|
+
expect(subject.path).to eq '/tmp/livereload-123'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|