sinatra 1.1.4 → 1.2.0.a

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/README.ru.rdoc CHANGED
@@ -39,6 +39,10 @@ Sinatra — это предметно-ориентированный язык (D
39
39
  .. что-то удалить ..
40
40
  end
41
41
 
42
+ options '/' do
43
+ .. что-то ответить ..
44
+ end
45
+
42
46
  Маршруты сверяются с запросом по очередности определения. Первый же совпавший с запросом маршрут и будет вызван.
43
47
 
44
48
  Шаблоны маршрутов могут включать в себя параметры доступные в
@@ -90,7 +94,7 @@ Sinatra — это предметно-ориентированный язык (D
90
94
  end
91
95
 
92
96
  get '/foo' do
93
- # соответствует non-songbird браузерам
97
+ # соответствует с non-songbird браузерам
94
98
  end
95
99
 
96
100
  Другими доступными условиями являются +host_name+ и +provides+:
@@ -122,7 +126,7 @@ Sinatra — это предметно-ориентированный язык (D
122
126
  === Возвращаемые значения
123
127
 
124
128
  Возвращаемое значение блока маршрута ограничивается телом ответа, которое будет передано HTTP клиенту,
125
- или следующей "прослойкой" (middleware, промежуточная программа) в Rack стеке. Чаще всего это строка, как в вышеизложенных примерах.
129
+ или следующей подпрограммой (middleware) в Rack стеке. Чаще всего это строка, как в вышеизложенных примерах.
126
130
  Но и другие значения также приемлемы.
127
131
 
128
132
  Вы можете вернуть любой объект, который будет либо корректным Rack ответом, Rack
@@ -334,31 +338,16 @@ rdiscount gem/библиотека необходима для рендерин
334
338
  Отрисует <tt>./views/index.markdown</tt> (+md+ и +mkd+ также являются допустимыми файловыми
335
339
  расширениями).
336
340
 
337
- В Markdown невозможно вызывать методы или передавать локальные переменные.
338
- Следовательно, вам скорее всего придется использовать этот шаблон совместно с другим
339
- движком рендеринга:
341
+ В markdown невозможно вызывать методы или передавать локальные переменные. Следовательно, вам скорее всего придется
342
+ использовать этот шаблон совместно с другим движком рендеринга:
340
343
 
341
344
  erb :overview, :locals => { :text => markdown(:introduction) }
342
345
 
343
- Заметьте, что вы можете вызывать метод +markdown+ из других шаблонов:
346
+ Заметьте, что вы можете вызывать метод markdown из других шаблонов:
344
347
 
345
348
  %h1 Hello From Haml!
346
349
  %p= markdown(:greetings)
347
350
 
348
- Также возможно обрабатывать Markdown с помощью BlueCloth, а не RDiscount:
349
-
350
- require 'bluecloth'
351
-
352
- Tilt.register 'markdown', BlueClothTemplate
353
- Tilt.register 'mkd', BlueClothTemplate
354
- Tilt.register 'md', BlueClothTemplate
355
-
356
- get '/' do
357
- markdown :index
358
- end
359
-
360
- Отрисует <tt>./views/index.md</tt> с помощью BlueCloth.
361
-
362
351
  === Textile шаблоны
363
352
 
364
353
  RedCloth gem/библиотека необходима для рендеринга Textile шаблонов:
@@ -436,6 +425,26 @@ markaby gem/библиотека необходима для рендеринг
436
425
 
437
426
  Отрисует <tt>./views/index.mab</tt>.
438
427
 
428
+ Если у вас установлен Tilt версии 1.2 или выше, то вы также можете использовать внутристроковые
429
+ markaby шаблоны:
430
+
431
+ get '/' do
432
+ markaby { h1 "Welcome!" }
433
+ end
434
+
435
+ === Slim шаблоны
436
+
437
+ slim gem/библиотека необходима для рендеринга slim шаблонов:
438
+
439
+ ## Вам нужно будет подключить slim в приложении
440
+ require 'slim'
441
+
442
+ get '/' do
443
+ slim :index
444
+ end
445
+
446
+ Отрисует <tt>./views/index.slim</tt>.
447
+
439
448
  === CoffeeScript шаблоны
440
449
 
441
450
  coffee-script gem/библиотека и `coffee` бинарный файл необходимы для рендеринга CoffeeScript шаблонов:
@@ -449,13 +458,13 @@ coffee-script gem/библиотека и `coffee` бинарный файл н
449
458
 
450
459
  Отрисует <tt>./views/application.coffee</tt>.
451
460
 
452
- === Встроенные шаблоны
461
+ === Внутристроковые шаблоны
453
462
 
454
463
  get '/' do
455
464
  haml '%div.title Hello World'
456
465
  end
457
466
 
458
- Отрисует встроенный (строчный) шаблон.
467
+ Отрисует внутристроковый шаблон.
459
468
 
460
469
  === Доступ к переменным в шаблонах
461
470
 
@@ -516,8 +525,7 @@ coffee-script gem/библиотека и `coffee` бинарный файл н
516
525
  end
517
526
 
518
527
  Если шаблон с именем "layout" существует, то он будет использован каждый раз,
519
- когда шаблоны будут отрисовываться. Вы можете отключать layout-шаблон в каждом конкретном случае с помощью
520
- <tt>:layout => false</tt> или отключить его для всего приложения, например, так: <tt>set :haml, :layout => false</tt>.
528
+ когда шаблоны будут отрисовываться. Вы можете отключить layout-шаблон с помощью <tt>:layout => false</tt>.
521
529
 
522
530
  get '/' do
523
531
  haml :index, :layout => !request.xhr?
@@ -570,6 +578,16 @@ After-фильтры выполняются после каждого запро
570
578
  session[:last_slug] = slug
571
579
  end
572
580
 
581
+ Как и маршруты, фильтры могут использовать условия:
582
+
583
+ before :agent => /Songbird/ do
584
+ # ...
585
+ end
586
+
587
+ after '/blog/*', :host_name => 'example.com' do
588
+ # ...
589
+ end
590
+
573
591
  == Прерывание
574
592
 
575
593
  Чтобы незамедлительно прервать обработку запроса внутри фильтра или маршрута, используйте:
@@ -745,15 +763,15 @@ Sinatra устанавливает специальные <tt>not_found</tt> и
745
763
 
746
764
  content_type :foo
747
765
 
748
- == Rack "прослойки"
766
+ == Rack подпрограммы
749
767
 
750
768
  Sinatra использует Rack[http://rack.rubyforge.org/], минимальный стандартный
751
769
  интерфейс для веб-фреймворков на Ruby. Одной из самых интересных для разработчиков возможностей Rack
752
- является поддержка "прослоек" ("middleware") компонентов,
770
+ является поддержка подпрограмм ("middleware") -- компонентов,
753
771
  "сидящих" между сервером и вашим приложением, которые отслеживают и/или манипулируют
754
772
  HTTP запросами/ответами для предоставления различной функциональности.
755
773
 
756
- В Sinatra очень просто использовать такие "прослойки" с помощью метода +use+:
774
+ В Sinatra очень просто использовать такие Rack подпрограммы с помощью метода +use+:
757
775
 
758
776
  require 'sinatra'
759
777
  require 'my_custom_middleware'
@@ -774,7 +792,7 @@ Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html] DSL
774
792
  username == 'admin' && password == 'secret'
775
793
  end
776
794
 
777
- Rack распространяется с различными стандартными "прослойками"
795
+ Rack распространяется с различными стандартными подпрограммами
778
796
  для логирования, отладки, маршрутизации URL, аутентификации, обработки сессий. Sinatra использует
779
797
  многие из этих компонентов автоматически, основываясь на конфигурации, чтобы вам не приходилось
780
798
  регистрировать/использовать (+use+) их вручную.
@@ -814,12 +832,13 @@ Rack распространяется с различными стандартн
814
832
  Заметьте: Встроенные модули Sinatra::Test и Sinatra::TestHarness являются
815
833
  устаревшими, начиная с 0.9.2 релиза.
816
834
 
817
- == Sinatra::Base "прослойки", библиотеки и модульные приложения
835
+ == Sinatra::Base - Подпрограммы, библиотеки и модульные приложения
818
836
 
819
837
  Описание своего приложения самым простейшим способом (с помощью DSL верхнего уровня, как в примерах выше)
820
- работает отлично для крохотных приложений, но имеет множество недостатков, когда надо создать компоненты, такие как
821
- Rack middleware ("прослойки"), Rails metal, простые библиотеки с серверными компонентами,
822
- расширения Sinatra.
838
+ работает отлично для крохотных приложений, но имеет множество недостатков, когда надо
839
+ создать компоненты, такие как Rack
840
+ middleware, Rails metal, простые библиотеки с серверными компонентами,
841
+ Sinatra расширения.
823
842
  DSL верхнего уровня загрязняет пространство имен <tt>Object</tt> и подразумевает стиль конфигурации
824
843
  микро-приложения (например, единый файл приложения, ./public и
825
844
  ./views директории, создание логов, страницу деталей об исключениях
@@ -836,6 +855,13 @@ DSL верхнего уровня загрязняет пространство
836
855
  end
837
856
  end
838
857
 
858
+ MyApp класс является независимым Rack компонентом, который может исполнять роли
859
+ Rack подпрограммы, Rack приложения, Rails metal. Вы можете +use+ (использовать) или
860
+ +run+ (запустить) этот класс из rackup файла +config.ru+; или контролировать серверную
861
+ часть из библиотеки:
862
+
863
+ MyApp.run! :host => 'localhost', :port => 9090
864
+
839
865
  Методы, доступные Sinatra::Base сабклассам идентичны тем, что доступны
840
866
  в DSL верхнего уровня. Большинство приложений верхнего уровня могут быть
841
867
  конвертированы в Sinatra::Base компоненты с помощью двух модификаций:
@@ -848,68 +874,10 @@ DSL верхнего уровня загрязняет пространство
848
874
  Смотрите {Опции и Конфигурация}[http://www.sinatrarb.com/configuration.html] для детальной информации
849
875
  об опциях и их поведении.
850
876
 
851
- === Запуск модульных приложений
852
-
853
- Есть два общепринятых способа запускать модульные приложения: запуск напрямую с помощью <tt>run!</tt>:
854
-
855
- # my_app.rb
856
- require 'sinatra/base'
857
-
858
- class MyApp < Sinatra::Base
859
- # ... здесь код приложения ...
860
-
861
- # запускаем сервер, если исполняется текущий файл
862
- run! if app_file == $0
863
- end
864
-
865
- И запускаем с помощью:
866
-
867
- ruby my_app.rb
868
-
869
- Или с помощью конфигурационного файла <tt>config.ru</tt>, который позволяет использовать любой
870
- Rack-совместимый сервер приложений.
871
-
872
- # config.ru
873
- require 'my_app'
874
- run MyApp
875
-
876
- Запускаем:
877
-
878
- rackup -p 4567
879
-
880
- === Запуск "классических" приложений с config.ru
881
-
882
- Файл приложения:
883
-
884
- # app.rb
885
- require 'sinatra'
886
-
887
- get '/' do
888
- 'Hello world!'
889
- end
890
-
891
- И соответствующий <tt>config.ru</tt>:
892
-
893
- require 'app'
894
- run Sinatra::Application
895
-
896
- === Когда использовать config.ru?
897
-
898
- Вот несколько причин, по которым вы, возможно, захотите использовать <tt>config.ru</tt>:
899
-
900
- * вы хотите разворачивать свое приложение на различных Rack-совместимых серверах (Passenger, Unicorn,
901
- Heroku, ...).
902
- * вы хотите использовать более одного сабкласса <tt>Sinatra::Base</tt>.
903
- * вы хотите использовать Sinatra только в качестве "прослойки" Rack.
904
-
905
- <b>Совсем необязательно переходить на использование <tt>config.ru</tt> лишь потому, что вы стали
906
- использовать модульный стиль приложения. И необязательно использовать модульный стиль, чтобы
907
- запускать приложение с помощью <tt>config.ru</tt>.</b>
908
-
909
- === Использование Sinatra в качестве "прослойки"
877
+ === Использование Sinatra как подпрограммы
910
878
 
911
- Не только сама Sinatra может использовать "прослойки" Rack, но и любое Sinatra приложение
912
- само может быть добавлено к любому Rack эндпоинту в качестве "прослойки". Этим эндпоинтом
879
+ Не только сама Sinatra может использовать подпрограммы Rack, любое Sinatra приложение
880
+ само может быть добавлено к любому Rack эндпоинту в качестве подпрограммы. Этим эндпоинтом
913
881
  может быть другое Sinatra приложение, или приложение, основанное на Rack (Rails/Ramaze/Camping/...).
914
882
 
915
883
  require 'sinatra/base'
@@ -929,7 +897,7 @@ Rack-совместимый сервер приложений.
929
897
  end
930
898
 
931
899
  class MyApp < Sinatra::Base
932
- # "прослойка" будет запущена перед фильтрами
900
+ # подпрограмма будет запущена перед фильтрами
933
901
  use LoginScreen
934
902
 
935
903
  before do
@@ -1005,7 +973,7 @@ Sinatra::Application, иначе это будет сабкласс, котор
1005
973
 
1006
974
  У вас будет область видимости запроса внутри:
1007
975
 
1008
- * get/head/post/put/delete блоков
976
+ * get/head/post/put/delete/options блоков
1009
977
  * before/after фильтрах
1010
978
  * методах помощниках
1011
979
  * шаблонах/видах
data/README.zh.rdoc CHANGED
@@ -420,6 +420,19 @@ Rack body对象或者HTTP状态码:
420
420
 
421
421
  渲染 <tt>./views/index.mab</tt>。
422
422
 
423
+ === Slim 模板
424
+
425
+ 需要引入 slim gem/library 来渲染 Slim 模板:
426
+
427
+ ## 需要在你的应用中引入 slim
428
+ require 'slim'
429
+
430
+ get '/' do
431
+ slim :index
432
+ end
433
+
434
+ 渲染 <tt>./views/index.slim</tt>。
435
+
423
436
  === CoffeeScript 模板
424
437
 
425
438
  需要引入 coffee-script gem/library 并在路径中存在 `coffee` 二进制文件以渲染
data/Rakefile CHANGED
@@ -6,48 +6,45 @@ require 'date'
6
6
  task :default => :test
7
7
  task :spec => :test
8
8
 
9
- CLEAN.include "**/*.rbc"
10
-
11
9
  def source_version
12
- @source_version ||= begin
13
- line = File.read('lib/sinatra/base.rb')[/^\s*VERSION = .*/]
14
- line.match(/.*VERSION = '(.*)'/)[1]
15
- end
16
- end
17
-
18
- def prev_feature
19
- source_version.gsub(/^(\d\.)(\d+)\..*$/) { $1 + ($2.to_i - 1).to_s }
20
- end
21
-
22
- def prev_version
23
- return prev_feature + '.0' if source_version.end_with? '.0'
24
- source_version.gsub(/\d+$/) { |s| s.to_i - 1 }
10
+ line = File.read('lib/sinatra/base.rb')[/^\s*VERSION = .*/]
11
+ line.match(/.*VERSION = '(.*)'/)[1]
25
12
  end
26
13
 
27
14
  # SPECS ===============================================================
28
-
29
15
  task :test do
30
16
  ENV['LANG'] = 'C'
31
17
  ENV.delete 'LC_CTYPE'
32
18
  end
33
19
 
34
- Rake::TestTask.new(:test) do |t|
35
- t.test_files = FileList['test/*_test.rb']
36
- t.ruby_opts = ['-rubygems'] if defined? Gem
37
- t.ruby_opts << '-I.'
20
+ if !ENV['NO_TEST_FIX'] and RUBY_VERSION == '1.9.2' and RUBY_PATCHLEVEL == 0
21
+ # Avoids seg fault
22
+ task(:test) do
23
+ second_run = %w[settings rdoc markaby templates static textile].map { |l| "test/#{l}_test.rb" }
24
+ first_run = Dir.glob('test/*_test.rb') - second_run
25
+ [first_run, second_run].each { |f| sh "testrb #{f.join ' '}" }
26
+ end
27
+ else
28
+ Rake::TestTask.new(:test) do |t|
29
+ t.test_files = FileList['test/*_test.rb']
30
+ t.ruby_opts = ['-rubygems'] if defined? Gem
31
+ t.ruby_opts << '-I.'
32
+ end
38
33
  end
39
34
 
40
35
  # Rcov ================================================================
41
-
42
36
  namespace :test do
43
37
  desc 'Mesures test coverage'
44
38
  task :coverage do
45
39
  rm_f "coverage"
46
- sh "rcov -Ilib test/*_test.rb"
40
+ rcov = "rcov --text-summary -Ilib"
41
+ system("#{rcov} --no-html --no-color test/*_test.rb")
47
42
  end
48
43
  end
49
44
 
50
45
  # Website =============================================================
46
+ # Building docs requires HAML and the hanna gem:
47
+ # gem install mislav-hanna --source=http://gems.github.com
51
48
 
52
49
  desc 'Generate RDoc under doc/api'
53
50
  task 'doc' => ['doc:api']
@@ -55,7 +52,6 @@ task('doc:api') { sh "yardoc -o doc/api" }
55
52
  CLEAN.include 'doc/api'
56
53
 
57
54
  # README ===============================================================
58
-
59
55
  task :add_template, [:name] do |t, args|
60
56
  Dir.glob('README.*') do |file|
61
57
  code = File.read(file)
@@ -75,36 +71,6 @@ task :add_template, [:name] do |t, args|
75
71
  end
76
72
  end
77
73
 
78
- # Thanks in announcement ===============================================
79
-
80
- team = ["Ryan Tomayko", "Blake Mizerany", "Simon Rozet", "Konstantin Haase"]
81
- desc "list of contributors"
82
- task :thanks, [:release,:backports] do |t, a|
83
- a.with_defaults :release => "#{prev_version}..HEAD",
84
- :backports => "#{prev_feature}.0..#{prev_feature}.x"
85
- included = `git log --format=format:"%aN\t%s" #{a.release}`.lines.to_a
86
- excluded = `git log --format=format:"%aN\t%s" #{a.backports}`.lines.to_a
87
- commits = (included - excluded).group_by { |c| c[/^[^\t]+/] }
88
- authors = commits.keys.sort_by { |n| - commits[n].size } - team
89
- puts authors[0..-2].join(', ') << " and " << authors.last,
90
- "(based on commits included in #{a.release}, but not in #{a.backports})"
91
- end
92
-
93
- task :authors, [:format, :sep] do |t, a|
94
- a.with_defaults :format => "%s (%d)", :sep => ', '
95
- authors = Hash.new { |h,k| h[k] = 0 }
96
- blake = "Blake Mizerany"
97
- mapping = {
98
- "blake.mizerany@gmail.com" => blake, "bmizerany" => blake,
99
- "a_user@mac.com" => blake, "ichverstehe" => "Harry Vangberg",
100
- "Wu Jiang (nouse)" => "Wu Jiang" }
101
- `git shortlog -s`.lines.map do |line|
102
- num, name = line.split("\t", 2).map(&:strip)
103
- authors[mapping[name] || name] += num.to_i
104
- end
105
- puts authors.sort_by { |n,c| -c }.map { |e| a.format % e }.join(a.sep)
106
- end
107
-
108
74
  # PACKAGING ============================================================
109
75
 
110
76
  if defined?(Gem)
@@ -169,8 +135,8 @@ if defined?(Gem)
169
135
  sh <<-SH
170
136
  gem install #{package('.gem')} --local &&
171
137
  gem push #{package('.gem')} &&
172
- git commit --allow-empty -a -m '#{source_version} release' &&
173
- git tag -s v#{source_version} -m '#{source_version} release' &&
138
+ git add sinatra.gemspec &&
139
+ git commit --allow-empty -m '#{source_version} release' &&
174
140
  git tag -s #{source_version} -m '#{source_version} release' &&
175
141
  git push && (git push sinatra || true) &&
176
142
  git push --tags && (git push sinatra --tags || true)
data/lib/sinatra/base.rb CHANGED
@@ -7,7 +7,7 @@ require 'sinatra/showexceptions'
7
7
  require 'tilt'
8
8
 
9
9
  module Sinatra
10
- VERSION = '1.1.4'
10
+ VERSION = '1.2.0.a'
11
11
 
12
12
  # The request object. See Rack::Request for more info:
13
13
  # http://rack.rubyforge.org/doc/classes/Rack/Request.html
@@ -164,7 +164,7 @@ module Sinatra
164
164
  # Use the contents of the file at +path+ as the response body.
165
165
  def send_file(path, opts={})
166
166
  stat = File.stat(path)
167
- last_modified stat.mtime
167
+ last_modified(opts[:last_modified] || stat.mtime)
168
168
 
169
169
  if opts[:type] or not response['Content-Type']
170
170
  content_type opts[:type] || File.extname(path), :default => 'application/octet-stream'
@@ -324,17 +324,10 @@ module Sinatra
324
324
  # with a '304 Not Modified' response.
325
325
  def last_modified(time)
326
326
  return unless time
327
- if time.respond_to?(:to_time)
328
- time = time.to_time
329
- else
330
- ## make a best effort to convert something else to a time object
331
- ## if this fails, this should throw an ArgumentError, then the
332
- # rescue will result in an http 200, which should be safe
333
- time = Time.parse(time.to_s)
334
- end
335
- response['Last-Modified'] = time.httpdate
336
- # compare based on seconds since epoch
337
- halt 304 if Time.httpdate(request.env['HTTP_IF_MODIFIED_SINCE']).to_i >= time.to_i
327
+ time = time.to_time if time.respond_to?(:to_time)
328
+ time = Time.parse time.strftime('%FT%T%:z') if time.respond_to?(:strftime)
329
+ response['Last-Modified'] = time.respond_to?(:httpdate) ? time.httpdate : time.to_s
330
+ halt 304 if Time.httpdate(request.env['HTTP_IF_MODIFIED_SINCE']) >= time
338
331
  rescue ArgumentError
339
332
  end
340
333
 
@@ -383,8 +376,6 @@ module Sinatra
383
376
  attr_accessor :content_type
384
377
  end
385
378
 
386
- include Tilt::CompileSite
387
-
388
379
  def erb(template, options={}, locals={})
389
380
  render :erb, template, options, locals
390
381
  end
@@ -413,7 +404,8 @@ module Sinatra
413
404
  end
414
405
 
415
406
  def builder(template=nil, options={}, locals={}, &block)
416
- render_xml(:builder, template, options, locals, &block)
407
+ options[:default_content_type] = :xml
408
+ render_ruby(:builder, template, options, locals, &block)
417
409
  end
418
410
 
419
411
  def liquid(template, options={}, locals={})
@@ -436,8 +428,8 @@ module Sinatra
436
428
  render :radius, template, options, locals
437
429
  end
438
430
 
439
- def markaby(template, options={}, locals={})
440
- render :mab, template, options, locals
431
+ def markaby(template=nil, options={}, locals={}, &block)
432
+ render_ruby(:mab, template, options, locals, &block)
441
433
  end
442
434
 
443
435
  def coffee(template, options={}, locals={})
@@ -446,14 +438,17 @@ module Sinatra
446
438
  end
447
439
 
448
440
  def nokogiri(template=nil, options={}, locals={}, &block)
449
- options[:layout] = false if Tilt::VERSION <= "1.1"
450
- render_xml(:nokogiri, template, options, locals, &block)
441
+ options[:default_content_type] = :xml
442
+ render_ruby(:nokogiri, template, options, locals, &block)
443
+ end
444
+
445
+ def slim(template, options={}, locals={})
446
+ render :slim, template, options, locals
451
447
  end
452
448
 
453
449
  private
454
450
  # logic shared between builder and nokogiri
455
- def render_xml(engine, template, options={}, locals={}, &block)
456
- options[:default_content_type] = :xml
451
+ def render_ruby(engine, template, options={}, locals={}, &block)
457
452
  options, template = template, nil if template.is_a?(Hash)
458
453
  template = Proc.new { block } if template.nil?
459
454
  render engine, template, options, locals
@@ -472,7 +467,8 @@ module Sinatra
472
467
  layout = options.delete(:layout)
473
468
  eat_errors = layout.nil?
474
469
  layout = @default_layout if layout.nil? or layout == true
475
- content_type = options.delete(:content_type) || options.delete(:default_content_type)
470
+ content_type = options.delete(:content_type) || options.delete(:default_content_type)
471
+ layout_engine = options.delete(:layout_engine) || engine
476
472
 
477
473
  # compile and render template
478
474
  layout_was = @default_layout
@@ -484,7 +480,7 @@ module Sinatra
484
480
  # render layout
485
481
  if layout
486
482
  options = options.merge(:views => views, :layout => false, :eat_errors => eat_errors)
487
- catch(:layout_missing) { output = render(engine, layout, options, locals) { output }}
483
+ catch(:layout_missing) { output = render(layout_engine, layout, options, locals) { output }}
488
484
  end
489
485
 
490
486
  output.extend(ContentTyped).content_type = content_type if content_type
@@ -505,12 +501,13 @@ module Sinatra
505
501
  template.new(path, line.to_i, options) { body }
506
502
  else
507
503
  found = false
504
+ path = ::File.join(views, "#{data}.#{engine}")
508
505
  Tilt.mappings.each do |ext, klass|
509
- next unless Array(klass).include? template
510
- path = ::File.join(views, "#{data}.#{ext}")
511
506
  break if found = File.exists?(path)
507
+ next unless klass == template
508
+ path = ::File.join(views, "#{data}.#{ext}")
512
509
  end
513
- throw :layout_missing if eat_errors and not found
510
+ throw :layout_missing if eat_errors and !found
514
511
  template.new(path, 1, options)
515
512
  end
516
513
  when data.is_a?(Proc) || data.is_a?(String)
@@ -956,21 +953,22 @@ module Sinatra
956
953
  # Define a before filter; runs before all requests within the same
957
954
  # context as route handlers and may access/modify the request and
958
955
  # response.
959
- def before(path = nil, &block)
960
- add_filter(:before, path, &block)
956
+ def before(path = nil, options = {}, &block)
957
+ add_filter(:before, path, options, &block)
961
958
  end
962
959
 
963
960
  # Define an after filter; runs after all requests within the same
964
961
  # context as route handlers and may access/modify the request and
965
962
  # response.
966
- def after(path = nil, &block)
967
- add_filter(:after, path, &block)
963
+ def after(path = nil, options = {}, &block)
964
+ add_filter(:after, path, options, &block)
968
965
  end
969
966
 
970
967
  # add a filter
971
- def add_filter(type, path = nil, &block)
968
+ def add_filter(type, path = nil, options = {}, &block)
972
969
  return filters[type] << block unless path
973
- block, *arguments = compile!(type, path, block)
970
+ path, options = //, path if path.respond_to?(:each_pair)
971
+ block, *arguments = compile!(type, path, block, options)
974
972
  add_filter(type) do
975
973
  process_route(*arguments) { instance_eval(&block) }
976
974
  end
@@ -992,7 +990,7 @@ module Sinatra
992
990
  # Will set params[:agent].
993
991
  def user_agent(pattern)
994
992
  condition do
995
- if request.user_agent.to_s =~ pattern
993
+ if request.user_agent =~ pattern
996
994
  @params[:agent] = $~[1..-1]
997
995
  true
998
996
  else
@@ -1009,7 +1007,7 @@ module Sinatra
1009
1007
  condition do
1010
1008
  matching_types = (request.accept & types)
1011
1009
  unless matching_types.empty?
1012
- content_type matching_types.first
1010
+ response.headers['Content-Type'] = matching_types.first
1013
1011
  true
1014
1012
  else
1015
1013
  false
@@ -1028,18 +1026,18 @@ module Sinatra
1028
1026
  route('HEAD', path, opts, &block)
1029
1027
  end
1030
1028
 
1031
- def put(path, opts={}, &bk); route 'PUT', path, opts, &bk end
1032
- def post(path, opts={}, &bk); route 'POST', path, opts, &bk end
1033
- def delete(path, opts={}, &bk); route 'DELETE', path, opts, &bk end
1034
- def head(path, opts={}, &bk); route 'HEAD', path, opts, &bk end
1029
+ def put(path, opts={}, &bk) route 'PUT', path, opts, &bk end
1030
+ def post(path, opts={}, &bk) route 'POST', path, opts, &bk end
1031
+ def delete(path, opts={}, &bk) route 'DELETE', path, opts, &bk end
1032
+ def head(path, opts={}, &bk) route 'HEAD', path, opts, &bk end
1033
+ def options(path, opts={}, &bk) route 'OPTIONS', path, opts, &bk end
1035
1034
 
1036
1035
  private
1037
1036
  def route(verb, path, options={}, &block)
1038
1037
  # Because of self.options.host
1039
1038
  host_name(options.delete(:host)) if options.key?(:host)
1040
- options.each { |option, args| send(option, *args) }
1041
1039
 
1042
- block, pattern, keys, conditions = compile! verb, path, block
1040
+ block, pattern, keys, conditions = compile! verb, path, block, options
1043
1041
  invoke_hook(:route_added, verb, path, block)
1044
1042
 
1045
1043
  (@routes[verb] ||= []).
@@ -1050,7 +1048,8 @@ module Sinatra
1050
1048
  extensions.each { |e| e.send(name, *args) if e.respond_to?(name) }
1051
1049
  end
1052
1050
 
1053
- def compile!(verb, path, block)
1051
+ def compile!(verb, path, block, options = {})
1052
+ options.each_pair { |option, args| send(option, *args) }
1054
1053
  method_name = "#{verb} #{path}"
1055
1054
 
1056
1055
  define_method(method_name, &block)
@@ -1068,7 +1067,7 @@ module Sinatra
1068
1067
  def compile(path)
1069
1068
  keys = []
1070
1069
  if path.respond_to? :to_str
1071
- special_chars = %w{. + ( ) $}
1070
+ special_chars = %w{. + ( )}
1072
1071
  pattern =
1073
1072
  path.to_str.gsub(/((:\w+)|[\*#{special_chars.join}])/) do |match|
1074
1073
  case match
@@ -1364,7 +1363,7 @@ module Sinatra
1364
1363
  end
1365
1364
  end
1366
1365
 
1367
- delegate :get, :put, :post, :delete, :head, :template, :layout,
1366
+ delegate :get, :put, :post, :delete, :head, :options, :template, :layout,
1368
1367
  :before, :after, :error, :not_found, :configure, :set, :mime_type,
1369
1368
  :enable, :disable, :use, :development?, :test?, :production?,
1370
1369
  :helpers, :settings