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.
data/Rakefile CHANGED
@@ -1,5 +1,21 @@
1
1
  require "bundler/gem_tasks"
2
2
 
3
- require 'rspec/core/rake_task'
4
- RSpec::Core::RakeTask.new(:spec)
5
- task :default => :spec
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
@@ -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 = "guard-livereload"
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 = "MIT"
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
- additionalWaitingTime = 5;
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
+ })();
@@ -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
- Notifier.notify(msg, title: 'Reloading browser', image: :success)
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
- private
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(options[:host], options[:port], WebSocket, {}) do |ws|
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 "LiveReload is waiting for a browser to connect."
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 "Browser connected." if connections_count == 1
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
- watch(%r{app/views/.+\.(erb|haml|slim)$})
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
@@ -1,5 +1,5 @@
1
1
  module Guard
2
2
  module LiveReloadVersion
3
- VERSION = '2.4.0'
3
+ VERSION = '2.5.0'
4
4
  end
5
5
  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
- send_data("HTTP/1.1 404 Not Found\r\nContent-Type: text/plain\r\nContent-Length: 13\r\n\r\n404 Not Found")
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
- send_data "HTTP/1.1 200 OK\r\nContent-Type: #{_content_type(path)}\r\nContent-Length: #{File.size path}\r\n\r\n"
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 _livereload_js_file
49
- File.expand_path("../../../../js/livereload.js", __FILE__)
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
- require 'spec_helper'
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
- describe "#reload_browser(paths = [])" do
8
- it "displays a message" do
9
- expect(Guard::UI).to receive(:info).
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(::Guard::Notifier).to_not receive(:notify)
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(::Guard::Notifier).to receive(:notify).
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 "each web socket receives send with data containing default options for each path modified" do
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 "each web socket receives send with data containing custom options for each path modified" do
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 "#_connect(ws)" do
50
+ describe '#_connect(ws)' do
47
51
  let(:ws) { double.as_null_object }
48
52
  let(:reactor) { new_live_reactor }
49
53
 
50
- it "displays a message once" do
51
- expect(Guard::UI).to receive(:info).with("Browser connected.").once
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 "increments the connection count" do
57
- expect {
60
+ it 'increments the connection count' do
61
+ expect do
58
62
  reactor.send(:_connect, ws)
59
- }.to change { reactor.connections_count }.from(0).to 1
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