detection and use of local livereload if available

This commit is contained in:
John Bintz 2011-11-08 09:48:34 -05:00
parent 38a3396cf2
commit 92479fdaff
5 changed files with 103 additions and 11 deletions

View File

@ -41,11 +41,20 @@ use Rack::LiveReload, :min_delay => 500, ...
## How it works ## How it works
The necessary `script` tag to bring in a vendored copy of [livereload.js](https://github.com/livereload/livereload-js) is The necessary `script` tag to bring in a copy of [livereload.js](https://github.com/livereload/livereload-js) is
injected right before the closing `head` tag in any `text/html` pages that come through. The `script` tag is built in injected right before the closing `head` tag in any `text/html` pages that come through. The `script` tag is built in
such a way that the `HTTP_HOST` is used as the LiveReload host, so you can connect from external machines (say, to such a way that the `HTTP_HOST` is used as the LiveReload host, so you can connect from external machines (say, to
`mycomputer:3000` instead of `localhost:3000`) and as long as the LiveReload port is accessible from the external machine, `mycomputer:3000` instead of `localhost:3000`) and as long as the LiveReload port is accessible from the external machine,
you'll connect and be LiveReloading away! you'll connect and be LiveReloading away!
### Which LiveReload script does it use?
* If you've got a LiveReload watcher running on the same machine as the app that responds
to `http://localhost:35729/livereload.js`, that gets used, with the hostname being changed when
injected into the HTML page.
* If you don't, the copy vendored with rack-livereload is used.
* You can force the use of either one (and save on the cost of checking to see if that file
is available) with the middleware option `:source => :vendored` or `:source => :livereload`.
As usual, super-alpha! As usual, super-alpha!

View File

@ -1,6 +1,7 @@
module Rack module Rack
class LiveReload class LiveReload
LIVERELOAD_JS_PATH = '/__rack/livereload.js' LIVERELOAD_JS_PATH = '/__rack/livereload.js'
LIVERELOAD_LOCAL_URI = 'http://localhost:35729/livereload.js'
attr_reader :app attr_reader :app
@ -9,6 +10,31 @@ module Rack
@options = options @options = options
end end
def use_vendored?
return @use_vendored if @use_vendored
if @options[:source]
@use_vendored = (@options[:source] == :vendored)
else
require 'net/http'
require 'uri'
uri = URI.parse(LIVERELOAD_LOCAL_URI)
http = Net::HTTP.new(uri.host, uri.port)
http.read_timeout = 1
begin
http.send_request('GET', uri.path)
@use_vendored = false
rescue Timeout::Error, Errno::ECONNREFUSED
@use_vendored = true
end
end
@use_vendored
end
def call(env) def call(env)
if env['PATH_INFO'] == LIVERELOAD_JS_PATH if env['PATH_INFO'] == LIVERELOAD_JS_PATH
deliver_file(::File.expand_path('../../../js/livereload.js', __FILE__)) deliver_file(::File.expand_path('../../../js/livereload.js', __FILE__))
@ -21,12 +47,14 @@ module Rack
body.each do |line| body.each do |line|
if !headers['X-Rack-LiveReload'] && line['</head>'] if !headers['X-Rack-LiveReload'] && line['</head>']
src = LIVERELOAD_JS_PATH.dup host_to_use = @options[:host] || env['HTTP_HOST'].gsub(%r{:.*}, '')
if @options[:host]
src << "?host=#{@options[:host]}" if use_vendored?
src = LIVERELOAD_JS_PATH.dup + "?host=#{host_to_use}"
else else
src << "?host=#{env['HTTP_HOST'].gsub(%r{:.*}, '')}" if env['HTTP_HOST'] src = LIVERELOAD_LOCAL_URI.dup.gsub('localhost', host_to_use) + '?'
end end
src << "&mindelay=#{@options[:min_delay]}" if @options[:min_delay] src << "&mindelay=#{@options[:min_delay]}" if @options[:min_delay]
src << "&maxdelay=#{@options[:max_delay]}" if @options[:max_delay] src << "&maxdelay=#{@options[:max_delay]}" if @options[:max_delay]
src << "&port=#{@options[:port]}" if @options[:port] src << "&port=#{@options[:port]}" if @options[:port]

View File

@ -28,6 +28,7 @@ Gem::Specification.new do |s|
s.add_development_dependency "mocha" s.add_development_dependency "mocha"
s.add_development_dependency "guard" s.add_development_dependency "guard"
s.add_development_dependency "guard-rspec" s.add_development_dependency "guard-rspec"
s.add_development_dependency "webmock"
s.add_runtime_dependency "rack" s.add_runtime_dependency "rack"
end end

View File

@ -1,7 +1,7 @@
require 'spec_helper' require 'spec_helper'
describe Rack::LiveReload do describe Rack::LiveReload do
let(:middleware) { described_class.new(app) } let(:middleware) { described_class.new(app, options) }
let(:app) { stub } let(:app) { stub }
subject { middleware } subject { middleware }
@ -9,6 +9,37 @@ describe Rack::LiveReload do
its(:app) { should == app } its(:app) { should == app }
let(:env) { {} } let(:env) { {} }
let(:options) { {} }
describe described_class::LIVERELOAD_LOCAL_URI do
context 'does not exist' do
before do
stub_request(:any, 'localhost:35729/livereload.js').to_timeout
end
it { should use_vendored }
end
context 'exists' do
before do
stub_request(:any, 'localhost:35729/livereload.js')
end
it { should_not use_vendored }
end
context 'specify vendored' do
let(:options) { { :source => :vendored } }
it { should use_vendored }
end
context 'specify LR' do
let(:options) { { :source => :livereload } }
it { should_not use_vendored }
end
end
context 'not text/html' do context 'not text/html' do
let(:ret) { [ 200, { 'Content-Type' => 'image/png' }, [ '<head></head>' ] ] } let(:ret) { [ 200, { 'Content-Type' => 'image/png' }, [ '<head></head>' ] ] }
@ -25,6 +56,7 @@ describe Rack::LiveReload do
context 'text/html' do context 'text/html' do
before do before do
app.stubs(:call).with(env).returns([ 200, { 'Content-Type' => 'text/html', 'Content-Length' => 0 }, [ '<head></head>' ] ]) app.stubs(:call).with(env).returns([ 200, { 'Content-Type' => 'text/html', 'Content-Length' => 0 }, [ '<head></head>' ] ])
middleware.stubs(:use_vendored?).returns(true)
end end
let(:host) { 'host' } let(:host) { 'host' }
@ -34,13 +66,26 @@ describe Rack::LiveReload do
let(:body) { ret.last.join } let(:body) { ret.last.join }
let(:length) { ret[1]['Content-Length'] } let(:length) { ret[1]['Content-Length'] }
it 'should add the livereload js script tag' do context 'vendored' do
body.should include("script") it 'should add the vendored livereload js script tag' do
body.should include(described_class::LIVERELOAD_JS_PATH) body.should include("script")
body.should include(described_class::LIVERELOAD_JS_PATH)
length.should == body.length.to_s length.should == body.length.to_s
described_class::LIVERELOAD_JS_PATH.should_not include(host) described_class::LIVERELOAD_JS_PATH.should_not include(host)
end
end
context 'not vendored' do
before do
middleware.stubs(:use_vendored?).returns(false)
end
it 'should add the LR livereload js script tag' do
body.should include("script")
body.should include(described_class::LIVERELOAD_LOCAL_URI.gsub('localhost', 'host'))
end
end end
context 'set options' do context 'set options' do

View File

@ -1,7 +1,16 @@
require 'mocha' require 'mocha'
require 'webmock/rspec'
require 'rack-livereload' require 'rack-livereload'
RSpec.configure do |c| RSpec.configure do |c|
c.mock_with :mocha c.mock_with :mocha
end end
module RSpec::Matchers
define :use_vendored do
match do |subject|
subject.use_vendored?
end
end
end