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 *.gem
.bundle .bundle
Gemfile.lock Gemfile.lock
gemfiles/*.lock
pkg/* pkg/*
*.orig *.orig
tmp/

View File

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

View File

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

View File

@ -1,7 +1,7 @@
# A sample Guardfile # A sample Guardfile
# More info at https://github.com/guard/guard#readme # 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{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { "spec" } 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> <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! No need for browser extensions anymore! Just plug it in your middleware stack and go!
Even supports browsers without WebSockets! Even supports browsers without WebSockets!
Use this with [guard-livereload](http://github.com/guard/guard-livereload) for maximum fun! Use this with [guard-livereload](http://github.com/guard/guard-livereload) for maximum fun!
## Installation ## Install
`gem install rack-livereload`
## Using in...
### Rails ### Rails
Add the gem to your Gemfile.
```ruby In `config/environments/development.rb`:
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
``` ruby
MyApp::Application.configure do 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: # ...or, change some options...
config.middleware.insert_before Rack::Lock, Rack::LiveReload
# ... 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 end
``` ```
#### Tweaking the options ### config.ru/Sinatra
```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
``` ruby ``` ruby
require 'rack-livereload' require 'rack-livereload'
use Rack::LiveReload use Rack::LiveReload
# ...or... # ...or...
use Rack::LiveReload, min_delay: 500, ... use Rack::LiveReload, :min_delay => 500, ...
``` ```
## How it works ## How it works
The necessary `script` tag to bring in a 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 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 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!
@ -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: WebSockets support or if you force it in the middleware stack:
``` ruby ``` 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: If you don't want any of the web-sockets-js code included at all, use the `no_swf` option:
``` ruby ``` 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 Once more browsers support WebSockets than don't, this option will be reversed and you'll have
to explicitly include the Flash shim. to explicitly include the Flash shim.
As usual, super-alpha!

View File

@ -2,6 +2,6 @@
source "http://rubygems.org" source "http://rubygems.org"
gem "rails", "~> 4.0.0" gem "rails", "~> 3.0.0"
gemspec :path=>"../" 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() { __options.Options = Options = (function() {
function Options() { function Options() {
this.host = null; this.host = null;
this.port = RACK_LIVERELOAD_PORT; this.port = 35729;
this.snipver = null; this.snipver = null;
this.ext = null; this.ext = null;
this.extver = null; this.extver = null;

View File

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

View File

@ -1,15 +1,46 @@
require 'erb' require 'erb'
require 'rack/livereload/processing_skip_analyzer'
require 'rack/livereload/body_processor'
module Rack module Rack
class LiveReload class LiveReload
LIVERELOAD_JS_PATH = '/__rack/livereload.js'
LIVERELOAD_LOCAL_URI = 'http://localhost:35729/livereload.js'
BAD_USER_AGENTS = [ %r{MSIE} ]
attr_reader :app attr_reader :app
def initialize(app, options = {}) def initialize(app, options = {})
@app, @options = app, options @app, @options = app, 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, EOFError
@use_vendored = true
rescue => e
$stderr.puts e.inspect
raise e
end
end
@use_vendored
end
def call(env) def call(env)
dup._call(env) dup._call(env)
end end
@ -20,23 +51,57 @@ module Rack
if path == '__rack' && ::File.file?(target = ::File.expand_path("../../../js/#{file}", __FILE__)) if path == '__rack' && ::File.file?(target = ::File.expand_path("../../../js/#{file}", __FILE__))
deliver_file(target) deliver_file(target)
else 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) if !ignored?(env['PATH_INFO']) and !bad_browser?(env['HTTP_USER_AGENT'])
processor.process!(env) 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 if use_vendored?
headers['X-Rack-LiveReload'] = '1' src = LIVERELOAD_JS_PATH.dup + "?host=#{host_to_use}"
else
src = LIVERELOAD_LOCAL_URI.dup.gsub('localhost', host_to_use) + '?'
end
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 end
[ status, headers, processor.new_body ] [ status, headers, new_body ]
end end
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 private
def deliver_file(file) def deliver_file(file)
type = case ::File.extname(file) type = case ::File.extname(file)
@ -48,6 +113,14 @@ module Rack
[ 200, { 'Content-Type' => type, 'Content-Length' => ::File.size(file).to_s }, [ ::File.read(file) ] ] [ 200, { 'Content-Type' => type, 'Content-Length' => ::File.size(file).to_s }, [ ::File.read(file) ] ]
end end
def force_swf?
@options[:force_swf]
end
def with_swf?
!@options[:no_swf]
end
end 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.authors = ["John Bintz"]
s.email = ["john@coswellproductions.com"] s.email = ["john@coswellproductions.com"]
s.homepage = "" s.homepage = ""
s.license = "MIT"
s.summary = %q{Insert LiveReload into your app easily as Rack middleware} s.summary = %q{Insert LiveReload into your app easily as Rack middleware}
s.description = %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-cucumber"
s.add_development_dependency "guard-livereload" s.add_development_dependency "guard-livereload"
s.add_development_dependency "webmock" 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_development_dependency 'appraisal', '~> 0.4'
s.add_runtime_dependency "rack" s.add_runtime_dependency "rack"
end end

View File

@ -5,11 +5,8 @@
WEB_SOCKET_FORCE_FLASH = true; WEB_SOCKET_FORCE_FLASH = true;
<% end %> <% end %>
</script> </script>
<script type="text/javascript" src="<%= app_root %>/__rack/swfobject.js"></script> <script type="text/javascript" src="/__rack/swfobject.js"></script>
<script type="text/javascript" src="<%= app_root %>/__rack/web_socket.js"></script> <script type="text/javascript" src="/__rack/web_socket.js"></script>
<% end %> <% end %>
<script type="text/javascript"> <script type="text/javascript" src="<%= src %>"></script>
RACK_LIVERELOAD_PORT = <%= @options[:live_reload_port] %>;
</script>
<script type="text/javascript" src="<%= livereload_source %>"></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 } subject { middleware }
it 'should be an app' do its(:app) { should == app }
middleware.app.should be == app
end
let(:env) { {} } let(:env) { {} }
let(:options) { {} } 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 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 before do
middleware.expects(:deliver_file).returns(true) middleware.expects(:deliver_file).returns(true)
end end
it 'should return the js file' do it 'should return the js file' do
middleware._call(env).should be_truthy middleware._call(env).should be_true
end end
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 end

View File

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