rack-livereload 0.3.13 → 0.3.14
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/README.md +1 -0
- data/lib/rack-livereload.rb +1 -1
- data/lib/rack/livereload.rb +9 -95
- data/lib/rack/livereload/body_processor.rb +112 -0
- data/lib/rack/livereload/processing_skip_analyzer.rb +44 -0
- data/skel/livereload.html.erb +4 -4
- data/spec/rack/livereload/body_processor_spec.rb +200 -0
- data/spec/rack/livereload/processing_skip_analyzer_spec.rb +77 -0
- data/spec/rack/livereload_spec.rb +1 -283
- metadata +10 -4
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
<a href="http://travis-ci.org/johnbintz/rack-livereload"><img src="https://secure.travis-ci.org/johnbintz/rack-livereload.png" /></a>
|
2
|
+
[](https://codeclimate.com/github/johnbintz/rack-livereload)
|
2
3
|
|
3
4
|
Hey, you've got [LiveReload](http://www.livereload.com/) in my [Rack](http://rack.rubyforge.org/)!
|
4
5
|
No need for browser extensions anymore! Just plug it in your middleware stack and go!
|
data/lib/rack-livereload.rb
CHANGED
data/lib/rack/livereload.rb
CHANGED
@@ -1,50 +1,13 @@
|
|
1
1
|
require 'erb'
|
2
|
+
require 'rack/livereload/processing_skip_analyzer'
|
3
|
+
require 'rack/livereload/body_processor'
|
2
4
|
|
3
5
|
module Rack
|
4
6
|
class LiveReload
|
5
|
-
LIVERELOAD_JS_PATH = '/__rack/livereload.js'
|
6
|
-
HEAD_TAG_REGEX = /<head>|<head[^(er)][^<]*>/
|
7
|
-
|
8
|
-
BAD_USER_AGENTS = [ %r{MSIE} ]
|
9
|
-
|
10
|
-
def livereload_local_uri
|
11
|
-
"http://localhost:#{@port}/livereload.js"
|
12
|
-
end
|
13
|
-
|
14
7
|
attr_reader :app
|
15
8
|
|
16
9
|
def initialize(app, options = {})
|
17
10
|
@app, @options = app, options
|
18
|
-
@port = @options[:live_reload_port] || 35729
|
19
|
-
end
|
20
|
-
|
21
|
-
def use_vendored?
|
22
|
-
return @use_vendored if @use_vendored
|
23
|
-
|
24
|
-
if @options[:source]
|
25
|
-
@use_vendored = (@options[:source] == :vendored)
|
26
|
-
else
|
27
|
-
require 'net/http'
|
28
|
-
require 'uri'
|
29
|
-
|
30
|
-
|
31
|
-
uri = URI.parse(livereload_local_uri)
|
32
|
-
|
33
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
34
|
-
http.read_timeout = 1
|
35
|
-
|
36
|
-
begin
|
37
|
-
http.send_request('GET', uri.path)
|
38
|
-
@use_vendored = false
|
39
|
-
rescue Timeout::Error, Errno::ECONNREFUSED, EOFError
|
40
|
-
@use_vendored = true
|
41
|
-
rescue => e
|
42
|
-
$stderr.puts e.inspect
|
43
|
-
raise e
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
@use_vendored
|
48
11
|
end
|
49
12
|
|
50
13
|
def call(env)
|
@@ -59,62 +22,21 @@ module Rack
|
|
59
22
|
else
|
60
23
|
status, headers, body = result = @app.call(env)
|
61
24
|
|
62
|
-
if (
|
63
|
-
headers['Transfer-Encoding'] == 'chunked' or
|
64
|
-
headers['Content-Disposition'] =~ %r{^inline}
|
65
|
-
return result
|
66
|
-
end
|
67
|
-
|
68
|
-
body.close if body.respond_to?(:close)
|
69
|
-
|
70
|
-
new_body = [] ; body.each { |line| new_body << line.to_s }
|
71
|
-
|
72
|
-
if !ignored?(env['PATH_INFO']) and !bad_browser?(env['HTTP_USER_AGENT'])
|
73
|
-
if headers['Content-Type'] and status == 200 and headers['Content-Type'][%r{text/html}]
|
74
|
-
content_length = 0
|
75
|
-
|
76
|
-
new_body.each do |line|
|
77
|
-
if !headers['X-Rack-LiveReload'] && line['<head']
|
78
|
-
host_to_use = (@options[:host] || env['HTTP_HOST'] || 'localhost').gsub(%r{:.*}, '')
|
79
|
-
|
80
|
-
if use_vendored?
|
81
|
-
src = LIVERELOAD_JS_PATH.dup + "?host=#{host_to_use}"
|
82
|
-
else
|
83
|
-
src = livereload_local_uri.dup.gsub('localhost', host_to_use) + '?'
|
84
|
-
end
|
85
|
-
|
86
|
-
src << "&mindelay=#{@options[:min_delay]}" if @options[:min_delay]
|
87
|
-
src << "&maxdelay=#{@options[:max_delay]}" if @options[:max_delay]
|
88
|
-
src << "&port=#{@options[:port]}" if @options[:port]
|
89
|
-
|
90
|
-
template = ERB.new(::File.read(::File.expand_path('../../../skel/livereload.html.erb', __FILE__)))
|
91
|
-
|
92
|
-
if line['<head']
|
93
|
-
line.gsub!(HEAD_TAG_REGEX) { |match| %{#{match}#{template.result(binding)}} }
|
94
|
-
end
|
25
|
+
return result if ProcessingSkipAnalyzer.skip_processing?(result, env, @options)
|
95
26
|
|
96
|
-
|
97
|
-
|
27
|
+
processor = BodyProcessor.new(body, @options)
|
28
|
+
processor.process!(env)
|
98
29
|
|
99
|
-
|
100
|
-
end
|
30
|
+
headers['Content-Length'] = processor.content_length.to_s
|
101
31
|
|
102
|
-
|
103
|
-
|
32
|
+
if processor.livereload_added
|
33
|
+
headers['X-Rack-LiveReload'] = '1'
|
104
34
|
end
|
105
35
|
|
106
|
-
[ status, headers, new_body ]
|
36
|
+
[ status, headers, processor.new_body ]
|
107
37
|
end
|
108
38
|
end
|
109
39
|
|
110
|
-
def ignored?(path_info)
|
111
|
-
@options[:ignore] and @options[:ignore].any? { |filter| path_info[filter] }
|
112
|
-
end
|
113
|
-
|
114
|
-
def bad_browser?(user_agent)
|
115
|
-
BAD_USER_AGENTS.any? { |pattern| (user_agent || '')[pattern] }
|
116
|
-
end
|
117
|
-
|
118
40
|
private
|
119
41
|
def deliver_file(file)
|
120
42
|
type = case ::File.extname(file)
|
@@ -126,14 +48,6 @@ module Rack
|
|
126
48
|
|
127
49
|
[ 200, { 'Content-Type' => type, 'Content-Length' => ::File.size(file).to_s }, [ ::File.read(file) ] ]
|
128
50
|
end
|
129
|
-
|
130
|
-
def force_swf?
|
131
|
-
@options[:force_swf]
|
132
|
-
end
|
133
|
-
|
134
|
-
def with_swf?
|
135
|
-
!@options[:no_swf]
|
136
|
-
end
|
137
51
|
end
|
138
52
|
end
|
139
53
|
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'rack/livereload'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
class LiveReload
|
5
|
+
class BodyProcessor
|
6
|
+
LIVERELOAD_JS_PATH = '/__rack/livereload.js'
|
7
|
+
HEAD_TAG_REGEX = /<head>|<head[^(er)][^<]*>/
|
8
|
+
LIVERELOAD_PORT = 35729
|
9
|
+
|
10
|
+
attr_reader :content_length, :new_body, :livereload_added
|
11
|
+
|
12
|
+
def livereload_local_uri
|
13
|
+
"http://localhost:#{@options[:live_reload_port]}/livereload.js"
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(body, options)
|
17
|
+
@body, @options = body, options
|
18
|
+
@options[:live_reload_port] ||= LIVERELOAD_PORT
|
19
|
+
|
20
|
+
@processed = false
|
21
|
+
end
|
22
|
+
|
23
|
+
def force_swf?
|
24
|
+
@options[:force_swf]
|
25
|
+
end
|
26
|
+
|
27
|
+
def with_swf?
|
28
|
+
!@options[:no_swf]
|
29
|
+
end
|
30
|
+
|
31
|
+
def use_vendored?
|
32
|
+
return @use_vendored if @use_vendored
|
33
|
+
|
34
|
+
if @options[:source]
|
35
|
+
@use_vendored = (@options[:source] == :vendored)
|
36
|
+
else
|
37
|
+
require 'net/http'
|
38
|
+
require 'uri'
|
39
|
+
|
40
|
+
uri = URI.parse(livereload_local_uri)
|
41
|
+
|
42
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
43
|
+
http.read_timeout = 1
|
44
|
+
|
45
|
+
begin
|
46
|
+
http.send_request('GET', uri.path)
|
47
|
+
@use_vendored = false
|
48
|
+
rescue Timeout::Error, Errno::ECONNREFUSED, EOFError
|
49
|
+
@use_vendored = true
|
50
|
+
rescue => e
|
51
|
+
$stderr.puts e.inspect
|
52
|
+
raise e
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
@use_vendored
|
57
|
+
end
|
58
|
+
|
59
|
+
def processed?
|
60
|
+
@processed
|
61
|
+
end
|
62
|
+
|
63
|
+
def process!(env)
|
64
|
+
@env = env
|
65
|
+
@body.close if @body.respond_to?(:close)
|
66
|
+
|
67
|
+
@new_body = [] ; @body.each { |line| @new_body << line.to_s }
|
68
|
+
|
69
|
+
@content_length = 0
|
70
|
+
@livereload_added = false
|
71
|
+
|
72
|
+
@new_body.each do |line|
|
73
|
+
if !@livereload_added && line['<head']
|
74
|
+
line.gsub!(HEAD_TAG_REGEX) { |match| %{#{match}#{template.result(binding)}} }
|
75
|
+
|
76
|
+
@livereload_added = true
|
77
|
+
end
|
78
|
+
|
79
|
+
@content_length += line.bytesize
|
80
|
+
@processed = true
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def app_root
|
85
|
+
ENV['RAILS_RELATIVE_URL_ROOT'] || ''
|
86
|
+
end
|
87
|
+
|
88
|
+
def host_to_use
|
89
|
+
(@options[:host] || @env['HTTP_HOST'] || 'localhost').gsub(%r{:.*}, '')
|
90
|
+
end
|
91
|
+
|
92
|
+
def template
|
93
|
+
ERB.new(::File.read(::File.expand_path('../../../../skel/livereload.html.erb', __FILE__)))
|
94
|
+
end
|
95
|
+
|
96
|
+
def livereload_source
|
97
|
+
if use_vendored?
|
98
|
+
src = "#{app_root}#{LIVERELOAD_JS_PATH.dup}?host=#{host_to_use}"
|
99
|
+
else
|
100
|
+
src = livereload_local_uri.dup.gsub('localhost', host_to_use) + '?'
|
101
|
+
end
|
102
|
+
|
103
|
+
src << "&mindelay=#{@options[:min_delay]}" if @options[:min_delay]
|
104
|
+
src << "&maxdelay=#{@options[:max_delay]}" if @options[:max_delay]
|
105
|
+
src << "&port=#{@options[:port]}" if @options[:port]
|
106
|
+
|
107
|
+
src
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'rack/livereload'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
class LiveReload
|
5
|
+
class ProcessingSkipAnalyzer
|
6
|
+
BAD_USER_AGENTS = [ %r{MSIE} ]
|
7
|
+
|
8
|
+
def self.skip_processing?(result, env, options)
|
9
|
+
new(result, env, options).skip_processing?
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(result, env, options)
|
13
|
+
@env, @options = env, options
|
14
|
+
|
15
|
+
@status, @headers, @body = result
|
16
|
+
end
|
17
|
+
|
18
|
+
def skip_processing?
|
19
|
+
!html? || chunked? || inline? || ignored? || bad_browser?
|
20
|
+
end
|
21
|
+
|
22
|
+
def chunked?
|
23
|
+
@headers['Transfer-Encoding'] == 'chunked'
|
24
|
+
end
|
25
|
+
|
26
|
+
def inline?
|
27
|
+
@headers['Content-Disposition'] =~ %r{^inline}
|
28
|
+
end
|
29
|
+
|
30
|
+
def ignored?
|
31
|
+
@options[:ignore] and @options[:ignore].any? { |filter| @env['PATH_INFO'][filter] }
|
32
|
+
end
|
33
|
+
|
34
|
+
def bad_browser?
|
35
|
+
BAD_USER_AGENTS.any? { |pattern| @env['HTTP_USER_AGENT'] =~ pattern }
|
36
|
+
end
|
37
|
+
|
38
|
+
def html?
|
39
|
+
@headers['Content-Type'] =~ %r{text/html}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
data/skel/livereload.html.erb
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
WEB_SOCKET_FORCE_FLASH = true;
|
6
6
|
<% end %>
|
7
7
|
</script>
|
8
|
-
<script type="text/javascript" src="
|
9
|
-
<script type="text/javascript" src="
|
8
|
+
<script type="text/javascript" src="<%= app_root %>/__rack/swfobject.js"></script>
|
9
|
+
<script type="text/javascript" src="<%= app_root %>/__rack/web_socket.js"></script>
|
10
10
|
<% end %>
|
11
11
|
<script type="text/javascript">
|
12
|
-
RACK_LIVERELOAD_PORT = <%= @
|
12
|
+
RACK_LIVERELOAD_PORT = <%= @options[:live_reload_port] %>;
|
13
13
|
</script>
|
14
|
-
<script type="text/javascript" src="<%=
|
14
|
+
<script type="text/javascript" src="<%= livereload_source %>"></script>
|
15
15
|
|
@@ -0,0 +1,200 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
describe Rack::LiveReload::BodyProcessor do
|
5
|
+
describe 'head tag regex' do
|
6
|
+
let(:regex) { described_class::HEAD_TAG_REGEX }
|
7
|
+
subject { regex }
|
8
|
+
|
9
|
+
it { should be_kind_of(Regexp) }
|
10
|
+
|
11
|
+
it 'only picks a valid <head> tag' do
|
12
|
+
regex.match("<head></head>").to_s.should eq('<head>')
|
13
|
+
regex.match("<head><title></title></head>").to_s.should eq('<head>')
|
14
|
+
regex.match("<head attribute='something'><title></title></head>").to_s.should eq("<head attribute='something'>")
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'responds false when no head tag' do
|
18
|
+
regex.match("<header></header>").should be_false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
let(:processor) { described_class.new(body, options) }
|
23
|
+
let(:body) { [ page_html ] }
|
24
|
+
let(:options) { {} }
|
25
|
+
let(:page_html) { '<head></head>' }
|
26
|
+
|
27
|
+
let(:processor_result) do
|
28
|
+
if !processor.processed?
|
29
|
+
processor.process!(env)
|
30
|
+
end
|
31
|
+
|
32
|
+
processor
|
33
|
+
end
|
34
|
+
|
35
|
+
subject { processor }
|
36
|
+
|
37
|
+
describe "livereload local uri" do
|
38
|
+
context 'does not exist' do
|
39
|
+
before do
|
40
|
+
stub_request(:any, 'localhost:35729/livereload.js').to_timeout
|
41
|
+
end
|
42
|
+
|
43
|
+
it { should use_vendored }
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'exists' do
|
47
|
+
before do
|
48
|
+
stub_request(:any, 'localhost:35729/livereload.js')
|
49
|
+
end
|
50
|
+
|
51
|
+
it { should_not use_vendored }
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'with custom port' do
|
55
|
+
let(:options) { {:live_reload_port => '12348'}}
|
56
|
+
|
57
|
+
context 'exists' do
|
58
|
+
before do
|
59
|
+
stub_request(:any, 'localhost:12348/livereload.js')
|
60
|
+
end
|
61
|
+
it { should_not use_vendored }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'specify vendored' do
|
66
|
+
let(:options) { { :source => :vendored } }
|
67
|
+
|
68
|
+
it { should use_vendored }
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'specify LR' do
|
72
|
+
let(:options) { { :source => :livereload } }
|
73
|
+
|
74
|
+
it { should_not use_vendored }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'text/html' do
|
79
|
+
before do
|
80
|
+
processor.stubs(:use_vendored?).returns(true)
|
81
|
+
end
|
82
|
+
|
83
|
+
let(:host) { 'host' }
|
84
|
+
let(:env) { { 'HTTP_HOST' => host } }
|
85
|
+
|
86
|
+
let(:processed_body) { processor_result.new_body.join('') }
|
87
|
+
let(:length) { processor_result.content_length }
|
88
|
+
|
89
|
+
let(:page_html) { '<head></head>' }
|
90
|
+
|
91
|
+
context 'vendored' do
|
92
|
+
it 'should add the vendored livereload js script tag' do
|
93
|
+
processed_body.should include("script")
|
94
|
+
processed_body.should include(described_class::LIVERELOAD_JS_PATH)
|
95
|
+
|
96
|
+
length.to_s.should == processed_body.length.to_s
|
97
|
+
|
98
|
+
described_class::LIVERELOAD_JS_PATH.should_not include(host)
|
99
|
+
|
100
|
+
processed_body.should include('swfobject')
|
101
|
+
processed_body.should include('web_socket')
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'at the top of the head tag' do
|
106
|
+
let(:page_html) { '<head attribute="attribute"><script type="text/javascript" insert="first"></script><script type="text/javascript" insert="before"></script></head>' }
|
107
|
+
|
108
|
+
let(:body_dom) { Nokogiri::XML(processed_body) }
|
109
|
+
|
110
|
+
it 'should add the livereload js script tag before all other script tags' do
|
111
|
+
body_dom.at_css("head")[:attribute].should == 'attribute'
|
112
|
+
body_dom.at_css("script:eq(5)")[:src].should include(described_class::LIVERELOAD_JS_PATH)
|
113
|
+
body_dom.at_css("script:last-child")[:insert].should == "before"
|
114
|
+
end
|
115
|
+
|
116
|
+
context 'when a relative URL root is specified' do
|
117
|
+
before do
|
118
|
+
ENV['RAILS_RELATIVE_URL_ROOT'] = '/a_relative_path'
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'should prepend the relative path to the script src' do
|
122
|
+
body_dom.at_css("script:eq(5)")[:src].should match(%r{^/a_relative_path/})
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe "LIVERELOAD_PORT value" do
|
128
|
+
let(:options) { { :live_reload_port => 12345 }}
|
129
|
+
|
130
|
+
it "sets the variable at the top of the file" do
|
131
|
+
processed_body.should include 'RACK_LIVERELOAD_PORT = 12345'
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
context 'in header tags' do
|
136
|
+
let(:page_html) { "<header class='hero'><h1>Just a normal header tag</h1></header>" }
|
137
|
+
|
138
|
+
let(:body_dom) { Nokogiri::XML(processed_body) }
|
139
|
+
|
140
|
+
it 'should not add the livereload js' do
|
141
|
+
body_dom.at_css("header")[:class].should == 'hero'
|
142
|
+
body_dom.css('script').should be_empty
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
context 'not vendored' do
|
147
|
+
before do
|
148
|
+
processor.stubs(:use_vendored?).returns(false)
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'should add the LR livereload js script tag' do
|
152
|
+
processed_body.should include("script")
|
153
|
+
processed_body.should include(processor.livereload_local_uri.gsub('localhost', 'host'))
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context 'set options' do
|
158
|
+
let(:options) { { :host => new_host, :port => port, :min_delay => min_delay, :max_delay => max_delay } }
|
159
|
+
let(:min_delay) { 5 }
|
160
|
+
let(:max_delay) { 10 }
|
161
|
+
let(:port) { 23 }
|
162
|
+
let(:new_host) { 'myhost' }
|
163
|
+
|
164
|
+
it 'should add the livereload.js script tag' do
|
165
|
+
processed_body.should include("mindelay=#{min_delay}")
|
166
|
+
processed_body.should include("maxdelay=#{max_delay}")
|
167
|
+
processed_body.should include("port=#{port}")
|
168
|
+
processed_body.should include("host=#{new_host}")
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context 'force flash' do
|
173
|
+
let(:options) { { :force_swf => true } }
|
174
|
+
|
175
|
+
it 'should not add the flash shim' do
|
176
|
+
processed_body.should include('WEB_SOCKET_FORCE_FLASH')
|
177
|
+
processed_body.should include('swfobject')
|
178
|
+
processed_body.should include('web_socket')
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
context 'no flash' do
|
183
|
+
let(:options) { { :no_swf => true } }
|
184
|
+
|
185
|
+
it 'should not add the flash shim' do
|
186
|
+
processed_body.should_not include('swfobject')
|
187
|
+
processed_body.should_not include('web_socket')
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
context 'no host at all' do
|
192
|
+
let(:env) { {} }
|
193
|
+
|
194
|
+
it 'should use localhost' do
|
195
|
+
processed_body.should include('localhost')
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rack::LiveReload::ProcessingSkipAnalyzer do
|
4
|
+
subject { described_class.new(result, env, options) }
|
5
|
+
|
6
|
+
let(:result) { [ status, headers, body ] }
|
7
|
+
let(:env) { { 'HTTP_USER_AGENT' => user_agent } }
|
8
|
+
let(:options) { {} }
|
9
|
+
|
10
|
+
let(:user_agent) { 'Firefox' }
|
11
|
+
let(:status) { 200 }
|
12
|
+
let(:headers) { {} }
|
13
|
+
let(:body) { [] }
|
14
|
+
|
15
|
+
describe '#skip_processing?' do
|
16
|
+
it "should skip processing" do
|
17
|
+
subject.skip_processing?.should be_true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#bad_browser?' do
|
22
|
+
let(:user_agent) { described_class::BAD_USER_AGENTS.first.source }
|
23
|
+
|
24
|
+
it { should be_bad_browser }
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'ignored' do
|
28
|
+
let(:options) { { :ignore => [ %r{file} ] } }
|
29
|
+
|
30
|
+
context 'not root' do
|
31
|
+
let(:env) { { 'PATH_INFO' => '/this/file' } }
|
32
|
+
|
33
|
+
it { should be_ignored }
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'root' do
|
37
|
+
let(:env) { { 'PATH_INFO' => '/' } }
|
38
|
+
|
39
|
+
it { should_not be_ignored }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'not text/html' do
|
44
|
+
let(:headers) { { 'Content-Type' => 'application/pdf' } }
|
45
|
+
|
46
|
+
it { should_not be_html }
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'chunked response' do
|
50
|
+
let(:headers) { { 'Transfer-Encoding' => 'chunked' } }
|
51
|
+
|
52
|
+
it { should be_chunked }
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
context 'inline disposition' do
|
57
|
+
let(:headers) { { 'Content-Disposition' => 'inline; filename=my_inlined_file' } }
|
58
|
+
|
59
|
+
it { should be_inline }
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#ignored?' do
|
63
|
+
let(:path_info) { 'path info' }
|
64
|
+
let(:env) { { 'PATH_INFO' => path_info } }
|
65
|
+
|
66
|
+
context 'no ignore set' do
|
67
|
+
it { should_not be_ignored }
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'ignore set' do
|
71
|
+
let(:options) { { :ignore => [ %r{#{path_info}} ] } }
|
72
|
+
|
73
|
+
it { should be_ignored }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
@@ -12,253 +12,8 @@ describe Rack::LiveReload do
|
|
12
12
|
let(:env) { {} }
|
13
13
|
let(:options) { {} }
|
14
14
|
|
15
|
-
describe "livereload local uri" do
|
16
|
-
context 'does not exist' do
|
17
|
-
before do
|
18
|
-
stub_request(:any, 'localhost:35729/livereload.js').to_timeout
|
19
|
-
end
|
20
|
-
|
21
|
-
it { should use_vendored }
|
22
|
-
end
|
23
|
-
|
24
|
-
context 'exists' do
|
25
|
-
before do
|
26
|
-
stub_request(:any, 'localhost:35729/livereload.js')
|
27
|
-
end
|
28
|
-
|
29
|
-
it { should_not use_vendored }
|
30
|
-
end
|
31
|
-
|
32
|
-
context 'with custom port' do
|
33
|
-
let(:options) { {:live_reload_port => '12348'}}
|
34
|
-
context 'exists' do
|
35
|
-
before do
|
36
|
-
stub_request(:any, 'localhost:12348/livereload.js')
|
37
|
-
end
|
38
|
-
it { should_not use_vendored }
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
context 'specify vendored' do
|
43
|
-
let(:options) { { :source => :vendored } }
|
44
|
-
|
45
|
-
it { should use_vendored }
|
46
|
-
end
|
47
|
-
|
48
|
-
context 'specify LR' do
|
49
|
-
let(:options) { { :source => :livereload } }
|
50
|
-
|
51
|
-
it { should_not use_vendored }
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
context 'not text/html' do
|
56
|
-
let(:ret) { [ 200, { 'Content-Type' => 'image/png' }, [ '<head></head>' ] ] }
|
57
|
-
|
58
|
-
before do
|
59
|
-
app.stubs(:call).with(env).returns(ret)
|
60
|
-
end
|
61
|
-
|
62
|
-
it 'should pass through' do
|
63
|
-
middleware.call(env).should == ret
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
context 'text/event-stram' do
|
68
|
-
let(:body) { [ '<head></head>' ] }
|
69
|
-
let(:ret) { [ 200, { 'Content-Type' => 'text/event-stream' }, body ] }
|
70
|
-
|
71
|
-
before do
|
72
|
-
app.stubs(:call).with(env).returns(ret)
|
73
|
-
body.expects(:close).never
|
74
|
-
body.stubs(:respond_to?).with(:close).returns(true)
|
75
|
-
end
|
76
|
-
|
77
|
-
it 'should pass through' do
|
78
|
-
middleware.call(env).should == ret
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
context 'chunked response' do
|
83
|
-
let(:body) { [ '<head></head>' ] }
|
84
|
-
let(:ret) { [ 200, { 'Transfer-Encoding' => 'chunked' }, body ] }
|
85
|
-
|
86
|
-
before do
|
87
|
-
app.stubs(:call).with(env).returns(ret)
|
88
|
-
body.expects(:close).never
|
89
|
-
body.stubs(:respond_to?).with(:close).returns(true)
|
90
|
-
end
|
91
|
-
|
92
|
-
it 'should pass through' do
|
93
|
-
middleware.call(env).should == ret
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
context 'inline disposition' do
|
98
|
-
let(:body) { [ '<head></head>' ] }
|
99
|
-
let(:ret) { [ 200, { 'Content-Disposition' => 'inline; filename=my_inlined_file' }, body ] }
|
100
|
-
|
101
|
-
before do
|
102
|
-
app.stubs(:call).with(env).returns(ret)
|
103
|
-
body.expects(:close).never
|
104
|
-
body.stubs(:respond_to?).with(:close).returns(true)
|
105
|
-
end
|
106
|
-
|
107
|
-
it 'should pass through' do
|
108
|
-
middleware.call(env).should == ret
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
context 'unknown Content-Type' do
|
113
|
-
let(:ret) { [ 200, {}, [ 'hey ho' ] ] }
|
114
|
-
|
115
|
-
before do
|
116
|
-
app.stubs(:call).with(env).returns(ret)
|
117
|
-
end
|
118
|
-
|
119
|
-
it 'should not break' do
|
120
|
-
middleware.call(env).should_not raise_error(NoMethodError, /You have a nil object/)
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
context 'text/html' do
|
125
|
-
before do
|
126
|
-
app.stubs(:call).with(env).returns([ 200, { 'Content-Type' => 'text/html', 'Content-Length' => 0 }, [ page_html ] ])
|
127
|
-
middleware.stubs(:use_vendored?).returns(true)
|
128
|
-
end
|
129
|
-
|
130
|
-
let(:host) { 'host' }
|
131
|
-
let(:env) { { 'HTTP_HOST' => host } }
|
132
|
-
|
133
|
-
let(:ret) { middleware._call(env) }
|
134
|
-
let(:body) { ret.last.join }
|
135
|
-
let(:length) { ret[1]['Content-Length'] }
|
136
|
-
|
137
|
-
let(:page_html) { '<head></head>' }
|
138
|
-
|
139
|
-
context 'vendored' do
|
140
|
-
it 'should add the vendored livereload js script tag' do
|
141
|
-
body.should include("script")
|
142
|
-
body.should include(described_class::LIVERELOAD_JS_PATH)
|
143
|
-
|
144
|
-
length.should == body.length.to_s
|
145
|
-
|
146
|
-
described_class::LIVERELOAD_JS_PATH.should_not include(host)
|
147
|
-
|
148
|
-
body.should include('swfobject')
|
149
|
-
body.should include('web_socket')
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
context 'at the top of the head tag' do
|
154
|
-
let(:page_html) { '<head attribute="attribute"><script type="text/javascript" insert="first"></script><script type="text/javascript" insert="before"></script></head>' }
|
155
|
-
|
156
|
-
let(:body_dom) { Nokogiri::XML(body) }
|
157
|
-
|
158
|
-
it 'should add the livereload js script tag before all other script tags' do
|
159
|
-
body_dom.at_css("head")[:attribute].should == 'attribute'
|
160
|
-
body_dom.at_css("script:eq(5)")[:src].should include(described_class::LIVERELOAD_JS_PATH)
|
161
|
-
body_dom.at_css("script:last-child")[:insert].should == "before"
|
162
|
-
end
|
163
|
-
|
164
|
-
end
|
165
|
-
|
166
|
-
describe "LIVERELOAD_PORT value" do
|
167
|
-
let(:options) { {:live_reload_port => 12345 }}
|
168
|
-
it "sets the variable at the top of the file" do
|
169
|
-
body.should include 'RACK_LIVERELOAD_PORT = 12345'
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
context 'in header tags' do
|
176
|
-
let(:page_html) { "<header class='hero'><h1>Just a normal header tag</h1></header>" }
|
177
|
-
|
178
|
-
let(:body_dom) { Nokogiri::XML(body) }
|
179
|
-
|
180
|
-
it 'should not add the livereload js' do
|
181
|
-
body_dom.at_css("header")[:class].should == 'hero'
|
182
|
-
body_dom.css('script').should be_empty
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
context 'not vendored' do
|
187
|
-
before do
|
188
|
-
middleware.stubs(:use_vendored?).returns(false)
|
189
|
-
end
|
190
|
-
|
191
|
-
it 'should add the LR livereload js script tag' do
|
192
|
-
body.should include("script")
|
193
|
-
body.should include(middleware.livereload_local_uri.gsub('localhost', 'host'))
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
context 'set options' do
|
198
|
-
let(:middleware) { described_class.new(app, :host => new_host, :port => port, :min_delay => min_delay, :max_delay => max_delay) }
|
199
|
-
let(:min_delay) { 5 }
|
200
|
-
let(:max_delay) { 10 }
|
201
|
-
let(:port) { 23 }
|
202
|
-
let(:new_host) { 'myhost' }
|
203
|
-
|
204
|
-
it 'should add the livereload.js script tag' do
|
205
|
-
body.should include("mindelay=#{min_delay}")
|
206
|
-
body.should include("maxdelay=#{max_delay}")
|
207
|
-
body.should include("port=#{port}")
|
208
|
-
body.should include("host=#{new_host}")
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
context 'force flash' do
|
213
|
-
let(:middleware) { described_class.new(app, :force_swf => true) }
|
214
|
-
|
215
|
-
it 'should not add the flash shim' do
|
216
|
-
body.should include('WEB_SOCKET_FORCE_FLASH')
|
217
|
-
body.should include('swfobject')
|
218
|
-
body.should include('web_socket')
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
context 'no flash' do
|
223
|
-
let(:middleware) { described_class.new(app, :no_swf => true) }
|
224
|
-
|
225
|
-
it 'should not add the flash shim' do
|
226
|
-
body.should_not include('swfobject')
|
227
|
-
body.should_not include('web_socket')
|
228
|
-
end
|
229
|
-
end
|
230
|
-
|
231
|
-
context 'no host at all' do
|
232
|
-
let(:env) { {} }
|
233
|
-
|
234
|
-
it 'should use localhost' do
|
235
|
-
body.should include('localhost')
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
|
-
context 'ignored' do
|
240
|
-
let(:options) { { :ignore => [ %r{file} ] } }
|
241
|
-
|
242
|
-
context 'not root' do
|
243
|
-
let(:env) { { 'PATH_INFO' => '/this/file' } }
|
244
|
-
|
245
|
-
it 'should have no change' do
|
246
|
-
body.should_not include('script')
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
context 'root' do
|
251
|
-
let(:env) { { 'PATH_INFO' => '/' } }
|
252
|
-
|
253
|
-
it 'should have script' do
|
254
|
-
body.should include('script')
|
255
|
-
end
|
256
|
-
end
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
15
|
context '/__rack/livereload.js' do
|
261
|
-
let(:env) { { 'PATH_INFO' => described_class::LIVERELOAD_JS_PATH } }
|
16
|
+
let(:env) { { 'PATH_INFO' => described_class::BodyProcessor::LIVERELOAD_JS_PATH } }
|
262
17
|
|
263
18
|
before do
|
264
19
|
middleware.expects(:deliver_file).returns(true)
|
@@ -268,42 +23,5 @@ describe Rack::LiveReload do
|
|
268
23
|
middleware._call(env).should be_true
|
269
24
|
end
|
270
25
|
end
|
271
|
-
|
272
|
-
describe '#ignored?' do
|
273
|
-
let(:path_info) { 'path info' }
|
274
|
-
|
275
|
-
context 'no ignore set' do
|
276
|
-
it { should_not be_ignored(path_info) }
|
277
|
-
end
|
278
|
-
|
279
|
-
context 'ignore set' do
|
280
|
-
let(:options) { { :ignore => [ %r{#{path_info}} ] } }
|
281
|
-
|
282
|
-
it { should be_ignored(path_info) }
|
283
|
-
end
|
284
|
-
end
|
285
|
-
|
286
|
-
describe '#bad_browser?' do
|
287
|
-
let(:user_agent) { described_class::BAD_USER_AGENTS.first.source }
|
288
|
-
|
289
|
-
it { should be_bad_browser(user_agent) }
|
290
|
-
end
|
291
|
-
|
292
|
-
describe 'head tag regex' do
|
293
|
-
let(:regex) { described_class::HEAD_TAG_REGEX }
|
294
|
-
subject { regex }
|
295
|
-
|
296
|
-
it { should be_kind_of(Regexp) }
|
297
|
-
|
298
|
-
it 'only picks a valid <head> tag' do
|
299
|
-
regex.match("<head></head>").to_s.should eq('<head>')
|
300
|
-
regex.match("<head><title></title></head>").to_s.should eq('<head>')
|
301
|
-
regex.match("<head attribute='something'><title></title></head>").to_s.should eq("<head attribute='something'>")
|
302
|
-
end
|
303
|
-
|
304
|
-
it 'responds false when no head tag' do
|
305
|
-
regex.match("<header></header>").should be_false
|
306
|
-
end
|
307
|
-
end
|
308
26
|
end
|
309
27
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-livereload
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.14
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-05-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -301,8 +301,12 @@ files:
|
|
301
301
|
- js/web_socket.js
|
302
302
|
- lib/rack-livereload.rb
|
303
303
|
- lib/rack/livereload.rb
|
304
|
+
- lib/rack/livereload/body_processor.rb
|
305
|
+
- lib/rack/livereload/processing_skip_analyzer.rb
|
304
306
|
- rack-livereload.gemspec
|
305
307
|
- skel/livereload.html.erb
|
308
|
+
- spec/rack/livereload/body_processor_spec.rb
|
309
|
+
- spec/rack/livereload/processing_skip_analyzer_spec.rb
|
306
310
|
- spec/rack/livereload_spec.rb
|
307
311
|
- spec/spec_helper.rb
|
308
312
|
homepage: ''
|
@@ -319,7 +323,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
319
323
|
version: '0'
|
320
324
|
segments:
|
321
325
|
- 0
|
322
|
-
hash:
|
326
|
+
hash: -189623715678249777
|
323
327
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
324
328
|
none: false
|
325
329
|
requirements:
|
@@ -328,7 +332,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
328
332
|
version: '0'
|
329
333
|
segments:
|
330
334
|
- 0
|
331
|
-
hash:
|
335
|
+
hash: -189623715678249777
|
332
336
|
requirements: []
|
333
337
|
rubyforge_project: rack-livereload
|
334
338
|
rubygems_version: 1.8.25
|
@@ -341,5 +345,7 @@ test_files:
|
|
341
345
|
- features/step_definitions/then/i_should_not_have_livereload_code.rb
|
342
346
|
- features/step_definitions/when/i_make_a_request_with_headers.rb
|
343
347
|
- features/support/env.rb
|
348
|
+
- spec/rack/livereload/body_processor_spec.rb
|
349
|
+
- spec/rack/livereload/processing_skip_analyzer_spec.rb
|
344
350
|
- spec/rack/livereload_spec.rb
|
345
351
|
- spec/spec_helper.rb
|