sinatra 1.3.2 → 1.3.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sinatra might be problematic. Click here for more details.
- data/CHANGES +27 -0
- data/Gemfile +11 -6
- data/README.de.rdoc +332 -196
- data/README.es.rdoc +41 -6
- data/README.fr.rdoc +58 -5
- data/README.jp.rdoc +1 -1
- data/README.ko.rdoc +1926 -0
- data/README.pt-br.rdoc +1 -1
- data/README.pt-pt.rdoc +1 -1
- data/README.rdoc +94 -71
- data/README.ru.rdoc +1 -1
- data/README.zh.rdoc +1 -1
- data/Rakefile +2 -1
- data/lib/sinatra.rb +0 -3
- data/lib/sinatra/base.rb +91 -33
- data/lib/sinatra/version.rb +1 -1
- data/sinatra.gemspec +1 -1
- data/test/delegator_test.rb +1 -1
- data/test/filter_test.rb +3 -3
- data/test/helpers_test.rb +5 -5
- data/test/integration/app.rb +57 -1
- data/test/integration_helper.rb +214 -0
- data/test/integration_test.rb +68 -48
- data/test/routing_test.rb +6 -0
- data/test/sass_test.rb +1 -0
- data/test/scss_test.rb +1 -0
- data/test/static_test.rb +1 -0
- data/test/streaming_test.rb +17 -0
- metadata +93 -56
data/README.ru.rdoc
CHANGED
@@ -315,7 +315,7 @@ Thin - это более производительный и функциона
|
|
315
315
|
|
316
316
|
=== Haml шаблоны
|
317
317
|
|
318
|
-
Зависимости:: {haml}[http://haml
|
318
|
+
Зависимости:: {haml}[http://haml.info/]
|
319
319
|
Расширения файлов:: <tt>.haml</tt>
|
320
320
|
Пример:: <tt>haml :index, :format => :html5</tt>
|
321
321
|
|
data/README.zh.rdoc
CHANGED
@@ -229,7 +229,7 @@ Rack body对象或者HTTP状态码:
|
|
229
229
|
|
230
230
|
渲染 <tt>./views/index.haml</tt>。
|
231
231
|
|
232
|
-
{Haml的选项}[http://haml
|
232
|
+
{Haml的选项}[http://haml.info/docs/yardoc/file.HAML_REFERENCE.html#options]
|
233
233
|
可以通过Sinatra的配置全局设定,
|
234
234
|
参见 {选项和配置}[http://www.sinatrarb.com/configuration.html],
|
235
235
|
也可以个别的被覆盖。
|
data/Rakefile
CHANGED
@@ -56,7 +56,7 @@ end
|
|
56
56
|
# Rcov ================================================================
|
57
57
|
|
58
58
|
namespace :test do
|
59
|
-
desc '
|
59
|
+
desc 'Measures test coverage'
|
60
60
|
task :coverage do
|
61
61
|
rm_f "coverage"
|
62
62
|
sh "rcov -Ilib test/*_test.rb"
|
@@ -117,6 +117,7 @@ task :authors, [:commit_range, :format, :sep] do |t, a|
|
|
117
117
|
"a_user@mac.com" => blake, "ichverstehe" => "Harry Vangberg",
|
118
118
|
"Wu Jiang (nouse)" => "Wu Jiang" }
|
119
119
|
`git shortlog -s #{a.commit_range}`.lines.map do |line|
|
120
|
+
line = line.force_encoding 'binary' if line.respond_to? :force_encoding
|
120
121
|
num, name = line.split("\t", 2).map(&:strip)
|
121
122
|
authors[mapping[name] || name] += num.to_i
|
122
123
|
overall += num.to_i
|
data/lib/sinatra.rb
CHANGED
data/lib/sinatra/base.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# external dependencies
|
2
2
|
require 'rack'
|
3
3
|
require 'tilt'
|
4
|
-
require
|
4
|
+
require 'rack/protection'
|
5
5
|
|
6
6
|
# stdlib dependencies
|
7
7
|
require 'thread'
|
@@ -77,7 +77,9 @@ module Sinatra
|
|
77
77
|
headers.delete "Content-Length"
|
78
78
|
headers.delete "Content-Type"
|
79
79
|
elsif Array === body and not [204, 304].include?(status.to_i)
|
80
|
-
|
80
|
+
# if some other code has already set Content-Length, don't muck with it
|
81
|
+
# currently, this would be the static file-handler
|
82
|
+
headers["Content-Length"] ||= body.inject(0) { |l, p| l + Rack::Utils.bytesize(p) }.to_s
|
81
83
|
end
|
82
84
|
|
83
85
|
# Rack::Response#finish sometimes returns self as response body. We don't want that.
|
@@ -87,6 +89,55 @@ module Sinatra
|
|
87
89
|
end
|
88
90
|
end
|
89
91
|
|
92
|
+
# Some Rack handlers (Thin, Rainbows!) implement an extended body object protocol, however,
|
93
|
+
# some middleware (namely Rack::Lint) will break it by not mirroring the methods in question.
|
94
|
+
# This middleware will detect an extended body object and will make sure it reaches the
|
95
|
+
# handler directly. We do this here, so our middleware and middleware set up by the app will
|
96
|
+
# still be able to run.
|
97
|
+
class ExtendedRack < Struct.new(:app)
|
98
|
+
def call(env)
|
99
|
+
result, callback = app.call(env), env['async.callback']
|
100
|
+
return result unless callback and async?(*result)
|
101
|
+
after_response { callback.call result }
|
102
|
+
setup_close(env, *result)
|
103
|
+
throw :async
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def setup_close(env, status, header, body)
|
109
|
+
return unless body.respond_to? :close and env.include? 'async.close'
|
110
|
+
env['async.close'].callback { body.close }
|
111
|
+
env['async.close'].errback { body.close }
|
112
|
+
end
|
113
|
+
|
114
|
+
def after_response(&block)
|
115
|
+
raise NotImplementedError, "only supports EventMachine at the moment" unless defined? EventMachine
|
116
|
+
EventMachine.next_tick(&block)
|
117
|
+
end
|
118
|
+
|
119
|
+
def async?(status, headers, body)
|
120
|
+
return true if status == -1
|
121
|
+
body.respond_to? :callback and body.respond_to? :errback
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Behaves exactly like Rack::CommonLogger with the notable exception that it does nothing,
|
126
|
+
# if another CommonLogger is already in the middleware chain.
|
127
|
+
class CommonLogger < Rack::CommonLogger
|
128
|
+
def call(env)
|
129
|
+
env['sinatra.commonlogger'] ? @app.call(env) : super
|
130
|
+
end
|
131
|
+
|
132
|
+
superclass.class_eval do
|
133
|
+
alias call_without_check call unless method_defined? :call_without_check
|
134
|
+
def call(env)
|
135
|
+
env['sinatra.commonlogger'] = true
|
136
|
+
call_without_check(env)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
90
141
|
class NotFound < NameError #:nodoc:
|
91
142
|
def code ; 404 ; end
|
92
143
|
end
|
@@ -275,6 +326,7 @@ module Sinatra
|
|
275
326
|
end
|
276
327
|
|
277
328
|
def callback(&block)
|
329
|
+
return yield if @closed
|
278
330
|
@callbacks << block
|
279
331
|
end
|
280
332
|
|
@@ -290,16 +342,7 @@ module Sinatra
|
|
290
342
|
def stream(keep_open = false)
|
291
343
|
scheduler = env['async.callback'] ? EventMachine : Stream
|
292
344
|
current = @params.dup
|
293
|
-
|
294
|
-
begin
|
295
|
-
original, @params = @params, current
|
296
|
-
yield(out)
|
297
|
-
ensure
|
298
|
-
@params = original if original
|
299
|
-
end
|
300
|
-
end
|
301
|
-
|
302
|
-
body Stream.new(scheduler, keep_open, &block)
|
345
|
+
body Stream.new(scheduler, keep_open) { |out| with_params(current) { yield(out) } }
|
303
346
|
end
|
304
347
|
|
305
348
|
# Specify response freshness policy for HTTP caches (Cache-Control header).
|
@@ -486,6 +529,13 @@ module Sinatra
|
|
486
529
|
return !new_resource if list == '*'
|
487
530
|
list.to_s.split(/\s*,\s*/).include? response['ETag']
|
488
531
|
end
|
532
|
+
|
533
|
+
def with_params(temp_params)
|
534
|
+
original, @params = @params, temp_params
|
535
|
+
yield
|
536
|
+
ensure
|
537
|
+
@params = original if original
|
538
|
+
end
|
489
539
|
end
|
490
540
|
|
491
541
|
private
|
@@ -814,7 +864,7 @@ module Sinatra
|
|
814
864
|
|
815
865
|
if values.any?
|
816
866
|
original, @params = params, params.merge('splat' => [], 'captures' => values)
|
817
|
-
keys.zip(values) { |k,v|
|
867
|
+
keys.zip(values) { |k,v| Array === @params[k] ? @params[k] << v : @params[k] = v if v }
|
818
868
|
end
|
819
869
|
|
820
870
|
catch(:pass) do
|
@@ -990,9 +1040,7 @@ module Sinatra
|
|
990
1040
|
when Proc
|
991
1041
|
getter = value
|
992
1042
|
when Symbol, Fixnum, FalseClass, TrueClass, NilClass
|
993
|
-
|
994
|
-
class_eval "def self.#{option}() #{value.inspect} end"
|
995
|
-
getter = nil
|
1043
|
+
getter = value.inspect
|
996
1044
|
when Hash
|
997
1045
|
setter = proc do |val|
|
998
1046
|
val = value.merge val if Hash === val
|
@@ -1000,13 +1048,9 @@ module Sinatra
|
|
1000
1048
|
end
|
1001
1049
|
end
|
1002
1050
|
|
1003
|
-
(
|
1004
|
-
|
1005
|
-
|
1006
|
-
unless method_defined? "#{option}?"
|
1007
|
-
class_eval "def #{option}?() !!#{option} end"
|
1008
|
-
end
|
1009
|
-
end
|
1051
|
+
define_singleton_method("#{option}=", setter) if setter
|
1052
|
+
define_singleton_method(option, getter) if getter
|
1053
|
+
define_singleton_method("#{option}?", "!!#{option}") unless method_defined? "#{option}?"
|
1010
1054
|
self
|
1011
1055
|
end
|
1012
1056
|
|
@@ -1126,7 +1170,16 @@ module Sinatra
|
|
1126
1170
|
set(:public_folder, value)
|
1127
1171
|
end
|
1128
1172
|
|
1129
|
-
|
1173
|
+
private
|
1174
|
+
# Dynamically defines a method on settings.
|
1175
|
+
def define_singleton_method(name, content = Proc.new)
|
1176
|
+
# replace with call to singleton_class once we're 1.9 only
|
1177
|
+
(class << self; self; end).class_eval do
|
1178
|
+
undef_method(name) if method_defined? name
|
1179
|
+
String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content)
|
1180
|
+
end
|
1181
|
+
end
|
1182
|
+
|
1130
1183
|
# Condition for matching host name. Parameter might be String or Regexp.
|
1131
1184
|
def host_name(pattern)
|
1132
1185
|
condition { pattern === request.host }
|
@@ -1237,9 +1290,11 @@ module Sinatra
|
|
1237
1290
|
end
|
1238
1291
|
end
|
1239
1292
|
|
1293
|
+
URI = ::URI.const_defined?(:Parser) ? ::URI::Parser.new : ::URI
|
1294
|
+
|
1240
1295
|
def encoded(char)
|
1241
|
-
enc = URI.
|
1242
|
-
enc = "(?:#{Regexp.escape enc}|#{URI.
|
1296
|
+
enc = URI.escape(char)
|
1297
|
+
enc = "(?:#{Regexp.escape enc}|#{URI.escape char, /./})" if enc == char
|
1243
1298
|
enc = "(?:#{enc}|#{encoded('+')})" if char == " "
|
1244
1299
|
enc
|
1245
1300
|
end
|
@@ -1302,7 +1357,7 @@ module Sinatra
|
|
1302
1357
|
set :running, true
|
1303
1358
|
yield server if block_given?
|
1304
1359
|
end
|
1305
|
-
rescue Errno::EADDRINUSE
|
1360
|
+
rescue Errno::EADDRINUSE
|
1306
1361
|
$stderr.puts "== Someone is already performing on port #{port}!"
|
1307
1362
|
end
|
1308
1363
|
|
@@ -1336,6 +1391,7 @@ module Sinatra
|
|
1336
1391
|
|
1337
1392
|
private
|
1338
1393
|
def setup_default_middleware(builder)
|
1394
|
+
builder.use ExtendedRack
|
1339
1395
|
builder.use ShowExceptions if show_exceptions?
|
1340
1396
|
builder.use Rack::MethodOverride if method_override?
|
1341
1397
|
builder.use Rack::Head
|
@@ -1362,8 +1418,7 @@ module Sinatra
|
|
1362
1418
|
end
|
1363
1419
|
|
1364
1420
|
def setup_common_logger(builder)
|
1365
|
-
|
1366
|
-
builder.use Rack::CommonLogger
|
1421
|
+
builder.use Sinatra::CommonLogger
|
1367
1422
|
end
|
1368
1423
|
|
1369
1424
|
def setup_custom_logger(builder)
|
@@ -1379,6 +1434,7 @@ module Sinatra
|
|
1379
1434
|
options = Hash === protection ? protection.dup : {}
|
1380
1435
|
options[:except] = Array options[:except]
|
1381
1436
|
options[:except] += [:session_hijacking, :remote_token] unless sessions?
|
1437
|
+
options[:reaction] ||= :drop_session
|
1382
1438
|
builder.use Rack::Protection, options
|
1383
1439
|
end
|
1384
1440
|
|
@@ -1395,8 +1451,7 @@ module Sinatra
|
|
1395
1451
|
servers.each do |server_name|
|
1396
1452
|
begin
|
1397
1453
|
return Rack::Handler.get(server_name.to_s)
|
1398
|
-
rescue LoadError
|
1399
|
-
rescue NameError
|
1454
|
+
rescue LoadError, NameError
|
1400
1455
|
end
|
1401
1456
|
end
|
1402
1457
|
fail "Server handler (#{servers.join(',')}) not found."
|
@@ -1429,8 +1484,11 @@ module Sinatra
|
|
1429
1484
|
/src\/kernel\/bootstrap\/[A-Z]/ # maglev kernel files
|
1430
1485
|
]
|
1431
1486
|
|
1432
|
-
#
|
1433
|
-
|
1487
|
+
# contrary to what the comment said previously, rubinius never supported this
|
1488
|
+
if defined?(RUBY_IGNORE_CALLERS)
|
1489
|
+
warn "RUBY_IGNORE_CALLERS is deprecated and will no longer be supported by Sinatra 2.0"
|
1490
|
+
CALLERS_TO_IGNORE.concat(RUBY_IGNORE_CALLERS)
|
1491
|
+
end
|
1434
1492
|
|
1435
1493
|
# Like Kernel#caller but excluding certain magic entries and without
|
1436
1494
|
# line / method information; the resulting array contains filenames only.
|
data/lib/sinatra/version.rb
CHANGED
data/sinatra.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new 'sinatra', Sinatra::VERSION do |s|
|
|
10
10
|
s.files = `git ls-files`.split("\n") - %w[.gitignore .travis.yml]
|
11
11
|
s.test_files = s.files.select { |p| p =~ /^test\/.*_test.rb/ }
|
12
12
|
s.extra_rdoc_files = s.files.select { |p| p =~ /^README/ } << 'LICENSE'
|
13
|
-
s.rdoc_options = %w[--line-numbers --inline-source --title Sinatra --main README.rdoc]
|
13
|
+
s.rdoc_options = %w[--line-numbers --inline-source --title Sinatra --main README.rdoc --encoding=UTF-8]
|
14
14
|
|
15
15
|
s.add_dependency 'rack', '~> 1.3', '>= 1.3.6'
|
16
16
|
s.add_dependency 'rack-protection', '~> 1.2'
|
data/test/delegator_test.rb
CHANGED
@@ -102,7 +102,7 @@ class DelegatorTest < Test::Unit::TestCase
|
|
102
102
|
assert_equal ["helpers", mixin.to_s], app.last_call
|
103
103
|
end
|
104
104
|
|
105
|
-
it "registers
|
105
|
+
it "registers middleware with the delegation target" do
|
106
106
|
app, mixin = mirror, Module.new
|
107
107
|
Sinatra.use mixin
|
108
108
|
assert_equal ["use", mixin.to_s], app.last_call
|
data/test/filter_test.rb
CHANGED
@@ -158,11 +158,11 @@ class BeforeFilterTest < Test::Unit::TestCase
|
|
158
158
|
end
|
159
159
|
|
160
160
|
class AfterFilterTest < Test::Unit::TestCase
|
161
|
-
it "executes filters in
|
161
|
+
it "executes before and after filters in correct order" do
|
162
162
|
invoked = 0
|
163
163
|
mock_app do
|
164
164
|
before { invoked = 2 }
|
165
|
-
get('/') { invoked += 2 }
|
165
|
+
get('/') { invoked += 2; 'hello' }
|
166
166
|
after { invoked *= 2 }
|
167
167
|
end
|
168
168
|
|
@@ -412,7 +412,7 @@ class AfterFilterTest < Test::Unit::TestCase
|
|
412
412
|
assert ran
|
413
413
|
end
|
414
414
|
|
415
|
-
it 'is possible to apply user_agent conditions to
|
415
|
+
it 'is possible to apply user_agent conditions to after filters with a path' do
|
416
416
|
ran = false
|
417
417
|
mock_app do
|
418
418
|
after('/foo', :user_agent => /foo/) { ran = true }
|
data/test/helpers_test.rb
CHANGED
@@ -31,12 +31,12 @@ class HelpersTest < Test::Unit::TestCase
|
|
31
31
|
assert_body 'true'
|
32
32
|
end
|
33
33
|
|
34
|
-
it 'is false for status
|
34
|
+
it 'is false for status gt 404' do
|
35
35
|
status_app(405) { not_found? }
|
36
36
|
assert_body 'false'
|
37
37
|
end
|
38
38
|
|
39
|
-
it 'is false for status
|
39
|
+
it 'is false for status lt 404' do
|
40
40
|
status_app(403) { not_found? }
|
41
41
|
assert_body 'false'
|
42
42
|
end
|
@@ -616,7 +616,7 @@ class HelpersTest < Test::Unit::TestCase
|
|
616
616
|
assert_equal '<sinatra></sinatra>', body
|
617
617
|
end
|
618
618
|
|
619
|
-
it 'sets the Content-Type response header
|
619
|
+
it 'sets the Content-Type response header with extname' do
|
620
620
|
mock_app do
|
621
621
|
get '/attachment' do
|
622
622
|
content_type :atom
|
@@ -828,12 +828,12 @@ class HelpersTest < Test::Unit::TestCase
|
|
828
828
|
assert_not_nil response['Expires']
|
829
829
|
end
|
830
830
|
|
831
|
-
it 'allows passing
|
831
|
+
it 'allows passing Time.now objects' do
|
832
832
|
get '/bar'
|
833
833
|
assert_not_nil response['Expires']
|
834
834
|
end
|
835
835
|
|
836
|
-
it 'allows passing
|
836
|
+
it 'allows passing Time.at objects' do
|
837
837
|
get '/baz'
|
838
838
|
assert_equal 'Thu, 01 Jan 1970 00:00:00 GMT', response['Expires']
|
839
839
|
end
|
data/test/integration/app.rb
CHANGED
@@ -1,6 +1,62 @@
|
|
1
|
+
$stderr.puts "loading"
|
1
2
|
require 'sinatra'
|
2
3
|
|
4
|
+
configure do
|
5
|
+
set :foo, :bar
|
6
|
+
end
|
7
|
+
|
3
8
|
get '/app_file' do
|
4
9
|
content_type :txt
|
5
10
|
settings.app_file
|
6
|
-
end
|
11
|
+
end
|
12
|
+
|
13
|
+
get '/ping' do
|
14
|
+
'pong'
|
15
|
+
end
|
16
|
+
|
17
|
+
get '/stream' do
|
18
|
+
stream do |out|
|
19
|
+
sleep 0.1
|
20
|
+
out << "a"
|
21
|
+
sleep 1.2
|
22
|
+
out << "b"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
get '/mainonly' do
|
27
|
+
object = Object.new
|
28
|
+
begin
|
29
|
+
object.send(:get, '/foo') { }
|
30
|
+
'false'
|
31
|
+
rescue NameError
|
32
|
+
'true'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
set :out, nil
|
37
|
+
get '/async' do
|
38
|
+
stream(:keep_open) { |o| (settings.out = o) << "hi!" }
|
39
|
+
end
|
40
|
+
|
41
|
+
get '/send' do
|
42
|
+
settings.out << params[:msg] if params[:msg]
|
43
|
+
settings.out.close if params[:close]
|
44
|
+
"ok"
|
45
|
+
end
|
46
|
+
|
47
|
+
class Subclass < Sinatra::Base
|
48
|
+
set :out, nil
|
49
|
+
get '/subclass/async' do
|
50
|
+
stream(:keep_open) { |o| (settings.out = o) << "hi!" }
|
51
|
+
end
|
52
|
+
|
53
|
+
get '/subclass/send' do
|
54
|
+
settings.out << params[:msg] if params[:msg]
|
55
|
+
settings.out.close if params[:close]
|
56
|
+
"ok"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
use Subclass
|
61
|
+
|
62
|
+
$stderr.puts "starting"
|
@@ -0,0 +1,214 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'rbconfig'
|
3
|
+
require 'open-uri'
|
4
|
+
require 'net/http'
|
5
|
+
require 'timeout'
|
6
|
+
|
7
|
+
module IntegrationHelper
|
8
|
+
class BaseServer
|
9
|
+
extend Enumerable
|
10
|
+
attr_accessor :server, :port, :pipe
|
11
|
+
alias name server
|
12
|
+
|
13
|
+
def self.all
|
14
|
+
@all ||= []
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.each(&block)
|
18
|
+
all.each(&block)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.run(server, port)
|
22
|
+
new(server, port).run
|
23
|
+
end
|
24
|
+
|
25
|
+
def app_file
|
26
|
+
File.expand_path('../integration/app.rb', __FILE__)
|
27
|
+
end
|
28
|
+
|
29
|
+
def environment
|
30
|
+
"development"
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(server, port)
|
34
|
+
@installed, @pipe, @server, @port = nil, nil, server, port
|
35
|
+
Server.all << self
|
36
|
+
end
|
37
|
+
|
38
|
+
def run
|
39
|
+
return unless installed?
|
40
|
+
kill
|
41
|
+
@log = ""
|
42
|
+
@pipe = IO.popen(command)
|
43
|
+
@started = Time.now
|
44
|
+
warn "#{server} up and running on port #{port}" if ping
|
45
|
+
at_exit { kill }
|
46
|
+
end
|
47
|
+
|
48
|
+
def ping(timeout = 30)
|
49
|
+
loop do
|
50
|
+
return if alive?
|
51
|
+
if Time.now - @started > timeout
|
52
|
+
$stderr.puts command, log
|
53
|
+
fail "timeout"
|
54
|
+
else
|
55
|
+
sleep 0.1
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def alive?
|
61
|
+
3.times { get('/ping') }
|
62
|
+
true
|
63
|
+
rescue Errno::ECONNREFUSED, Errno::ECONNRESET, EOFError, SystemCallError, OpenURI::HTTPError, Timeout::Error => error
|
64
|
+
false
|
65
|
+
end
|
66
|
+
|
67
|
+
def get_stream(url = "/stream", &block)
|
68
|
+
Net::HTTP.start '127.0.0.1', port do |http|
|
69
|
+
request = Net::HTTP::Get.new url
|
70
|
+
http.request request do |response|
|
71
|
+
response.read_body(&block)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def get(url)
|
77
|
+
Timeout.timeout(1) { open("http://127.0.0.1:#{port}#{url}").read }
|
78
|
+
end
|
79
|
+
|
80
|
+
def log
|
81
|
+
@log ||= ""
|
82
|
+
loop { @log << @pipe.read_nonblock(1) }
|
83
|
+
rescue Exception
|
84
|
+
@log
|
85
|
+
end
|
86
|
+
|
87
|
+
def installed?
|
88
|
+
return @installed unless @installed.nil?
|
89
|
+
require server
|
90
|
+
@installed = true
|
91
|
+
rescue LoadError
|
92
|
+
warn "#{server} is not installed, skipping integration tests"
|
93
|
+
@installed = false
|
94
|
+
end
|
95
|
+
|
96
|
+
def command
|
97
|
+
@command ||= begin
|
98
|
+
cmd = ["RACK_ENV=#{environment}", "exec"]
|
99
|
+
if RbConfig.respond_to? :ruby
|
100
|
+
cmd << RbConfig.ruby.inspect
|
101
|
+
else
|
102
|
+
file, dir = RbConfig::CONFIG.values_at('ruby_install_name', 'bindir')
|
103
|
+
cmd << File.expand_path(file, dir).inspect
|
104
|
+
end
|
105
|
+
cmd << "-w" unless thin?
|
106
|
+
cmd << "-I" << File.expand_path('../../lib', __FILE__).inspect
|
107
|
+
cmd << app_file.inspect << '-s' << server << '-o' << '127.0.0.1' << '-p' << port
|
108
|
+
cmd << "-e" << environment.to_s << '2>&1'
|
109
|
+
cmd.join " "
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def kill
|
114
|
+
return unless pipe
|
115
|
+
Process.kill("KILL", pipe.pid)
|
116
|
+
rescue NotImplementedError
|
117
|
+
system "kill -9 #{pipe.pid}"
|
118
|
+
rescue Errno::ESRCH
|
119
|
+
end
|
120
|
+
|
121
|
+
def webrick?
|
122
|
+
name.to_s == "webrick"
|
123
|
+
end
|
124
|
+
|
125
|
+
def thin?
|
126
|
+
name.to_s == "thin"
|
127
|
+
end
|
128
|
+
|
129
|
+
def warnings
|
130
|
+
log.scan(%r[(?:\(eval|lib/sinatra).*warning:.*$])
|
131
|
+
end
|
132
|
+
|
133
|
+
def run_test(target, &block)
|
134
|
+
retries ||= 3
|
135
|
+
target.server = self
|
136
|
+
run unless alive?
|
137
|
+
target.instance_eval(&block)
|
138
|
+
rescue Exception => error
|
139
|
+
retries -= 1
|
140
|
+
kill
|
141
|
+
retries < 0 ? retry : raise(error)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
if RUBY_ENGINE == "jruby"
|
146
|
+
class JRubyServer < BaseServer
|
147
|
+
def start_vm
|
148
|
+
require 'java'
|
149
|
+
# Create a new container, set load paths and env
|
150
|
+
# SINGLETHREAD means create a new runtime
|
151
|
+
vm = org.jruby.embed.ScriptingContainer.new(org.jruby.embed.LocalContextScope::SINGLETHREAD)
|
152
|
+
vm.load_paths = [File.expand_path('../../lib', __FILE__)]
|
153
|
+
vm.environment = ENV.merge('RACK_ENV' => environment.to_s)
|
154
|
+
|
155
|
+
# This ensures processing of RUBYOPT which activates Bundler
|
156
|
+
vm.provider.ruby_instance_config.process_arguments []
|
157
|
+
vm.argv = ['-s', server.to_s, '-o', '127.0.0.1', '-p', port.to_s, '-e', environment.to_s]
|
158
|
+
|
159
|
+
# Set stdout/stderr so we can retrieve log
|
160
|
+
@pipe = java.io.ByteArrayOutputStream.new
|
161
|
+
vm.output = java.io.PrintStream.new(@pipe)
|
162
|
+
vm.error = java.io.PrintStream.new(@pipe)
|
163
|
+
|
164
|
+
Thread.new do
|
165
|
+
# Hack to ensure that Kernel#caller has the same info as
|
166
|
+
# when run from command-line, for Sintra::Application.app_file.
|
167
|
+
# Also, line numbers are zero-based in JRuby's parser
|
168
|
+
vm.provider.runtime.current_context.set_file_and_line(app_file, 0)
|
169
|
+
# Run the app
|
170
|
+
vm.run_scriptlet org.jruby.embed.PathType::ABSOLUTE, app_file
|
171
|
+
# terminate launches at_exit hooks which start server
|
172
|
+
vm.terminate
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def run
|
177
|
+
return unless installed?
|
178
|
+
kill
|
179
|
+
@thread = start_vm
|
180
|
+
@started = Time.now
|
181
|
+
warn "#{server} up and running on port #{port}" if ping
|
182
|
+
at_exit { kill }
|
183
|
+
end
|
184
|
+
|
185
|
+
def log
|
186
|
+
String.from_java_bytes @pipe.to_byte_array
|
187
|
+
end
|
188
|
+
|
189
|
+
def kill
|
190
|
+
@thread.kill if @thread
|
191
|
+
@thread = nil
|
192
|
+
end
|
193
|
+
end
|
194
|
+
Server = JRubyServer
|
195
|
+
else
|
196
|
+
Server = BaseServer
|
197
|
+
end
|
198
|
+
|
199
|
+
def it(message, &block)
|
200
|
+
Server.each do |server|
|
201
|
+
next unless server.installed?
|
202
|
+
super("with #{server.name}: #{message}") { server.run_test(self, &block) }
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def self.extend_object(obj)
|
207
|
+
super
|
208
|
+
|
209
|
+
base_port = 5000 + Process.pid % 100
|
210
|
+
Sinatra::Base.server.each_with_index do |server, index|
|
211
|
+
Server.run(server, 5000+index)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|