Compare commits

..

No commits in common. "master" and "turbolinks_fix" have entirely different histories.

21 changed files with 822 additions and 585 deletions

2
.gitignore vendored
View File

@ -1,7 +1,5 @@
*.gem
.bundle
Gemfile.lock
gemfiles/*.lock
pkg/*
*.orig
tmp/

View File

@ -1,10 +1,11 @@
rvm:
- 1.8.7
- 1.9.2
- 1.9.3
- 2.0.0
branches:
only:
- master
gemfile:
- gemfiles/rails30.gemfile
- gemfiles/rails31.gemfile
- gemfiles/rails32.gemfile
- gemfiles/rails40.gemfile

View File

@ -1,7 +1,11 @@
appraise 'rails30' do
gem 'rails', '~> 3.0.0'
end
appraise 'rails31' do
gem 'rails', '~> 3.1.0'
end
appraise 'rails32' do
gem 'rails', '~> 3.2.0'
end
appraise 'rails40' do
gem 'rails', '~> 4.0.0'
end

View File

@ -1,7 +1,7 @@
# A sample Guardfile
# More info at https://github.com/guard/guard#readme
guard 'rspec', :cli => '-c' do
guard 'rspec', :version => 2, :cli => '-c' do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { "spec" }

View File

@ -1,75 +1,53 @@
# Rack::LiveReload
_This fork is deprecated: Go check out https://github.com/onesupercoder/rack-livereload instead._
<a href="http://travis-ci.org/johnbintz/rack-livereload"><img src="https://secure.travis-ci.org/johnbintz/rack-livereload.png" /></a>
[![Code Climate](https://codeclimate.com/github/johnbintz/rack-livereload.png)](https://codeclimate.com/github/johnbintz/rack-livereload)
Hey, you've got [LiveReload](http://livereload.com/) in my [Rack](http://rack.rubyforge.org/)!
Hey, you've got [LiveReload](http://www.livereload.com/) in my [Rack](http://rack.rubyforge.org/)!
No need for browser extensions anymore! Just plug it in your middleware stack and go!
Even supports browsers without WebSockets!
Use this with [guard-livereload](http://github.com/guard/guard-livereload) for maximum fun!
## Installation
## Install
`gem install rack-livereload`
## Using in...
### Rails
Add the gem to your Gemfile.
In `config/environments/development.rb`:
``` ruby
gem "rack-livereload", group: :development
```
Then add the middleware to your Rails middleware stack by editing your `config/environments/development.rb`.
```ruby
# config/environments/development.rb
MyApp::Application.configure do
# Add Rack::LiveReload to the bottom of the middleware stack with the default options:
config.middleware.insert_after ActionDispatch::Static, Rack::LiveReload
config.middleware.insert_after(ActionDispatch::Static, Rack::LiveReload)
# or, if you're using better_errors:
config.middleware.insert_before Rack::Lock, Rack::LiveReload
# ...or, change some options...
# ...
config.middleware.insert_before(
Rack::Lock, Rack::LiveReload,
:min_delay => 500,
:max_delay => 10000,
:port => 56789,
:host => 'myhost.cool.wow',
:ignore => [ %r{dont/modify\.html$} ]
)
end
```
#### Tweaking the options
```ruby
# Specifying Rack::LiveReload options.
config.middleware.use(Rack::LiveReload,
min_delay : 500, # default 1000
max_delay : 10_000, # default 60_000
live_reload_port : 56789, # default 35729
host : 'myhost.cool.wow',
ignore : [ %r{dont/modify\.html$} ]
)
```
In addition, Rack::LiveReload's position within middleware stack can be
specified by inserting it relative to an exsiting middleware via
`insert_before` or `insert_after`. See the [Rails on Rack: Adding a
Middleware](http://guides.rubyonrails.org/rails_on_rack.html#adding-a-middleware)
section for more detail.
### Sinatra / config.ru
### config.ru/Sinatra
``` ruby
require 'rack-livereload'
use Rack::LiveReload
# ...or...
use Rack::LiveReload, min_delay: 500, ...
use Rack::LiveReload, :min_delay => 500, ...
```
## How it works
The necessary `script` tag to bring in a copy of [livereload.js](https://github.com/livereload/livereload-js) is
injected right after the opening `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
`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!
@ -91,15 +69,17 @@ your browser doesn't need it. The SWF WebSocket implementor won't be loaded unle
WebSockets support or if you force it in the middleware stack:
``` ruby
use Rack::LiveReload, force_swf: true
use Rack::LiveReload, :force_swf => true
```
If you don't want any of the web-sockets-js code included at all, use the `no_swf` option:
``` ruby
use Rack::LiveReload, no_swf: true
use Rack::LiveReload, :no_swf => true
```
Once more browsers support WebSockets than don't, this option will be reversed and you'll have
to explicitly include the Flash shim.
As usual, super-alpha!

View File

@ -2,6 +2,6 @@
source "http://rubygems.org"
gem "rails", "~> 4.0.0"
gem "rails", "~> 3.0.0"
gemspec :path=>"../"

View File

@ -0,0 +1,157 @@
PATH
remote: /Volumes/work/current/open_source/rack-livereload
specs:
rack-livereload (0.3.6)
rack
GEM
remote: http://rubygems.org/
specs:
abstract (1.0.0)
actionmailer (3.0.12)
actionpack (= 3.0.12)
mail (~> 2.2.19)
actionpack (3.0.12)
activemodel (= 3.0.12)
activesupport (= 3.0.12)
builder (~> 2.1.2)
erubis (~> 2.6.6)
i18n (~> 0.5.0)
rack (~> 1.2.5)
rack-mount (~> 0.6.14)
rack-test (~> 0.5.7)
tzinfo (~> 0.3.23)
activemodel (3.0.12)
activesupport (= 3.0.12)
builder (~> 2.1.2)
i18n (~> 0.5.0)
activerecord (3.0.12)
activemodel (= 3.0.12)
activesupport (= 3.0.12)
arel (~> 2.0.10)
tzinfo (~> 0.3.23)
activeresource (3.0.12)
activemodel (= 3.0.12)
activesupport (= 3.0.12)
activesupport (3.0.12)
addressable (2.2.8)
appraisal (0.4.1)
bundler
rake
arel (2.0.10)
builder (2.1.2)
crack (0.3.1)
cucumber (1.2.0)
builder (>= 2.1.2)
diff-lcs (>= 1.1.3)
gherkin (~> 2.10.0)
json (>= 1.4.6)
daemons (1.1.8)
diff-lcs (1.1.3)
em-websocket (0.3.6)
addressable (>= 2.1.1)
eventmachine (>= 0.12.9)
erubis (2.6.6)
abstract (>= 1.0.0)
eventmachine (0.12.10)
ffi (1.0.11)
gherkin (2.10.0)
json (>= 1.4.6)
guard (1.0.3)
ffi (>= 0.5.0)
thor (>= 0.14.6)
guard-cucumber (0.8.0)
cucumber (>= 1.2.0)
guard (>= 0.8.3)
guard-livereload (0.4.2)
em-websocket (>= 0.2.0)
guard (>= 0.10.0)
multi_json (~> 1.0)
guard-rspec (0.7.3)
guard (>= 0.10.0)
httparty (0.8.3)
multi_json (~> 1.0)
multi_xml
i18n (0.5.0)
json (1.7.3)
mail (2.2.19)
activesupport (>= 2.3.6)
i18n (>= 0.4.0)
mime-types (~> 1.16)
treetop (~> 1.4.8)
metaclass (0.0.1)
mime-types (1.18)
mocha (0.11.4)
metaclass (~> 0.0.1)
multi_json (1.3.6)
multi_xml (0.5.1)
polyglot (0.3.3)
rack (1.2.5)
rack-mount (0.6.14)
rack (>= 1.0.0)
rack-test (0.5.7)
rack (>= 1.0)
rails (3.0.12)
actionmailer (= 3.0.12)
actionpack (= 3.0.12)
activerecord (= 3.0.12)
activeresource (= 3.0.12)
activesupport (= 3.0.12)
bundler (~> 1.0)
railties (= 3.0.12)
railties (3.0.12)
actionpack (= 3.0.12)
activesupport (= 3.0.12)
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (~> 0.14.4)
rake (0.9.2.2)
rdoc (3.12)
json (~> 1.4)
rspec (2.10.0)
rspec-core (~> 2.10.0)
rspec-expectations (~> 2.10.0)
rspec-mocks (~> 2.10.0)
rspec-core (2.10.1)
rspec-expectations (2.10.0)
diff-lcs (~> 1.1.3)
rspec-mocks (2.10.1)
shotgun (0.9)
rack (>= 1.0)
sinatra (1.2.8)
rack (~> 1.1)
tilt (>= 1.2.2, < 2.0)
thin (1.3.1)
daemons (>= 1.0.9)
eventmachine (>= 0.12.6)
rack (>= 1.0.0)
thor (0.14.6)
tilt (1.3.3)
treetop (1.4.10)
polyglot
polyglot (>= 0.3.1)
tzinfo (0.3.33)
webmock (1.8.7)
addressable (>= 2.2.7)
crack (>= 0.1.7)
PLATFORMS
ruby
DEPENDENCIES
appraisal (~> 0.4)
cucumber
guard
guard-cucumber
guard-livereload
guard-rspec
httparty
mocha
rack-livereload!
rails (~> 3.0.0)
rake
rspec
shotgun
sinatra
thin
webmock

7
gemfiles/rails31.gemfile Normal file
View File

@ -0,0 +1,7 @@
# This file was generated by Appraisal
source "http://rubygems.org"
gem "rails", "~> 3.1.0"
gemspec :path=>"../"

View File

@ -0,0 +1,169 @@
PATH
remote: /Volumes/work/current/open_source/rack-livereload
specs:
rack-livereload (0.3.6)
rack
GEM
remote: http://rubygems.org/
specs:
actionmailer (3.1.4)
actionpack (= 3.1.4)
mail (~> 2.3.0)
actionpack (3.1.4)
activemodel (= 3.1.4)
activesupport (= 3.1.4)
builder (~> 3.0.0)
erubis (~> 2.7.0)
i18n (~> 0.6)
rack (~> 1.3.6)
rack-cache (~> 1.1)
rack-mount (~> 0.8.2)
rack-test (~> 0.6.1)
sprockets (~> 2.0.3)
activemodel (3.1.4)
activesupport (= 3.1.4)
builder (~> 3.0.0)
i18n (~> 0.6)
activerecord (3.1.4)
activemodel (= 3.1.4)
activesupport (= 3.1.4)
arel (~> 2.2.3)
tzinfo (~> 0.3.29)
activeresource (3.1.4)
activemodel (= 3.1.4)
activesupport (= 3.1.4)
activesupport (3.1.4)
multi_json (~> 1.0)
addressable (2.2.8)
appraisal (0.4.1)
bundler
rake
arel (2.2.3)
builder (3.0.0)
crack (0.3.1)
cucumber (1.2.0)
builder (>= 2.1.2)
diff-lcs (>= 1.1.3)
gherkin (~> 2.10.0)
json (>= 1.4.6)
daemons (1.1.8)
diff-lcs (1.1.3)
em-websocket (0.3.6)
addressable (>= 2.1.1)
eventmachine (>= 0.12.9)
erubis (2.7.0)
eventmachine (0.12.10)
ffi (1.0.11)
gherkin (2.10.0)
json (>= 1.4.6)
guard (1.0.3)
ffi (>= 0.5.0)
thor (>= 0.14.6)
guard-cucumber (0.8.0)
cucumber (>= 1.2.0)
guard (>= 0.8.3)
guard-livereload (0.4.2)
em-websocket (>= 0.2.0)
guard (>= 0.10.0)
multi_json (~> 1.0)
guard-rspec (0.7.3)
guard (>= 0.10.0)
hike (1.2.1)
httparty (0.8.3)
multi_json (~> 1.0)
multi_xml
i18n (0.6.0)
json (1.7.3)
mail (2.3.3)
i18n (>= 0.4.0)
mime-types (~> 1.16)
treetop (~> 1.4.8)
metaclass (0.0.1)
mime-types (1.18)
mocha (0.11.4)
metaclass (~> 0.0.1)
multi_json (1.3.6)
multi_xml (0.5.1)
polyglot (0.3.3)
rack (1.3.6)
rack-cache (1.2)
rack (>= 0.4)
rack-mount (0.8.3)
rack (>= 1.0.0)
rack-protection (1.2.0)
rack
rack-ssl (1.3.2)
rack
rack-test (0.6.1)
rack (>= 1.0)
rails (3.1.4)
actionmailer (= 3.1.4)
actionpack (= 3.1.4)
activerecord (= 3.1.4)
activeresource (= 3.1.4)
activesupport (= 3.1.4)
bundler (~> 1.0)
railties (= 3.1.4)
railties (3.1.4)
actionpack (= 3.1.4)
activesupport (= 3.1.4)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (~> 0.14.6)
rake (0.9.2.2)
rdoc (3.12)
json (~> 1.4)
rspec (2.10.0)
rspec-core (~> 2.10.0)
rspec-expectations (~> 2.10.0)
rspec-mocks (~> 2.10.0)
rspec-core (2.10.1)
rspec-expectations (2.10.0)
diff-lcs (~> 1.1.3)
rspec-mocks (2.10.1)
shotgun (0.9)
rack (>= 1.0)
sinatra (1.3.2)
rack (~> 1.3, >= 1.3.6)
rack-protection (~> 1.2)
tilt (~> 1.3, >= 1.3.3)
sprockets (2.0.4)
hike (~> 1.2)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
thin (1.3.1)
daemons (>= 1.0.9)
eventmachine (>= 0.12.6)
rack (>= 1.0.0)
thor (0.14.6)
tilt (1.3.3)
treetop (1.4.10)
polyglot
polyglot (>= 0.3.1)
tzinfo (0.3.33)
webmock (1.8.7)
addressable (>= 2.2.7)
crack (>= 0.1.7)
PLATFORMS
ruby
DEPENDENCIES
appraisal (~> 0.4)
cucumber
guard
guard-cucumber
guard-livereload
guard-rspec
httparty
mocha
rack-livereload!
rails (~> 3.1.0)
rake
rspec
shotgun
sinatra
thin
webmock

View File

@ -0,0 +1,167 @@
PATH
remote: /Volumes/work/current/open_source/rack-livereload
specs:
rack-livereload (0.3.6)
rack
GEM
remote: http://rubygems.org/
specs:
actionmailer (3.2.3)
actionpack (= 3.2.3)
mail (~> 2.4.4)
actionpack (3.2.3)
activemodel (= 3.2.3)
activesupport (= 3.2.3)
builder (~> 3.0.0)
erubis (~> 2.7.0)
journey (~> 1.0.1)
rack (~> 1.4.0)
rack-cache (~> 1.2)
rack-test (~> 0.6.1)
sprockets (~> 2.1.2)
activemodel (3.2.3)
activesupport (= 3.2.3)
builder (~> 3.0.0)
activerecord (3.2.3)
activemodel (= 3.2.3)
activesupport (= 3.2.3)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activeresource (3.2.3)
activemodel (= 3.2.3)
activesupport (= 3.2.3)
activesupport (3.2.3)
i18n (~> 0.6)
multi_json (~> 1.0)
addressable (2.2.8)
appraisal (0.4.1)
bundler
rake
arel (3.0.2)
builder (3.0.0)
crack (0.3.1)
cucumber (1.2.0)
builder (>= 2.1.2)
diff-lcs (>= 1.1.3)
gherkin (~> 2.10.0)
json (>= 1.4.6)
daemons (1.1.8)
diff-lcs (1.1.3)
em-websocket (0.3.6)
addressable (>= 2.1.1)
eventmachine (>= 0.12.9)
erubis (2.7.0)
eventmachine (0.12.10)
ffi (1.0.11)
gherkin (2.10.0)
json (>= 1.4.6)
guard (1.0.3)
ffi (>= 0.5.0)
thor (>= 0.14.6)
guard-cucumber (0.8.0)
cucumber (>= 1.2.0)
guard (>= 0.8.3)
guard-livereload (0.4.2)
em-websocket (>= 0.2.0)
guard (>= 0.10.0)
multi_json (~> 1.0)
guard-rspec (0.7.3)
guard (>= 0.10.0)
hike (1.2.1)
httparty (0.8.3)
multi_json (~> 1.0)
multi_xml
i18n (0.6.0)
journey (1.0.3)
json (1.7.3)
mail (2.4.4)
i18n (>= 0.4.0)
mime-types (~> 1.16)
treetop (~> 1.4.8)
metaclass (0.0.1)
mime-types (1.18)
mocha (0.11.4)
metaclass (~> 0.0.1)
multi_json (1.3.6)
multi_xml (0.5.1)
polyglot (0.3.3)
rack (1.4.1)
rack-cache (1.2)
rack (>= 0.4)
rack-protection (1.2.0)
rack
rack-ssl (1.3.2)
rack
rack-test (0.6.1)
rack (>= 1.0)
rails (3.2.3)
actionmailer (= 3.2.3)
actionpack (= 3.2.3)
activerecord (= 3.2.3)
activeresource (= 3.2.3)
activesupport (= 3.2.3)
bundler (~> 1.0)
railties (= 3.2.3)
railties (3.2.3)
actionpack (= 3.2.3)
activesupport (= 3.2.3)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (~> 0.14.6)
rake (0.9.2.2)
rdoc (3.12)
json (~> 1.4)
rspec (2.10.0)
rspec-core (~> 2.10.0)
rspec-expectations (~> 2.10.0)
rspec-mocks (~> 2.10.0)
rspec-core (2.10.1)
rspec-expectations (2.10.0)
diff-lcs (~> 1.1.3)
rspec-mocks (2.10.1)
shotgun (0.9)
rack (>= 1.0)
sinatra (1.3.2)
rack (~> 1.3, >= 1.3.6)
rack-protection (~> 1.2)
tilt (~> 1.3, >= 1.3.3)
sprockets (2.1.3)
hike (~> 1.2)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
thin (1.3.1)
daemons (>= 1.0.9)
eventmachine (>= 0.12.6)
rack (>= 1.0.0)
thor (0.14.6)
tilt (1.3.3)
treetop (1.4.10)
polyglot
polyglot (>= 0.3.1)
tzinfo (0.3.33)
webmock (1.8.7)
addressable (>= 2.2.7)
crack (>= 0.1.7)
PLATFORMS
ruby
DEPENDENCIES
appraisal (~> 0.4)
cucumber
guard
guard-cucumber
guard-livereload
guard-rspec
httparty
mocha
rack-livereload!
rails (~> 3.2.0)
rake
rspec
shotgun
sinatra
thin
webmock

View File

@ -317,7 +317,7 @@ var Options;
__options.Options = Options = (function() {
function Options() {
this.host = null;
this.port = RACK_LIVERELOAD_PORT;
this.port = 35729;
this.snipver = null;
this.ext = null;
this.extver = null;

View File

@ -1,6 +1,6 @@
require "rack/livereload"
class Rack::LiveReload
VERSION = '0.3.16'
VERSION = '0.3.8'
end

View File

@ -1,15 +1,46 @@
require 'erb'
require 'rack/livereload/processing_skip_analyzer'
require 'rack/livereload/body_processor'
module Rack
class LiveReload
LIVERELOAD_JS_PATH = '/__rack/livereload.js'
LIVERELOAD_LOCAL_URI = 'http://localhost:35729/livereload.js'
BAD_USER_AGENTS = [ %r{MSIE} ]
attr_reader :app
def initialize(app, options = {})
@app, @options = app, options
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, EOFError
@use_vendored = true
rescue => e
$stderr.puts e.inspect
raise e
end
end
@use_vendored
end
def call(env)
dup._call(env)
end
@ -20,21 +51,55 @@ module Rack
if path == '__rack' && ::File.file?(target = ::File.expand_path("../../../js/#{file}", __FILE__))
deliver_file(target)
else
status, headers, body = result = @app.call(env)
status, headers, body = @app.call(env)
body.close if body.respond_to?(:close)
return result if ProcessingSkipAnalyzer.skip_processing?(result, env, @options)
new_body = [] ; body.each { |line| new_body << line.to_s }
processor = BodyProcessor.new(body, @options)
processor.process!(env)
if !ignored?(env['PATH_INFO']) and !bad_browser?(env['HTTP_USER_AGENT'])
if headers['Content-Type'] and status == 200 and headers['Content-Type'][%r{text/html}]
content_length = 0
headers['Content-Length'] = processor.content_length.to_s
new_body.each do |line|
if !headers['X-Rack-LiveReload'] && line['<head']
host_to_use = (@options[:host] || env['HTTP_HOST'] || 'localhost').gsub(%r{:.*}, '')
if processor.livereload_added
headers['X-Rack-LiveReload'] = '1'
if use_vendored?
src = LIVERELOAD_JS_PATH.dup + "?host=#{host_to_use}"
else
src = LIVERELOAD_LOCAL_URI.dup.gsub('localhost', host_to_use) + '?'
end
[ status, headers, processor.new_body ]
src << "&mindelay=#{@options[:min_delay]}" if @options[:min_delay]
src << "&maxdelay=#{@options[:max_delay]}" if @options[:max_delay]
src << "&port=#{@options[:port]}" if @options[:port]
template = ERB.new(::File.read(::File.expand_path('../../../skel/livereload.html.erb', __FILE__)))
if line['<head']
line.gsub!(/<head[^>]*>/) { |match| %{#{match}#{template.result(binding)}} }
end
headers["X-Rack-LiveReload"] = '1'
end
content_length += line.bytesize
end
headers['Content-Length'] = content_length.to_s
end
end
[ status, headers, new_body ]
end
end
def ignored?(path_info)
@options[:ignore] and @options[:ignore].any? { |filter| path_info[filter] }
end
def bad_browser?(user_agent)
BAD_USER_AGENTS.any? { |pattern| (user_agent || '')[pattern] }
end
private
@ -48,6 +113,14 @@ module Rack
[ 200, { 'Content-Type' => type, 'Content-Length' => ::File.size(file).to_s }, [ ::File.read(file) ] ]
end
def force_swf?
@options[:force_swf]
end
def with_swf?
!@options[:no_swf]
end
end
end

View File

@ -1,116 +0,0 @@
require 'rack/livereload'
module Rack
class LiveReload
class BodyProcessor
LIVERELOAD_JS_PATH = '/__rack/livereload.js'
HEAD_TAG_REGEX = /<head>|<head[^(er)][^<]*>/
LIVERELOAD_PORT = 35729
attr_reader :content_length, :new_body, :livereload_added
def protocol
@options[:protocol] || "http"
end
def livereload_local_uri
"#{protocol}://localhost:#{@options[:live_reload_port]}/livereload.js"
end
def initialize(body, options)
@body, @options = body, options
@options[:live_reload_port] ||= LIVERELOAD_PORT
@processed = false
end
def force_swf?
@options[:force_swf]
end
def with_swf?
!@options[:no_swf]
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, EOFError, IOError
@use_vendored = true
rescue => e
$stderr.puts e.inspect
raise e
end
end
@use_vendored
end
def processed?
@processed
end
def process!(env)
@env = env
@body.close if @body.respond_to?(:close)
@new_body = [] ; @body.each { |line| @new_body << line.to_s }
@content_length = 0
@livereload_added = false
@new_body.each do |line|
if !@livereload_added && line['<head']
line.gsub!(HEAD_TAG_REGEX) { |match| %{#{match}#{template.result(binding)}} }
@livereload_added = true
end
@content_length += line.bytesize
@processed = true
end
end
def app_root
ENV['RAILS_RELATIVE_URL_ROOT'] || ''
end
def host_to_use
(@options[:host] || @env['HTTP_HOST'] || 'localhost').gsub(%r{:.*}, '')
end
def template
ERB.new(::File.read(::File.expand_path('../../../../skel/livereload.html.erb', __FILE__)))
end
def livereload_source
if use_vendored?
src = "#{app_root}#{LIVERELOAD_JS_PATH.dup}?host=#{host_to_use}"
else
src = livereload_local_uri.dup.gsub('localhost', host_to_use) + '?'
end
src << "&amp;mindelay=#{@options[:min_delay]}" if @options[:min_delay]
src << "&amp;maxdelay=#{@options[:max_delay]}" if @options[:max_delay]
src << "&amp;port=#{@options[:port]}" if @options[:port]
src
end
end
end
end

View File

@ -1,49 +0,0 @@
require 'rack/livereload'
module Rack
class LiveReload
class ProcessingSkipAnalyzer
BAD_USER_AGENTS = [ %r{MSIE} ]
def self.skip_processing?(result, env, options)
new(result, env, options).skip_processing?
end
def initialize(result, env, options)
@env, @options = env, options
@status, @headers, @body = result
end
def skip_processing?
!html? || chunked? || inline? || ignored? || bad_browser? || !get?
end
def chunked?
@headers['Transfer-Encoding'] == 'chunked'
end
def inline?
@headers['Content-Disposition'] =~ %r{^inline}
end
def ignored?
path = @env['QUERY_STRING'].empty? ? @env['PATH_INFO'] : "#{@env['PATH_INFO']}?#{@env['QUERY_STRING']}"
@options[:ignore] and @options[:ignore].any? { |filter| path[filter] }
end
def bad_browser?
BAD_USER_AGENTS.any? { |pattern| @env['HTTP_USER_AGENT'] =~ pattern }
end
def html?
@headers['Content-Type'] =~ %r{text/html}
end
def get?
@env['REQUEST_METHOD'] == 'GET'
end
end
end
end

View File

@ -8,7 +8,6 @@ Gem::Specification.new do |s|
s.authors = ["John Bintz"]
s.email = ["john@coswellproductions.com"]
s.homepage = ""
s.license = "MIT"
s.summary = %q{Insert LiveReload into your app easily as Rack middleware}
s.description = %q{Insert LiveReload into your app easily as Rack middleware}
@ -33,7 +32,7 @@ Gem::Specification.new do |s|
s.add_development_dependency "guard-cucumber"
s.add_development_dependency "guard-livereload"
s.add_development_dependency "webmock"
s.add_development_dependency "nokogiri", ("< 1.6" if RUBY_VERSION < "1.9") # Nokogiri >= 1.6 requires Ruby >= 1.9
s.add_development_dependency "nokogiri"
s.add_development_dependency 'appraisal', '~> 0.4'
s.add_runtime_dependency "rack"
end

View File

@ -5,11 +5,8 @@
WEB_SOCKET_FORCE_FLASH = true;
<% end %>
</script>
<script type="text/javascript" src="<%= app_root %>/__rack/swfobject.js"></script>
<script type="text/javascript" src="<%= app_root %>/__rack/web_socket.js"></script>
<script type="text/javascript" src="/__rack/swfobject.js"></script>
<script type="text/javascript" src="/__rack/web_socket.js"></script>
<% end %>
<script type="text/javascript">
RACK_LIVERELOAD_PORT = <%= @options[:live_reload_port] %>;
</script>
<script type="text/javascript" src="<%= livereload_source %>"></script>
<script type="text/javascript" src="<%= src %>"></script>

View File

@ -1,200 +0,0 @@
require 'spec_helper'
require 'nokogiri'
describe Rack::LiveReload::BodyProcessor do
describe 'head tag regex' do
let(:regex) { described_class::HEAD_TAG_REGEX }
subject { regex }
it { should be_kind_of(Regexp) }
it 'only picks a valid <head> tag' do
regex.match("<head></head>").to_s.should eq('<head>')
regex.match("<head><title></title></head>").to_s.should eq('<head>')
regex.match("<head attribute='something'><title></title></head>").to_s.should eq("<head attribute='something'>")
end
it 'responds false when no head tag' do
regex.match("<header></header>").should be_falsey
end
end
let(:processor) { described_class.new(body, options) }
let(:body) { [ page_html ] }
let(:options) { {} }
let(:page_html) { '<head></head>' }
let(:processor_result) do
if !processor.processed?
processor.process!(env)
end
processor
end
subject { processor }
describe "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 'with custom port' do
let(:options) { {:live_reload_port => '12348'}}
context 'exists' do
before do
stub_request(:any, 'localhost:12348/livereload.js')
end
it { should_not use_vendored }
end
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 'text/html' do
before do
processor.stubs(:use_vendored?).returns(true)
end
let(:host) { 'host' }
let(:env) { { 'HTTP_HOST' => host } }
let(:processed_body) { processor_result.new_body.join('') }
let(:length) { processor_result.content_length }
let(:page_html) { '<head></head>' }
context 'vendored' do
it 'should add the vendored livereload js script tag' do
processed_body.should include("script")
processed_body.should include(described_class::LIVERELOAD_JS_PATH)
length.to_s.should == processed_body.length.to_s
described_class::LIVERELOAD_JS_PATH.should_not include(host)
processed_body.should include('swfobject')
processed_body.should include('web_socket')
end
end
context 'at the top of the head tag' do
let(:page_html) { '<head attribute="attribute"><script type="text/javascript" insert="first"></script><script type="text/javascript" insert="before"></script></head>' }
let(:body_dom) { Nokogiri::XML(processed_body) }
it 'should add the livereload js script tag before all other script tags' do
body_dom.at_css("head")[:attribute].should == 'attribute'
body_dom.at_css("script:eq(5)")[:src].should include(described_class::LIVERELOAD_JS_PATH)
body_dom.at_css("script:last-child")[:insert].should == "before"
end
context 'when a relative URL root is specified' do
before do
ENV['RAILS_RELATIVE_URL_ROOT'] = '/a_relative_path'
end
it 'should prepend the relative path to the script src' do
body_dom.at_css("script:eq(5)")[:src].should match(%r{^/a_relative_path/})
end
end
end
describe "LIVERELOAD_PORT value" do
let(:options) { { :live_reload_port => 12345 }}
it "sets the variable at the top of the file" do
processed_body.should include 'RACK_LIVERELOAD_PORT = 12345'
end
end
context 'in header tags' do
let(:page_html) { "<header class='hero'><h1>Just a normal header tag</h1></header>" }
let(:body_dom) { Nokogiri::XML(processed_body) }
it 'should not add the livereload js' do
body_dom.at_css("header")[:class].should == 'hero'
body_dom.css('script').should be_empty
end
end
context 'not vendored' do
before do
processor.stubs(:use_vendored?).returns(false)
end
it 'should add the LR livereload js script tag' do
processed_body.should include("script")
processed_body.should include(processor.livereload_local_uri.gsub('localhost', 'host'))
end
end
context 'set options' do
let(:options) { { :host => new_host, :port => port, :min_delay => min_delay, :max_delay => max_delay } }
let(:min_delay) { 5 }
let(:max_delay) { 10 }
let(:port) { 23 }
let(:new_host) { 'myhost' }
it 'should add the livereload.js script tag' do
processed_body.should include("mindelay=#{min_delay}")
processed_body.should include("maxdelay=#{max_delay}")
processed_body.should include("port=#{port}")
processed_body.should include("host=#{new_host}")
end
end
context 'force flash' do
let(:options) { { :force_swf => true } }
it 'should not add the flash shim' do
processed_body.should include('WEB_SOCKET_FORCE_FLASH')
processed_body.should include('swfobject')
processed_body.should include('web_socket')
end
end
context 'no flash' do
let(:options) { { :no_swf => true } }
it 'should not add the flash shim' do
processed_body.should_not include('swfobject')
processed_body.should_not include('web_socket')
end
end
context 'no host at all' do
let(:env) { {} }
it 'should use localhost' do
processed_body.should include('localhost')
end
end
end
end

View File

@ -1,137 +0,0 @@
require 'spec_helper'
describe Rack::LiveReload::ProcessingSkipAnalyzer do
subject { described_class.new(result, env, options) }
let(:result) { [ status, headers, body ] }
let(:env) { { 'HTTP_USER_AGENT' => user_agent } }
let(:options) { {} }
let(:user_agent) { 'Firefox' }
let(:status) { 200 }
let(:headers) { {} }
let(:body) { [] }
describe '#skip_processing?' do
it "should skip processing" do
subject.skip_processing?.should be_truthy
end
end
describe '#ignored?' do
let(:options) { { :ignore => [ %r{file} ] } }
context 'path contains ignore pattern' do
let(:env) { { 'PATH_INFO' => '/this/file', 'QUERY_STRING' => '' } }
it { should be_ignored }
end
context 'root path' do
let(:env) { { 'PATH_INFO' => '/', 'QUERY_STRING' => '' } }
it { should_not be_ignored }
end
end
describe '#chunked?' do
context 'regular response' do
it { should_not be_chunked }
end
context 'chunked response' do
let(:headers) { { 'Transfer-Encoding' => 'chunked' } }
it { should be_chunked }
end
end
describe '#inline?' do
context 'inline disposition' do
let(:headers) { { 'Content-Disposition' => 'inline; filename=my_inlined_file' } }
it { should be_inline }
end
end
describe '#ignored?' do
let(:path_info) { 'path info' }
let(:query_string) { 'query_string' }
let(:env) { { 'PATH_INFO' => path_info, 'QUERY_STRING' => query_string } }
context 'no ignore set' do
it { should_not be_ignored }
end
context 'ignore set' do
let(:options) { { :ignore => [ %r{#{path_info}} ] } }
it { should be_ignored }
end
context 'ignore set including query_string' do
let(:options) { { :ignore => [ %r{#{path_info}\?#{query_string}} ] } }
it { should be_ignored }
end
end
describe '#bad_browser?' do
context 'Firefox' do
it { should_not be_bad_browser }
end
context 'BAD browser' do
let(:user_agent) { described_class::BAD_USER_AGENTS.first.source }
it { should be_bad_browser }
end
end
describe '#html?' do
context 'HTML content' do
let(:headers) { { 'Content-Type' => 'text/html' } }
it { should be_html }
end
context 'PDF content' do
let(:headers) { { 'Content-Type' => 'application/pdf' } }
it { should_not be_html }
end
end
describe '#get?' do
context 'GET request' do
let(:env) { { 'REQUEST_METHOD' => 'GET' } }
it { should be_get }
end
context 'PUT request' do
let(:env) { { 'REQUEST_METHOD' => 'PUT' } }
it { should_not be_get }
end
context 'POST request' do
let(:env) { { 'REQUEST_METHOD' => 'POST' } }
it { should_not be_get }
end
context 'DELETE request' do
let(:env) { { 'REQUEST_METHOD' => 'DELETE' } }
it { should_not be_get }
end
context 'PATCH request' do
let(:env) { { 'REQUEST_METHOD' => 'PATCH' } }
it { should_not be_get }
end
end
end

View File

@ -7,23 +7,210 @@ describe Rack::LiveReload do
subject { middleware }
it 'should be an app' do
middleware.app.should be == app
end
its(:app) { should == app }
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
let(:ret) { [ 200, { 'Content-Type' => 'image/png' }, [ '<head></head>' ] ] }
before do
app.stubs(:call).with(env).returns(ret)
end
it 'should pass through' do
middleware.call(env).should == ret
end
end
context 'unknown Content-Type' do
let(:ret) { [ 200, {}, [ 'hey ho' ] ] }
before do
app.stubs(:call).with(env).returns(ret)
end
it 'should not break' do
middleware.call(env).should_not raise_error(NoMethodError, /You have a nil object/)
end
end
context 'text/html' do
before do
app.stubs(:call).with(env).returns([ 200, { 'Content-Type' => 'text/html', 'Content-Length' => 0 }, [ page_html ] ])
middleware.stubs(:use_vendored?).returns(true)
end
let(:host) { 'host' }
let(:env) { { 'HTTP_HOST' => host } }
let(:ret) { middleware._call(env) }
let(:body) { ret.last.join }
let(:length) { ret[1]['Content-Length'] }
let(:page_html) { '<head></head>' }
context 'vendored' do
it 'should add the vendored livereload js script tag' do
body.should include("script")
body.should include(described_class::LIVERELOAD_JS_PATH)
length.should == body.length.to_s
described_class::LIVERELOAD_JS_PATH.should_not include(host)
body.should include('swfobject')
body.should include('web_socket')
end
end
context 'at the top of the head tag' do
let(:page_html) { '<head attribute="attribute"><script type="text/javascript" insert="first"></script><script type="text/javascript" insert="before"></script></head>' }
let(:body_dom) { Nokogiri::XML(body) }
it 'should add the livereload js script tag before all other script tags' do
body_dom.at_css("head")[:attribute].should == 'attribute'
body_dom.at_css("script:eq(4)")[:src].should include(described_class::LIVERELOAD_JS_PATH)
body_dom.at_css("script:last-child")[:insert].should == "before"
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
context 'set options' do
let(:middleware) { described_class.new(app, :host => new_host, :port => port, :min_delay => min_delay, :max_delay => max_delay) }
let(:min_delay) { 5 }
let(:max_delay) { 10 }
let(:port) { 23 }
let(:new_host) { 'myhost' }
it 'should add the livereload.js script tag' do
body.should include("mindelay=#{min_delay}")
body.should include("maxdelay=#{max_delay}")
body.should include("port=#{port}")
body.should include("host=#{new_host}")
end
end
context 'force flash' do
let(:middleware) { described_class.new(app, :force_swf => true) }
it 'should not add the flash shim' do
body.should include('WEB_SOCKET_FORCE_FLASH')
body.should include('swfobject')
body.should include('web_socket')
end
end
context 'no flash' do
let(:middleware) { described_class.new(app, :no_swf => true) }
it 'should not add the flash shim' do
body.should_not include('swfobject')
body.should_not include('web_socket')
end
end
context 'no host at all' do
let(:env) { {} }
it 'should use localhost' do
body.should include('localhost')
end
end
context 'ignored' do
let(:options) { { :ignore => [ %r{file} ] } }
context 'not root' do
let(:env) { { 'PATH_INFO' => '/this/file' } }
it 'should have no change' do
body.should_not include('script')
end
end
context 'root' do
let(:env) { { 'PATH_INFO' => '/' } }
it 'should have script' do
body.should include('script')
end
end
end
end
context '/__rack/livereload.js' do
let(:env) { { 'PATH_INFO' => described_class::BodyProcessor::LIVERELOAD_JS_PATH } }
let(:env) { { 'PATH_INFO' => described_class::LIVERELOAD_JS_PATH } }
before do
middleware.expects(:deliver_file).returns(true)
end
it 'should return the js file' do
middleware._call(env).should be_truthy
end
middleware._call(env).should be_true
end
end
describe '#ignored?' do
let(:path_info) { 'path info' }
context 'no ignore set' do
it { should_not be_ignored(path_info) }
end
context 'ignore set' do
let(:options) { { :ignore => [ %r{#{path_info}} ] } }
it { should be_ignored(path_info) }
end
end
describe '#bad_browser?' do
let(:user_agent) { described_class::BAD_USER_AGENTS.first.source }
it { should be_bad_browser(user_agent) }
end
end

View File

@ -1,4 +1,4 @@
require 'mocha/api'
require 'mocha'
require 'webmock/rspec'
require 'rack-livereload'