guard-livereload 2.4.0 → 2.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|