sinatra 1.1.0 → 1.1.2

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.

@@ -859,7 +859,7 @@ Sinatra::Base子类可用的方法实际上就是
859
859
  require 'sinatra/base'
860
860
 
861
861
  class LoginScreen < Sinatra::Base
862
- enable :session
862
+ enable :sessions
863
863
 
864
864
  get('/login') { haml :login }
865
865
 
@@ -901,7 +901,7 @@ Sinatra::Application,或者这个类就是你显式创建的子类。
901
901
 
902
902
  通过 `set` 创建的选项是类层面的方法:
903
903
 
904
- class MyApp << Sinatra::Base
904
+ class MyApp < Sinatra::Base
905
905
  # 嘿,我在应用变量域!
906
906
  set :foo, 42
907
907
  foo # => 42
@@ -931,7 +931,7 @@ Sinatra::Application,或者这个类就是你显式创建的子类。
931
931
  `erb` 或者 `haml`。你可以在请求变量域当中通过`settings`辅助方法
932
932
  访问应用变量域:
933
933
 
934
- class MyApp << Sinatra::Base
934
+ class MyApp < Sinatra::Base
935
935
  # 嘿,我在应用变量域!
936
936
  get '/define_route/:name' do
937
937
  # 针对 '/define_route/:name' 的请求变量域
data/Rakefile CHANGED
@@ -12,6 +12,10 @@ def source_version
12
12
  end
13
13
 
14
14
  # SPECS ===============================================================
15
+ task :test do
16
+ ENV['LANG'] = 'C'
17
+ ENV.delete 'LC_CTYPE'
18
+ end
15
19
 
16
20
  if !ENV['NO_TEST_FIX'] and RUBY_VERSION == '1.9.2' and RUBY_PATCHLEVEL == 0
17
21
  # Avoids seg fault
@@ -44,26 +48,28 @@ end
44
48
 
45
49
  desc 'Generate RDoc under doc/api'
46
50
  task 'doc' => ['doc:api']
51
+ task('doc:api') { sh "yardoc -o doc/api" }
52
+ CLEAN.include 'doc/api'
47
53
 
48
- task 'doc:api' => ['doc/api/index.html']
49
-
50
- file 'doc/api/index.html' => FileList['lib/**/*.rb', 'README.*'] do |f|
51
- require 'rbconfig'
52
- hanna = RbConfig::CONFIG['ruby_install_name'].sub('ruby', 'hanna')
53
- rb_files = f.prerequisites
54
- sh(<<-end.gsub(/\s+/, ' '))
55
- #{hanna}
56
- --charset utf8
57
- --fmt html
58
- --inline-source
59
- --line-numbers
60
- --main README.rdoc
61
- --op doc/api
62
- --title 'Sinatra API Documentation'
63
- #{rb_files.join(' ')}
54
+ # README ===============================================================
55
+ task :add_template, [:name] do |t, args|
56
+ Dir.glob('README.*') do |file|
57
+ code = File.read(file)
58
+ if code =~ /^===.*#{args.name.capitalize}/
59
+ puts "Already covered in #{file}"
60
+ else
61
+ template = code[/===[^\n]*Liquid.*index\.liquid<\/tt>[^\n]*/m]
62
+ if !template
63
+ puts "Liquid not found in #{file}"
64
+ else
65
+ puts "Adding section to #{file}"
66
+ template = template.gsub(/Liquid/, args.name.capitalize).gsub(/liquid/, args.name.downcase)
67
+ code.gsub! /^(\s*===.*CoffeeScript)/, template << "\n\\1"
68
+ File.open(file, "w") { |f| f << code }
69
+ end
70
+ end
64
71
  end
65
72
  end
66
- CLEAN.include 'doc/api'
67
73
 
68
74
  # PACKAGING ============================================================
69
75
 
@@ -7,7 +7,7 @@ require 'sinatra/showexceptions'
7
7
  require 'tilt'
8
8
 
9
9
  module Sinatra
10
- VERSION = '1.1.0'
10
+ VERSION = '1.1.2'
11
11
 
12
12
  # The request object. See Rack::Request for more info:
13
13
  # http://rack.rubyforge.org/doc/classes/Rack/Request.html
@@ -28,6 +28,18 @@ module Sinatra
28
28
  else
29
29
  alias secure? ssl?
30
30
  end
31
+
32
+ def route
33
+ @route ||= begin
34
+ path = Rack::Utils.unescape(path_info)
35
+ path.empty? ? "/" : path
36
+ end
37
+ end
38
+
39
+ def path_info=(value)
40
+ @route = nil
41
+ super
42
+ end
31
43
  end
32
44
 
33
45
  # The response object. See Rack::Response and Rack::ResponseHelpers for
@@ -128,10 +140,15 @@ module Sinatra
128
140
  # Set the Content-Type of the response body given a media type or file
129
141
  # extension.
130
142
  def content_type(type, params={})
131
- mime_type = mime_type(type)
143
+ default = params.delete :default
144
+ mime_type = mime_type(type) || default
132
145
  fail "Unknown media type: %p" % type if mime_type.nil?
133
- params[:charset] ||= params.delete('charset') || settings.default_encoding
134
- response['Content-Type'] = "#{mime_type};#{params.map { |kv| kv.join('=') }.join(', ')}"
146
+ mime_type = mime_type.dup
147
+ unless params.include? :charset or settings.add_charset.all? { |p| not p === mime_type }
148
+ params[:charset] = params.delete('charset') || settings.default_encoding
149
+ end
150
+ mime_type << ";#{params.map { |kv| kv.join('=') }.join(', ')}" unless params.empty?
151
+ response['Content-Type'] = mime_type
135
152
  end
136
153
 
137
154
  # Set the Content-Disposition to "attachment" with the specified filename,
@@ -149,10 +166,9 @@ module Sinatra
149
166
  stat = File.stat(path)
150
167
  last_modified stat.mtime
151
168
 
152
- content_type opts[:type] ||
153
- File.extname(path) ||
154
- response['Content-Type'] ||
155
- 'application/octet-stream'
169
+ if opts[:type] or not response['Content-Type']
170
+ content_type opts[:type] || File.extname(path), :default => 'application/octet-stream'
171
+ end
156
172
 
157
173
  if opts[:disposition] == 'attachment' || opts[:filename]
158
174
  attachment opts[:filename] || path
@@ -264,7 +280,11 @@ module Sinatra
264
280
  end
265
281
 
266
282
  values = values.map { |value| value.to_s.tr('_','-') }
267
- hash.each { |k,v| values << [k.to_s.tr('_', '-'), v].join('=') }
283
+ hash.each do |key, value|
284
+ key = key.to_s.tr('_', '-')
285
+ value = value.to_i if key == "max-age"
286
+ values << [key, value].join('=')
287
+ end
268
288
 
269
289
  response['Cache-Control'] = values.join(', ') if values.any?
270
290
  end
@@ -435,30 +455,29 @@ module Sinatra
435
455
  def render(engine, data, options={}, locals={}, &block)
436
456
  # merge app-level options
437
457
  options = settings.send(engine).merge(options) if settings.respond_to?(engine)
438
- options[:outvar] ||= '@_out_buf'
458
+ options[:outvar] ||= '@_out_buf'
459
+ options[:default_encoding] ||= settings.default_encoding
439
460
 
440
461
  # extract generic options
441
462
  locals = options.delete(:locals) || locals || {}
442
463
  views = options.delete(:views) || settings.views || "./views"
443
464
  @default_layout = :layout if @default_layout.nil?
444
465
  layout = options.delete(:layout)
466
+ eat_errors = layout.nil?
445
467
  layout = @default_layout if layout.nil? or layout == true
446
468
  content_type = options.delete(:content_type) || options.delete(:default_content_type)
447
469
 
448
470
  # compile and render template
449
471
  layout_was = @default_layout
450
- @default_layout = false if layout
472
+ @default_layout = false
451
473
  template = compile_template(engine, data, options, views)
452
474
  output = template.render(self, locals, &block)
453
475
  @default_layout = layout_was
454
476
 
455
477
  # render layout
456
478
  if layout
457
- begin
458
- options = options.merge(:views => views, :layout => false)
459
- output = render(engine, layout, options, locals) { output }
460
- rescue Errno::ENOENT
461
- end
479
+ options = options.merge(:views => views, :layout => false, :eat_errors => eat_errors)
480
+ catch(:layout_missing) { output = render(engine, layout, options, locals) { output }}
462
481
  end
463
482
 
464
483
  output.extend(ContentTyped).content_type = content_type if content_type
@@ -466,6 +485,7 @@ module Sinatra
466
485
  end
467
486
 
468
487
  def compile_template(engine, data, options, views)
488
+ eat_errors = options.delete :eat_errors
469
489
  template_cache.fetch engine, data, options do
470
490
  template = Tilt[engine]
471
491
  raise "Template engine not found: #{engine}" if template.nil?
@@ -477,12 +497,14 @@ module Sinatra
477
497
  body = body.call if body.respond_to?(:call)
478
498
  template.new(path, line.to_i, options) { body }
479
499
  else
500
+ found = false
480
501
  path = ::File.join(views, "#{data}.#{engine}")
481
502
  Tilt.mappings.each do |ext, klass|
482
- break if File.exists?(path)
503
+ break if found = File.exists?(path)
483
504
  next unless klass == template
484
505
  path = ::File.join(views, "#{data}.#{ext}")
485
506
  end
507
+ throw :layout_missing if eat_errors and !found
486
508
  template.new(path, 1, options)
487
509
  end
488
510
  when data.is_a?(Proc) || data.is_a?(String)
@@ -524,6 +546,7 @@ module Sinatra
524
546
  @response = Response.new
525
547
  @params = indifferent_params(@request.params)
526
548
  template_cache.clear if settings.reload_templates
549
+ force_encoding(@request.route)
527
550
  force_encoding(@params)
528
551
 
529
552
  @response['Content-Type'] = nil
@@ -627,11 +650,7 @@ module Sinatra
627
650
  # Returns pass block.
628
651
  def process_route(pattern, keys, conditions)
629
652
  @original_params ||= @params
630
- @path ||= begin
631
- path = unescape(@request.path_info)
632
- path.empty? ? "/" : path
633
- end
634
- if match = pattern.match(@path)
653
+ if match = pattern.match(@request.route)
635
654
  values = match.captures.to_a
636
655
  params =
637
656
  if keys.any?
@@ -893,19 +912,25 @@ module Sinatra
893
912
  file = (file.nil? || file == true) ? (caller_files.first || File.expand_path($0)) : file
894
913
 
895
914
  begin
896
- app, data =
897
- ::IO.read(file).gsub("\r\n", "\n").split(/^__END__$/, 2)
915
+ io = ::IO.respond_to?(:binread) ? ::IO.binread(file) : ::IO.read(file)
916
+ app, data = io.gsub("\r\n", "\n").split(/^__END__$/, 2)
898
917
  rescue Errno::ENOENT
899
918
  app, data = nil
900
919
  end
901
920
 
902
921
  if data
922
+ if app and app =~ /([^\n]*\n)?#[^\n]*coding: *(\S+)/m
923
+ encoding = $2
924
+ else
925
+ encoding = settings.default_encoding
926
+ end
903
927
  lines = app.count("\n") + 1
904
928
  template = nil
929
+ force_encoding data, encoding
905
930
  data.each_line do |line|
906
931
  lines += 1
907
932
  if line =~ /^@@\s*(.*\S)\s*$/
908
- template = ''
933
+ template = force_encoding('', encoding)
909
934
  templates[$1.to_sym] = [template, file, lines]
910
935
  elsif template
911
936
  template << line
@@ -1181,7 +1206,8 @@ module Sinatra
1181
1206
  /\(.*\)/, # generated code
1182
1207
  /rubygems\/custom_require\.rb$/, # rubygems require hacks
1183
1208
  /active_support/, # active_support require hacks
1184
- /<internal:/, # internal in ruby >= 1.9.2
1209
+ /bundler(\/runtime)?\.rb/, # bundler require hacks
1210
+ /<internal:/ # internal in ruby >= 1.9.2
1185
1211
  ]
1186
1212
 
1187
1213
  # add rubinius (and hopefully other VM impls) ignore patterns ...
@@ -1209,20 +1235,22 @@ module Sinatra
1209
1235
  #
1210
1236
  # The latter might not be necessary if Rack handles it one day.
1211
1237
  # Keep an eye on Rack's LH #100.
1238
+ def force_encoding(*args) settings.force_encoding(*args) end
1212
1239
  if defined? Encoding
1213
- def force_encoding(data)
1214
- return if data == self || data.is_a?(Tempfile)
1240
+ def self.force_encoding(data, encoding = default_encoding)
1241
+ return if data == settings || data.is_a?(Tempfile)
1215
1242
  if data.respond_to? :force_encoding
1216
- data.force_encoding settings.default_encoding
1243
+ data.force_encoding encoding
1217
1244
  elsif data.respond_to? :each_value
1218
- data.each_value { |v| force_encoding(v) }
1245
+ data.each_value { |v| force_encoding(v, encoding) }
1219
1246
  elsif data.respond_to? :each
1220
- data.each { |v| force_encoding(v) }
1247
+ data.each { |v| force_encoding(v, encoding) }
1221
1248
  end
1249
+ data
1222
1250
  end
1223
1251
  else
1224
- def force_encoding(*) end
1225
- end
1252
+ def self.force_encoding(data, *) data end
1253
+ end
1226
1254
 
1227
1255
  reset!
1228
1256
 
@@ -1234,6 +1262,7 @@ module Sinatra
1234
1262
  set :logging, false
1235
1263
  set :method_override, false
1236
1264
  set :default_encoding, "utf-8"
1265
+ set :add_charset, [/^text\//, 'application/javascript', 'application/xml', 'application/xhtml+xml']
1237
1266
 
1238
1267
  class << self
1239
1268
  alias_method :methodoverride?, :method_override?
@@ -3,8 +3,8 @@ Gem::Specification.new do |s|
3
3
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
4
4
 
5
5
  s.name = 'sinatra'
6
- s.version = '1.1.0'
7
- s.date = '2010-10-24'
6
+ s.version = '1.1.2'
7
+ s.date = '2010-12-25'
8
8
 
9
9
  s.description = "Classy web-development dressed in a DSL"
10
10
  s.summary = "Classy web-development dressed in a DSL"
@@ -22,7 +22,9 @@ Gem::Specification.new do |s|
22
22
  README.fr.rdoc
23
23
  README.hu.rdoc
24
24
  README.jp.rdoc
25
+ README.pt-br.rdoc
25
26
  README.rdoc
27
+ README.ru.rdoc
26
28
  README.zh.rdoc
27
29
  Rakefile
28
30
  lib/sinatra.rb
@@ -112,7 +114,7 @@ Gem::Specification.new do |s|
112
114
 
113
115
  s.extra_rdoc_files = %w[README.rdoc README.de.rdoc README.jp.rdoc README.fr.rdoc README.es.rdoc README.hu.rdoc README.zh.rdoc LICENSE]
114
116
  s.add_dependency 'rack', '~> 1.1'
115
- s.add_dependency 'tilt', '~> 1.1'
117
+ s.add_dependency 'tilt', '~> 1.2'
116
118
  s.add_development_dependency 'rake'
117
119
  s.add_development_dependency 'shotgun', '~> 0.6'
118
120
  s.add_development_dependency 'rack-test', '>= 0.5.6'
@@ -125,7 +127,7 @@ Gem::Specification.new do |s|
125
127
  s.add_development_dependency 'RedCloth'
126
128
  s.add_development_dependency 'radius'
127
129
  s.add_development_dependency 'markaby'
128
- s.add_development_dependency 'coffee-script'
130
+ s.add_development_dependency 'coffee-script', '>= 2.0'
129
131
  s.add_development_dependency 'rdoc'
130
132
  s.add_development_dependency 'nokogiri'
131
133
 
@@ -16,7 +16,7 @@ class CoffeeTest < Test::Unit::TestCase
16
16
  it 'renders inline Coffee strings' do
17
17
  coffee_app { coffee "alert 'Aye!'\n" }
18
18
  assert ok?
19
- assert_equal "(function() {\n alert('Aye!');\n})();\n", body
19
+ assert_equal "(function() {\n alert('Aye!');\n}).call(this);\n", body
20
20
  end
21
21
 
22
22
  it 'defaults content type to javascript' do
@@ -45,13 +45,13 @@ class CoffeeTest < Test::Unit::TestCase
45
45
  it 'renders .coffee files in views path' do
46
46
  coffee_app { coffee :hello }
47
47
  assert ok?
48
- assert_equal "(function() {\n alert(\"Aye!\");\n})();\n", body
48
+ assert_equal "(function() {\n alert(\"Aye!\");\n}).call(this);\n", body
49
49
  end
50
50
 
51
51
  it 'ignores the layout option' do
52
52
  coffee_app { coffee :hello, :layout => :layout2 }
53
53
  assert ok?
54
- assert_equal "(function() {\n alert(\"Aye!\");\n})();\n", body
54
+ assert_equal "(function() {\n alert(\"Aye!\");\n}).call(this);\n", body
55
55
  end
56
56
 
57
57
  it "raises error if template not found" do
@@ -255,6 +255,16 @@ class AfterFilterTest < Test::Unit::TestCase
255
255
  assert ran_filter
256
256
  end
257
257
 
258
+ it 'changes to path_info from a pattern matching before filter are respoected when routing' do
259
+ mock_app do
260
+ before('/foo') { request.path_info = '/bar' }
261
+ get('/bar') { 'blah' }
262
+ end
263
+ get '/foo'
264
+ assert ok?
265
+ assert_equal 'blah', body
266
+ end
267
+
258
268
  it 'generates block arguments from route pattern' do
259
269
  subpath = nil
260
270
  mock_app do
@@ -89,6 +89,11 @@ class HAMLTest < Test::Unit::TestCase
89
89
  assert ok?
90
90
  assert_match(/^<!DOCTYPE html PUBLIC (.*) HTML 4.01/, body)
91
91
  end
92
+
93
+ it "is possible to pass locals" do
94
+ haml_app { haml "= foo", :locals => { :foo => 'bar' }}
95
+ assert_equal "bar\n", body
96
+ end
92
97
  end
93
98
  rescue
94
99
  warn "#{$!.to_s}: skipping haml tests"
@@ -53,6 +53,10 @@ class Test::Unit::TestCase
53
53
  response.body.to_s
54
54
  end
55
55
 
56
+ def assert_body(value)
57
+ assert_equal value.lstrip.gsub(/\s*\n\s*/, ""), body.lstrip.gsub(/\s*\n\s*/, "")
58
+ end
59
+
56
60
  # Delegate other missing methods to response.
57
61
  def method_missing(name, *args, &block)
58
62
  if response && response.respond_to?(name)
@@ -320,7 +320,7 @@ class HelpersTest < Test::Unit::TestCase
320
320
 
321
321
  get '/foo.xml'
322
322
  assert ok?
323
- assert_equal 'application/foo;charset=utf-8', response['Content-Type']
323
+ assert_equal 'application/foo', response['Content-Type']
324
324
  assert_equal 'I AM FOO', body
325
325
  end
326
326
 
@@ -334,6 +334,32 @@ class HelpersTest < Test::Unit::TestCase
334
334
 
335
335
  assert_raise(RuntimeError) { get '/foo.xml' }
336
336
  end
337
+
338
+ it 'only sets default charset for specific mime types' do
339
+ tests_ran = false
340
+ mock_app do
341
+ mime_type :foo, 'text/foo'
342
+ mime_type :bar, 'application/bar'
343
+ mime_type :baz, 'application/baz'
344
+ add_charset << mime_type(:baz)
345
+ get '/' do
346
+ assert_equal content_type(:txt), 'text/plain;charset=utf-8'
347
+ assert_equal content_type(:css), 'text/css;charset=utf-8'
348
+ assert_equal content_type(:html), 'text/html;charset=utf-8'
349
+ assert_equal content_type(:foo), 'text/foo;charset=utf-8'
350
+ assert_equal content_type(:xml), 'application/xml;charset=utf-8'
351
+ assert_equal content_type(:xhtml), 'application/xhtml+xml;charset=utf-8'
352
+ assert_equal content_type(:js), 'application/javascript;charset=utf-8'
353
+ assert_equal content_type(:bar), 'application/bar'
354
+ assert_equal content_type(:png), 'image/png'
355
+ assert_equal content_type(:baz), 'application/baz;charset=utf-8'
356
+ tests_ran = true
357
+ "done"
358
+ end
359
+ end
360
+ get '/'
361
+ assert tests_ran
362
+ end
337
363
  end
338
364
 
339
365
  describe 'send_file' do
@@ -378,7 +404,7 @@ class HelpersTest < Test::Unit::TestCase
378
404
  it 'sets the Content-Type response header if type option is set to a mime type' do
379
405
  send_file_app :type => 'application/octet-stream'
380
406
  get '/file.txt'
381
- assert_equal 'application/octet-stream;charset=utf-8', response['Content-Type']
407
+ assert_equal 'application/octet-stream', response['Content-Type']
382
408
  end
383
409
 
384
410
  it 'sets the Content-Length response header' do
@@ -420,13 +446,45 @@ class HelpersTest < Test::Unit::TestCase
420
446
  get '/file.txt'
421
447
  assert_equal 'attachment; filename="foo.txt"', response['Content-Disposition']
422
448
  end
449
+
450
+ it "is able to send files with unkown mime type" do
451
+ @file = File.dirname(__FILE__) + '/file.foobar'
452
+ File.open(@file, 'wb') { |io| io.write('Hello World') }
453
+ send_file_app
454
+ get '/file.txt'
455
+ assert_equal 'application/octet-stream', response['Content-Type']
456
+ end
457
+
458
+ it "does not override Content-Type if already set and no explicit type is given" do
459
+ path = @file
460
+ mock_app do
461
+ get '/' do
462
+ content_type :png
463
+ send_file path
464
+ end
465
+ end
466
+ get '/'
467
+ assert_equal 'image/png', response['Content-Type']
468
+ end
469
+
470
+ it "does override Content-Type even if already set, if explicit type is given" do
471
+ path = @file
472
+ mock_app do
473
+ get '/' do
474
+ content_type :png
475
+ send_file path, :type => :gif
476
+ end
477
+ end
478
+ get '/'
479
+ assert_equal 'image/gif', response['Content-Type']
480
+ end
423
481
  end
424
482
 
425
483
  describe 'cache_control' do
426
484
  setup do
427
485
  mock_app {
428
486
  get '/' do
429
- cache_control :public, :no_cache, :max_age => 60
487
+ cache_control :public, :no_cache, :max_age => 60.0
430
488
  'Hello World'
431
489
  end
432
490
  }