sinatra 1.3.0.d → 1.3.0.e

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.

Files changed (53) hide show
  1. data/.gitignore +6 -0
  2. data/.yardopts +4 -0
  3. data/CHANGES +80 -3
  4. data/Gemfile +18 -12
  5. data/README.de.rdoc +189 -381
  6. data/README.es.rdoc +193 -316
  7. data/README.fr.rdoc +327 -475
  8. data/README.jp.rdoc +7 -1
  9. data/README.rdoc +132 -101
  10. data/README.ru.rdoc +3 -3
  11. data/README.zh.rdoc +2 -2
  12. data/Rakefile +19 -27
  13. data/lib/sinatra/base.rb +186 -262
  14. data/lib/sinatra/version.rb +3 -0
  15. data/sinatra.gemspec +12 -128
  16. data/test/base_test.rb +1 -1
  17. data/test/builder_test.rb +1 -1
  18. data/test/coffee_test.rb +1 -1
  19. data/test/creole_test.rb +1 -1
  20. data/test/delegator_test.rb +9 -7
  21. data/test/encoding_test.rb +1 -1
  22. data/test/erb_test.rb +1 -1
  23. data/test/extensions_test.rb +1 -1
  24. data/test/filter_test.rb +7 -4
  25. data/test/haml_test.rb +1 -1
  26. data/test/helper.rb +16 -1
  27. data/test/helpers_test.rb +12 -1
  28. data/test/less_test.rb +1 -1
  29. data/test/liquid_test.rb +1 -1
  30. data/test/mapped_error_test.rb +44 -1
  31. data/test/markaby_test.rb +1 -1
  32. data/test/markdown_test.rb +1 -1
  33. data/test/middleware_test.rb +1 -1
  34. data/test/nokogiri_test.rb +1 -1
  35. data/test/radius_test.rb +1 -1
  36. data/test/rdoc_test.rb +2 -2
  37. data/test/readme_test.rb +136 -0
  38. data/test/request_test.rb +1 -1
  39. data/test/response_test.rb +13 -3
  40. data/test/result_test.rb +3 -3
  41. data/test/route_added_hook_test.rb +1 -1
  42. data/test/routing_test.rb +9 -2
  43. data/test/sass_test.rb +1 -1
  44. data/test/scss_test.rb +1 -1
  45. data/test/server_test.rb +1 -1
  46. data/test/settings_test.rb +55 -22
  47. data/test/sinatra_test.rb +1 -1
  48. data/test/slim_test.rb +1 -1
  49. data/test/static_test.rb +22 -1
  50. data/test/templates_test.rb +1 -1
  51. data/test/textile_test.rb +1 -1
  52. metadata +47 -59
  53. data/lib/sinatra/rack.rb +0 -44
@@ -1547,7 +1547,7 @@ DSL верхнего уровня "загрязняет" пространств
1547
1547
  Rack-совместимый сервер приложений.
1548
1548
 
1549
1549
  # config.ru
1550
- require 'my_app'
1550
+ require './my_app'
1551
1551
  run MyApp
1552
1552
 
1553
1553
  Запускаем:
@@ -1567,7 +1567,7 @@ Rack-совместимый сервер приложений.
1567
1567
 
1568
1568
  И соответствующий <tt>config.ru</tt>:
1569
1569
 
1570
- require 'app'
1570
+ require './app'
1571
1571
  run Sinatra::Application
1572
1572
 
1573
1573
  === Когда использовать config.ru?
@@ -1738,7 +1738,7 @@ Sinatra приложения могут быть запущены напряму
1738
1738
  языки шаблонов, работает.
1739
1739
 
1740
1740
  [ JRuby ]
1741
- JRuby официально поддерживается (JRuby >= 1.6.0). Нет никаких проблем с
1741
+ JRuby официально поддерживается (JRuby >= 1.6.1). Нет никаких проблем с
1742
1742
  использованием альтернативных шаблонов. Тем не менее, если вы выбираете
1743
1743
  JRuby, то, пожалуйста, посмотрите на JRuby Rack-сервера, так как Thin не
1744
1744
  поддерживается полностью на JRuby. Поддержка расширений на C в JRuby все
@@ -1513,7 +1513,7 @@ Sinatra::Base子类可用的方法实际上就是通过顶层 DSL
1513
1513
  或者使用一个 <tt>config.ru</tt>,允许你使用任何Rack处理器:
1514
1514
 
1515
1515
  # config.ru
1516
- require 'my_app'
1516
+ require './my_app'
1517
1517
  run MyApp
1518
1518
 
1519
1519
  运行:
@@ -1533,7 +1533,7 @@ Sinatra::Base子类可用的方法实际上就是通过顶层 DSL
1533
1533
 
1534
1534
  加入相应的 <tt>config.ru</tt>:
1535
1535
 
1536
- require 'app'
1536
+ require './app'
1537
1537
  run Sinatra::Application
1538
1538
 
1539
1539
  === 什么时候用 config.ru?
data/Rakefile CHANGED
@@ -10,8 +10,8 @@ CLEAN.include "**/*.rbc"
10
10
 
11
11
  def source_version
12
12
  @source_version ||= begin
13
- line = File.read('lib/sinatra/base.rb')[/^\s*VERSION = .*/]
14
- line.match(/.*VERSION = '(.*)'/)[1]
13
+ load './lib/sinatra/version.rb'
14
+ Sinatra::VERSION
15
15
  end
16
16
  end
17
17
 
@@ -37,6 +37,16 @@ Rake::TestTask.new(:test) do |t|
37
37
  t.ruby_opts << '-I.'
38
38
  end
39
39
 
40
+ Rake::TestTask.new(:"test:core") do |t|
41
+ core_tests = %w[base delegator encoding extensions filter
42
+ helpers mapped_error middleware radius rdoc
43
+ readme request response result route_added_hook
44
+ routing server settings sinatra static templates]
45
+ t.test_files = core_tests.map {|n| "test/#{n}_test.rb"}
46
+ t.ruby_opts = ["-rubygems"] if defined? Gem
47
+ t.ruby_opts << "-I."
48
+ end
49
+
40
50
  # Rcov ================================================================
41
51
 
42
52
  namespace :test do
@@ -90,18 +100,22 @@ task :thanks, [:release,:backports] do |t, a|
90
100
  "(based on commits included in #{a.release}, but not in #{a.backports})"
91
101
  end
92
102
 
93
- task :authors, [:format, :sep] do |t, a|
94
- a.with_defaults :format => "%s (%d)", :sep => ', '
103
+ desc "list of authors"
104
+ task :authors, [:commit_range, :format, :sep] do |t, a|
105
+ a.with_defaults :format => "%s (%d)", :sep => ", "
95
106
  authors = Hash.new { |h,k| h[k] = 0 }
96
107
  blake = "Blake Mizerany"
108
+ overall = 0
97
109
  mapping = {
98
110
  "blake.mizerany@gmail.com" => blake, "bmizerany" => blake,
99
111
  "a_user@mac.com" => blake, "ichverstehe" => "Harry Vangberg",
100
112
  "Wu Jiang (nouse)" => "Wu Jiang" }
101
- `git shortlog -s`.lines.map do |line|
113
+ `git shortlog -s #{a.commit_range}`.lines.map do |line|
102
114
  num, name = line.split("\t", 2).map(&:strip)
103
115
  authors[mapping[name] || name] += num.to_i
116
+ overall += num.to_i
104
117
  end
118
+ puts "#{overall} commits by #{authors.count} authors:"
105
119
  puts authors.sort_by { |n,c| -c }.map { |e| a.format % e }.join(a.sep)
106
120
  end
107
121
 
@@ -143,28 +157,6 @@ if defined?(Gem)
143
157
  SH
144
158
  end
145
159
 
146
- task 'sinatra.gemspec' => FileList['{lib,test,compat}/**','Rakefile','CHANGES','*.rdoc'] do |f|
147
- # read spec file and split out manifest section
148
- spec = File.read(f.name)
149
- head, manifest, tail = spec.split(" # = MANIFEST =\n")
150
- # replace version and date
151
- head.sub!(/\.version = '.*'/, ".version = '#{source_version}'")
152
- head.sub!(/\.date = '.*'/, ".date = '#{Date.today.to_s}'")
153
- # determine file list from git ls-files
154
- files = `git ls-files`.
155
- split("\n").
156
- sort.
157
- reject{ |file| file =~ /^\./ }.
158
- reject { |file| file =~ /^doc/ }.
159
- map{ |file| " #{file}" }.
160
- join("\n")
161
- # piece file back together and write...
162
- manifest = " s.files = %w[\n#{files}\n ]\n"
163
- spec = [head,manifest,tail].join(" # = MANIFEST =\n")
164
- File.open(f.name, 'w') { |io| io.write(spec) }
165
- puts "updated #{f.name}"
166
- end
167
-
168
160
  task 'release' => ['test', package('.gem')] do
169
161
  sh <<-SH
170
162
  gem install #{package('.gem')} --local &&
@@ -1,13 +1,17 @@
1
+ # external dependencies
2
+ require 'rack'
3
+ require 'tilt'
4
+
5
+ # stdlib dependencies
1
6
  require 'thread'
2
7
  require 'time'
3
8
  require 'uri'
4
- require 'sinatra/rack'
9
+
10
+ # other files we need
5
11
  require 'sinatra/showexceptions'
6
- require 'tilt'
12
+ require 'sinatra/version'
7
13
 
8
14
  module Sinatra
9
- VERSION = '1.3.0.d'
10
-
11
15
  # The request object. See Rack::Request for more info:
12
16
  # http://rack.rubyforge.org/doc/classes/Rack/Request.html
13
17
  class Request < Rack::Request
@@ -35,15 +39,6 @@ module Sinatra
35
39
  @env.include? "HTTP_X_FORWARDED_HOST"
36
40
  end
37
41
 
38
- def route
39
- @route ||= Rack::Utils.unescape(path_info)
40
- end
41
-
42
- def path_info=(value)
43
- @route = nil
44
- super
45
- end
46
-
47
42
  private
48
43
 
49
44
  def accept_entry(entry)
@@ -59,20 +54,20 @@ module Sinatra
59
54
  # http://rack.rubyforge.org/doc/classes/Rack/Response.html
60
55
  # http://rack.rubyforge.org/doc/classes/Rack/Response/Helpers.html
61
56
  class Response < Rack::Response
57
+ def body=(value)
58
+ value = value.body while value.respond_to? :body and value.body != value
59
+ @body = value.respond_to?(:to_str) ? [value.to_str] : value
60
+ end
61
+
62
+ def each
63
+ block_given? ? super : enum_for(:each)
64
+ end
65
+
62
66
  def finish
63
- @body = block if block_given?
64
- if [204, 304].include?(status.to_i)
65
- header.delete "Content-Type"
66
- [status.to_i, header.to_hash, []]
67
- else
68
- body = @body || []
69
- body = [body] if body.respond_to? :to_str
70
- if body.respond_to?(:to_ary)
71
- header["Content-Length"] = body.to_ary.
72
- inject(0) { |len, part| len + Rack::Utils.bytesize(part) }.to_s
73
- end
74
- [status.to_i, header.to_hash, body]
67
+ if body.respond_to? :to_ary and not [204, 304].include?(status.to_i)
68
+ headers["Content-Length"] = body.inject(0) { |l, p| l + Rack::Utils.bytesize(p) }.to_s
75
69
  end
70
+ super
76
71
  end
77
72
  end
78
73
 
@@ -117,9 +112,7 @@ module Sinatra
117
112
  return addr if addr =~ /\A[A-z][A-z0-9\+\.\-]*:/
118
113
  uri = [host = ""]
119
114
  if absolute
120
- host << 'http'
121
- host << 's' if request.secure?
122
- host << "://"
115
+ host << "http#{'s' if request.secure?}://"
123
116
  if request.forwarded? or request.port != (request.secure? ? 443 : 80)
124
117
  host << request.host_with_port
125
118
  else
@@ -198,9 +191,6 @@ module Sinatra
198
191
 
199
192
  # Use the contents of the file at +path+ as the response body.
200
193
  def send_file(path, opts={})
201
- stat = File.stat(path)
202
- last_modified(opts[:last_modified] || stat.mtime)
203
-
204
194
  if opts[:type] or not response['Content-Type']
205
195
  content_type opts[:type] || File.extname(path), :default => 'application/octet-stream'
206
196
  end
@@ -211,90 +201,17 @@ module Sinatra
211
201
  response['Content-Disposition'] = 'inline'
212
202
  end
213
203
 
214
- file_length = opts[:length] || stat.size
215
- sf = StaticFile.open(path, 'rb')
216
- if ! sf.parse_ranges(env, file_length)
217
- response['Content-Range'] = "bytes */#{file_length}"
218
- halt 416
219
- elsif r=sf.range
220
- response['Content-Range'] = "bytes #{r.begin}-#{r.end}/#{file_length}"
221
- response['Content-Length'] = (r.end - r.begin + 1).to_s
222
- halt 206, sf
223
- else
224
- response['Content-Length'] ||= file_length.to_s
225
- halt sf
226
- end
204
+ last_modified opts[:last_modified] if opts[:last_modified]
205
+
206
+ file = Rack::File.new nil
207
+ file.path = path
208
+ result = file.serving env
209
+ result[1].each { |k,v| headers[k] ||= v }
210
+ halt result[0], result[2]
227
211
  rescue Errno::ENOENT
228
212
  not_found
229
213
  end
230
214
 
231
- # Rack response body used to deliver static files. The file contents are
232
- # generated iteratively in 8K chunks.
233
- class StaticFile < ::File #:nodoc:
234
- alias_method :to_path, :path
235
-
236
- attr_accessor :range # a Range or nil
237
-
238
- # Checks for byte-ranges in the request and sets self.range appropriately.
239
- # Returns false if the ranges are unsatisfiable and the request should return 416.
240
- def parse_ranges(env, size)
241
- #r = Rack::Utils::byte_ranges(env, size) # TODO: not available yet in released Rack
242
- r = byte_ranges(env, size)
243
- return false if r == [] # Unsatisfiable; report error
244
- @range = r[0] if r && r.length == 1 # Ignore multiple-range requests for now
245
- return true
246
- end
247
-
248
- # TODO: Copied from the new method Rack::Utils::byte_ranges; this method can be removed once
249
- # a version of Rack with that method is released and Sinatra can depend on it.
250
- def byte_ranges(env, size)
251
- # See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
252
- http_range = env['HTTP_RANGE']
253
- return nil unless http_range
254
- ranges = []
255
- http_range.split(/,\s*/).each do |range_spec|
256
- matches = range_spec.match(/bytes=(\d*)-(\d*)/)
257
- return nil unless matches
258
- r0,r1 = matches[1], matches[2]
259
- if r0.empty?
260
- return nil if r1.empty?
261
- # suffix-byte-range-spec, represents trailing suffix of file
262
- r0 = [size - r1.to_i, 0].max
263
- r1 = size - 1
264
- else
265
- r0 = r0.to_i
266
- if r1.empty?
267
- r1 = size - 1
268
- else
269
- r1 = r1.to_i
270
- return nil if r1 < r0 # backwards range is syntactically invalid
271
- r1 = size-1 if r1 >= size
272
- end
273
- end
274
- ranges << (r0..r1) if r0 <= r1
275
- end
276
- ranges
277
- end
278
-
279
- CHUNK_SIZE = 8192
280
-
281
- def each
282
- if @range
283
- self.pos = @range.begin
284
- length = @range.end - @range.begin + 1
285
- while length > 0 && (buf = read([CHUNK_SIZE,length].min))
286
- yield buf
287
- length -= buf.length
288
- end
289
- else
290
- rewind
291
- while buf = read(CHUNK_SIZE)
292
- yield buf
293
- end
294
- end
295
- end
296
- end
297
-
298
215
  # Specify response freshness policy for HTTP caches (Cache-Control header).
299
216
  # Any number of non-value directives (:public, :private, :no_cache,
300
217
  # :no_store, :must_revalidate, :proxy_revalidate) may be passed along with
@@ -375,8 +292,8 @@ module Sinatra
375
292
  # When the current request includes an 'If-None-Match' header with a
376
293
  # matching etag, execution is immediately halted. If the request method is
377
294
  # GET or HEAD, a '304 Not Modified' response is sent.
378
- def etag(value, kind=:strong)
379
- raise TypeError, ":strong or :weak expected" if ![:strong,:weak].include?(kind)
295
+ def etag(value, kind = :strong)
296
+ raise ArgumentError, ":strong or :weak expected" unless [:strong,:weak].include?(kind)
380
297
  value = '"%s"' % value
381
298
  value = 'W/' + value if kind == :weak
382
299
  response['ETag'] = value
@@ -446,6 +363,11 @@ module Sinatra
446
363
  attr_accessor :content_type
447
364
  end
448
365
 
366
+ def initialize
367
+ super
368
+ @default_layout = :layout
369
+ end
370
+
449
371
  def erb(template, options={}, locals={})
450
372
  render :erb, template, options, locals
451
373
  end
@@ -549,7 +471,6 @@ module Sinatra
549
471
  # extract generic options
550
472
  locals = options.delete(:locals) || locals || {}
551
473
  views = options.delete(:views) || settings.views || "./views"
552
- @default_layout = :layout if @default_layout.nil?
553
474
  layout = options.delete(:layout)
554
475
  eat_errors = layout.nil?
555
476
  layout = @default_layout if layout.nil? or layout == true
@@ -620,6 +541,7 @@ module Sinatra
620
541
  attr_reader :template_cache
621
542
 
622
543
  def initialize(app=nil)
544
+ super()
623
545
  @app = app
624
546
  @template_cache = Tilt::Cache.new
625
547
  yield self if block_given?
@@ -638,15 +560,15 @@ module Sinatra
638
560
  @response = Response.new
639
561
  @params = indifferent_params(@request.params)
640
562
  template_cache.clear if settings.reload_templates
641
- force_encoding(@request.route)
642
563
  force_encoding(@params)
643
564
 
644
565
  @response['Content-Type'] = nil
645
566
  invoke { dispatch! }
646
567
  invoke { error_block!(response.status) }
568
+
647
569
  unless @response['Content-Type']
648
- if body.respond_to?(:to_ary) and body.first.respond_to? :content_type
649
- content_type body.first.content_type
570
+ if body.respond_to? :to_ary and body[0].respond_to? :content_type
571
+ content_type body[0].content_type
650
572
  else
651
573
  content_type :html
652
574
  end
@@ -667,7 +589,7 @@ module Sinatra
667
589
 
668
590
  def options
669
591
  warn "Sinatra::Base#options is deprecated and will be removed, " \
670
- "use #settings insetad.\n\tfrom #{caller.first}"
592
+ "use #settings instead.\n\tfrom #{caller.first}"
671
593
  settings
672
594
  end
673
595
 
@@ -699,15 +621,15 @@ module Sinatra
699
621
  # Run filters defined on the class and all superclasses.
700
622
  def filter!(type, base = settings)
701
623
  filter! type, base.superclass if base.superclass.respond_to?(:filters)
702
- base.filters[type].each { |block| instance_eval(&block) }
624
+ base.filters[type].each { |args| process_route(*args) }
703
625
  end
704
626
 
705
627
  # Run routes defined on the class and all superclasses.
706
628
  def route!(base = settings, pass_block=nil)
707
629
  if routes = base.routes[@request.request_method]
708
630
  routes.each do |pattern, keys, conditions, block|
709
- pass_block = process_route(pattern, keys, conditions) do
710
- route_eval(&block)
631
+ pass_block = process_route(pattern, keys, conditions) do |*args|
632
+ route_eval { block[*args] }
711
633
  end
712
634
  end
713
635
  end
@@ -722,8 +644,8 @@ module Sinatra
722
644
  end
723
645
 
724
646
  # Run a route block and throw :halt with the result.
725
- def route_eval(&block)
726
- throw :halt, instance_eval(&block)
647
+ def route_eval
648
+ throw :halt, yield
727
649
  end
728
650
 
729
651
  # If the current request matches pattern and conditions, fill params
@@ -731,12 +653,12 @@ module Sinatra
731
653
  # Revert params afterwards.
732
654
  #
733
655
  # Returns pass block.
734
- def process_route(pattern, keys, conditions)
656
+ def process_route(pattern, keys, conditions, block = nil)
735
657
  @original_params ||= @params
736
- route = @request.route
658
+ route = @request.path_info
737
659
  route = '/' if route.empty? and not settings.empty_path_info?
738
660
  if match = pattern.match(route)
739
- values = match.captures.to_a
661
+ values = match.captures.to_a.map { |v| force_encoding URI.decode(v) if v }
740
662
  params =
741
663
  if keys.any?
742
664
  keys.zip(values).inject({}) do |hash,(k,v)|
@@ -755,9 +677,8 @@ module Sinatra
755
677
  @params = @original_params.merge(params)
756
678
  @block_params = values
757
679
  catch(:pass) do
758
- conditions.each { |cond|
759
- throw :pass if instance_eval(&cond) == false }
760
- yield
680
+ conditions.each { |c| throw :pass if c.bind(self).call == false }
681
+ block ? block[self, @block_params] : yield(self, @block_params)
761
682
  end
762
683
  end
763
684
  ensure
@@ -787,6 +708,7 @@ module Sinatra
787
708
  return unless path.start_with?(public_dir) and File.file?(path)
788
709
 
789
710
  env['sinatra.static_file'] = path
711
+ cache_control *settings.static_cache_control if settings.static_cache_control?
790
712
  send_file path, :disposition => nil
791
713
  end
792
714
 
@@ -805,36 +727,16 @@ module Sinatra
805
727
  end
806
728
 
807
729
  # Run the block with 'throw :halt' support and apply result to the response.
808
- def invoke(&block)
809
- res = catch(:halt) { instance_eval(&block) }
810
- return if res.nil?
811
-
812
- case
813
- when res.respond_to?(:to_str)
814
- @response.body = [res]
815
- when res.respond_to?(:to_ary)
816
- res = res.to_ary
817
- if Fixnum === res.first
818
- if res.length == 3
819
- @response.status, headers, body = res
820
- @response.body = body if body
821
- headers.each { |k, v| @response.headers[k] = v } if headers
822
- elsif res.length == 2
823
- @response.status = res.first
824
- @response.body = res.last
825
- else
826
- raise TypeError, "#{res.inspect} not supported"
827
- end
828
- else
829
- @response.body = res
830
- end
831
- when res.respond_to?(:each)
832
- @response.body = res
833
- when (100..599) === res
834
- @response.status = res
730
+ def invoke
731
+ res = catch(:halt) { yield }
732
+ res = [res] if Fixnum === res or String === res
733
+ if Array === res and Fixnum === res.first
734
+ status(res.shift)
735
+ body(res.pop)
736
+ headers(*res)
737
+ elsif res.respond_to? :each
738
+ body res
835
739
  end
836
-
837
- res
838
740
  end
839
741
 
840
742
  # Dispatch a request with error handling.
@@ -842,60 +744,45 @@ module Sinatra
842
744
  static! if settings.static? && (request.get? || request.head?)
843
745
  filter! :before
844
746
  route!
845
- rescue NotFound => boom
846
- handle_not_found!(boom)
847
747
  rescue ::Exception => boom
848
748
  handle_exception!(boom)
849
749
  ensure
850
750
  filter! :after unless env['sinatra.static_file']
851
751
  end
852
752
 
853
- # Special treatment for 404s in order to play nice with cascades.
854
- def handle_not_found!(boom)
855
- @env['sinatra.error'] = boom
856
- @response.status = 404
857
- @response.headers['X-Cascade'] = 'pass'
858
- @response.body = ['<h1>Not Found</h1>']
859
- error_block! boom.class, NotFound
860
- end
861
-
862
753
  # Error handling during requests.
863
754
  def handle_exception!(boom)
864
755
  @env['sinatra.error'] = boom
865
-
866
756
  dump_errors!(boom) if settings.dump_errors?
867
757
  raise boom if settings.show_exceptions? and settings.show_exceptions != :after_handler
758
+ @response.status = boom.respond_to?(:code) ? Integer(boom.code) : 500
759
+
760
+ if @response.status == 404
761
+ @response.headers['X-Cascade'] = 'pass'
762
+ @response.body = ['<h1>Not Found</h1>']
763
+ end
868
764
 
869
- @response.status = 500
870
765
  if res = error_block!(boom.class)
871
766
  res
872
- elsif settings.raise_errors?
873
- raise boom
874
- else
875
- error_block!(Exception)
767
+ elsif @response.status >= 500
768
+ raise boom if settings.raise_errors? or settings.show_exceptions?
769
+ error_block! Exception
876
770
  end
877
771
  end
878
772
 
879
773
  # Find an custom error block for the key(s) specified.
880
- def error_block!(*keys)
881
- keys.each do |key|
882
- base = settings
883
- while base.respond_to?(:errors)
884
- if block = base.errors[key]
885
- # found a handler, eval and return result
886
- return instance_eval(&block)
887
- else
888
- base = base.superclass
889
- end
890
- end
774
+ def error_block!(key)
775
+ base = settings
776
+ while base.respond_to?(:errors)
777
+ next base = base.superclass unless args = base.errors[key]
778
+ return process_route(*args)
891
779
  end
892
- raise boom if settings.show_exceptions? and keys == Exception
893
- nil
780
+ return false unless key.respond_to? :superclass and key.superclass < Exception
781
+ error_block! key.superclass
894
782
  end
895
783
 
896
784
  def dump_errors!(boom)
897
- msg = ["#{boom.class} - #{boom.message}:",
898
- *boom.backtrace].join("\n ")
785
+ msg = ["#{boom.class} - #{boom.message}:", *boom.backtrace].join("\n\t")
899
786
  @env['rack.errors'].puts(msg)
900
787
  end
901
788
 
@@ -940,20 +827,43 @@ module Sinatra
940
827
 
941
828
  # Sets an option to the given value. If the value is a proc,
942
829
  # the proc will be called every time the option is accessed.
943
- def set(option, value = (not_set = true), &block)
830
+ def set(option, value = (not_set = true), ignore_setter = false, &block)
944
831
  raise ArgumentError if block and !not_set
945
- value = block if block
946
- if value.kind_of?(Proc)
947
- metadef(option, &value)
948
- metadef("#{option}?") { !!__send__(option) }
949
- metadef("#{option}=") { |val| metadef(option, &Proc.new{val}) }
950
- elsif not_set
832
+ value, not_set = block, false if block
833
+
834
+ if not_set
951
835
  raise ArgumentError unless option.respond_to?(:each)
952
836
  option.each { |k,v| set(k, v) }
953
- elsif respond_to?("#{option}=")
954
- __send__ "#{option}=", value
955
- else
956
- set option, Proc.new{value}
837
+ return self
838
+ end
839
+
840
+ if respond_to?("#{option}=") and not ignore_setter
841
+ return __send__("#{option}=", value)
842
+ end
843
+
844
+ setter = proc { |val| set option, val, true }
845
+ getter = proc { value }
846
+
847
+ case value
848
+ when Proc
849
+ getter = value
850
+ when Symbol, Fixnum, FalseClass, TrueClass, NilClass
851
+ # we have a lot of enable and disable calls, let's optimize those
852
+ class_eval "def self.#{option}() #{value.inspect} end"
853
+ getter = nil
854
+ when Hash
855
+ setter = proc do |val|
856
+ val = value.merge val if Hash === val
857
+ set option, val, true
858
+ end
859
+ end
860
+
861
+ (class << self; self; end).class_eval do
862
+ define_method("#{option}=", &setter) if setter
863
+ define_method(option, &getter) if getter
864
+ unless method_defined? "#{option}?"
865
+ class_eval "def #{option}?() !!#{option} end"
866
+ end
957
867
  end
958
868
  self
959
869
  end
@@ -971,8 +881,11 @@ module Sinatra
971
881
  # Define a custom error handler. Optionally takes either an Exception
972
882
  # class, or an HTTP status code to specify which errors should be
973
883
  # handled.
974
- def error(codes=Exception, &block)
975
- Array(codes).each { |code| @errors[code] = block }
884
+ def error(*codes, &block)
885
+ args = compile! "ERROR", //, block
886
+ codes = codes.map { |c| Array(c) }.flatten
887
+ codes << Exception if codes.empty?
888
+ codes.each { |c| @errors[c] = args }
976
889
  end
977
890
 
978
891
  # Sugar for `error(404) { ... }`
@@ -1056,18 +969,14 @@ module Sinatra
1056
969
 
1057
970
  # add a filter
1058
971
  def add_filter(type, path = nil, options = {}, &block)
1059
- return filters[type] << block unless path
1060
972
  path, options = //, path if path.respond_to?(:each_pair)
1061
- block, *arguments = compile!(type, path, block, options)
1062
- add_filter(type) do
1063
- process_route(*arguments) { instance_eval(&block) }
1064
- end
973
+ filters[type] << compile!(type, path || //, block, options)
1065
974
  end
1066
975
 
1067
976
  # Add a route condition. The route is considered non-matching when the
1068
977
  # block returns false.
1069
- def condition(&block)
1070
- @conditions << block
978
+ def condition(name = "#{caller.first[/`.*'/]} condition", &block)
979
+ @conditions << generate_method(name, &block)
1071
980
  end
1072
981
 
1073
982
  private
@@ -1127,51 +1036,47 @@ module Sinatra
1127
1036
  # Because of self.options.host
1128
1037
  host_name(options.delete(:host)) if options.key?(:host)
1129
1038
  enable :empty_path_info if path == "" and empty_path_info.nil?
1130
-
1131
- block, pattern, keys, conditions = compile! verb, path, block, options
1039
+ (@routes[verb] ||= []) << compile!(verb, path, block, options)
1132
1040
  invoke_hook(:route_added, verb, path, block)
1133
-
1134
- (@routes[verb] ||= []).
1135
- push([pattern, keys, conditions, block]).last
1136
1041
  end
1137
1042
 
1138
1043
  def invoke_hook(name, *args)
1139
1044
  extensions.each { |e| e.send(name, *args) if e.respond_to?(name) }
1140
1045
  end
1141
1046
 
1047
+ def generate_method(method_name, &block)
1048
+ define_method(method_name, &block)
1049
+ method = instance_method method_name
1050
+ remove_method method_name
1051
+ method
1052
+ end
1053
+
1142
1054
  def compile!(verb, path, block, options = {})
1143
1055
  options.each_pair { |option, args| send(option, *args) }
1144
- method_name = "#{verb} #{path}"
1145
-
1146
- define_method(method_name, &block)
1147
- unbound_method = instance_method method_name
1148
- pattern, keys = compile(path)
1056
+ method_name = "#{verb} #{path}"
1057
+ unbound_method = generate_method(method_name, &block)
1058
+ pattern, keys = compile path
1149
1059
  conditions, @conditions = @conditions, []
1150
- remove_method method_name
1151
1060
 
1152
- [ block.arity != 0 ?
1153
- proc { unbound_method.bind(self).call(*@block_params) } :
1154
- proc { unbound_method.bind(self).call },
1155
- pattern, keys, conditions ]
1061
+ [ pattern, keys, conditions, block.arity != 0 ?
1062
+ proc { |a,p| unbound_method.bind(a).call(*p) } :
1063
+ proc { |a,p| unbound_method.bind(a).call } ]
1156
1064
  end
1157
1065
 
1158
1066
  def compile(path)
1159
1067
  keys = []
1160
1068
  if path.respond_to? :to_str
1161
1069
  special_chars = %w{. + ( ) $}
1162
- pattern =
1163
- path.to_str.gsub(/((:\w+)|[\*#{special_chars.join}])/) do |match|
1164
- case match
1165
- when "*"
1166
- keys << 'splat'
1167
- "(.*?)"
1168
- when *special_chars
1169
- Regexp.escape(match)
1170
- else
1171
- keys << $2[1..-1]
1172
- "([^/?#]+)"
1173
- end
1070
+ pattern = path.to_str.gsub(/[^\?\%\\\/\:\*\w]/) { |c| encoded(c) }
1071
+ pattern.gsub! /((:\w+)|\*)/ do |match|
1072
+ if match == "*"
1073
+ keys << 'splat'
1074
+ "(.*?)"
1075
+ else
1076
+ keys << $2[1..-1]
1077
+ "([^/?#]+)"
1174
1078
  end
1079
+ end
1175
1080
  [/^#{pattern}$/, keys]
1176
1081
  elsif path.respond_to?(:keys) && path.respond_to?(:match)
1177
1082
  [path, path.keys]
@@ -1184,11 +1089,18 @@ module Sinatra
1184
1089
  end
1185
1090
  end
1186
1091
 
1092
+ def encoded(char)
1093
+ enc = URI.encode(char)
1094
+ enc = "(?:#{Regexp.escape enc}|#{URI.encode char, /./})" if enc == char
1095
+ enc = "(?:#{enc}|#{encoded('+')})" if char == " "
1096
+ enc
1097
+ end
1098
+
1187
1099
  public
1188
1100
  # Makes the methods defined in the block and in the Modules given
1189
1101
  # in `extensions` available to the handlers and templates
1190
1102
  def helpers(*extensions, &block)
1191
- class_eval(&block) if block_given?
1103
+ class_eval(&block) if block_given?
1192
1104
  include(*extensions) if extensions.any?
1193
1105
  end
1194
1106
 
@@ -1222,7 +1134,7 @@ module Sinatra
1222
1134
  def quit!(server, handler_name)
1223
1135
  # Use Thin's hard #stop! if available, otherwise just #stop.
1224
1136
  server.respond_to?(:stop!) ? server.stop! : server.stop
1225
- puts "\n== Sinatra has ended his set (crowd applauds)" unless handler_name =~/cgi/i
1137
+ STDERR.puts "\n== Sinatra has ended his set (crowd applauds)" unless handler_name =~/cgi/i
1226
1138
  end
1227
1139
 
1228
1140
  # Run the Sinatra app as a self-hosted server using
@@ -1231,14 +1143,14 @@ module Sinatra
1231
1143
  set options
1232
1144
  handler = detect_rack_handler
1233
1145
  handler_name = handler.name.gsub(/.*::/, '')
1234
- puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " +
1146
+ STDERR.puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " +
1235
1147
  "on #{port} for #{environment} with backup from #{handler_name}" unless handler_name =~/cgi/i
1236
1148
  handler.run self, :Host => bind, :Port => port do |server|
1237
1149
  [:INT, :TERM].each { |sig| trap(sig) { quit!(server, handler_name) } }
1238
1150
  set :running, true
1239
1151
  end
1240
1152
  rescue Errno::EADDRINUSE => e
1241
- puts "== Someone is already performing on port #{port}!"
1153
+ STDERR.puts "== Someone is already performing on port #{port}!"
1242
1154
  end
1243
1155
 
1244
1156
  # The prototype instance used to process requests.
@@ -1253,19 +1165,14 @@ module Sinatra
1253
1165
  # pipeline. The object is guaranteed to respond to #call but may not be
1254
1166
  # an instance of the class new was called on.
1255
1167
  def new(*args, &bk)
1256
- build(*args, &bk).to_app
1168
+ build(Rack::Builder.new, *args, &bk).to_app
1257
1169
  end
1258
1170
 
1259
1171
  # Creates a Rack::Builder instance with all the middleware set up and
1260
1172
  # an instance of this class as end point.
1261
- def build(*args, &bk)
1262
- builder = Rack::Builder.new
1263
- builder.use Rack::MethodOverride if method_override?
1264
- builder.use ShowExceptions if show_exceptions?
1265
- builder.use Rack::Head
1266
- setup_logging builder
1267
- setup_sessions builder
1268
- middleware.each { |c,a,b| builder.use(c, *a, &b) }
1173
+ def build(builder, *args, &bk)
1174
+ setup_default_middleware builder
1175
+ setup_middleware builder
1269
1176
  builder.run new!(*args, &bk)
1270
1177
  builder
1271
1178
  end
@@ -1275,6 +1182,18 @@ module Sinatra
1275
1182
  end
1276
1183
 
1277
1184
  private
1185
+ def setup_default_middleware(builder)
1186
+ builder.use ShowExceptions if show_exceptions?
1187
+ builder.use Rack::MethodOverride if method_override?
1188
+ builder.use Rack::Head
1189
+ setup_logging builder
1190
+ setup_sessions builder
1191
+ end
1192
+
1193
+ def setup_middleware(builder)
1194
+ middleware.each { |c,a,b| builder.use(c, *a, &b) }
1195
+ end
1196
+
1278
1197
  def setup_logging(builder)
1279
1198
  if logging?
1280
1199
  builder.use Rack::CommonLogger
@@ -1300,7 +1219,7 @@ module Sinatra
1300
1219
  servers = Array(server)
1301
1220
  servers.each do |server_name|
1302
1221
  begin
1303
- return Rack::Handler.get(server_name.downcase)
1222
+ return Rack::Handler.get(server_name)
1304
1223
  rescue LoadError
1305
1224
  rescue NameError
1306
1225
  end
@@ -1310,6 +1229,7 @@ module Sinatra
1310
1229
 
1311
1230
  def inherited(subclass)
1312
1231
  subclass.reset!
1232
+ subclass.set :app_file, caller_files.first unless subclass.app_file?
1313
1233
  super
1314
1234
  end
1315
1235
 
@@ -1322,16 +1242,11 @@ module Sinatra
1322
1242
  end
1323
1243
  end
1324
1244
 
1325
- def metadef(message, &block)
1326
- (class << self; self; end).
1327
- send :define_method, message, &block
1328
- end
1329
-
1330
1245
  public
1331
1246
  CALLERS_TO_IGNORE = [ # :nodoc:
1332
1247
  /\/sinatra(\/(base|main|showexceptions))?\.rb$/, # all sinatra code
1333
1248
  /lib\/tilt.*\.rb$/, # all tilt code
1334
- /\(.*\)/, # generated code
1249
+ /^\(.*\)$/, # generated code
1335
1250
  /rubygems\/custom_require\.rb$/, # rubygems require hacks
1336
1251
  /active_support/, # active_support require hacks
1337
1252
  /bundler(\/runtime)?\.rb/, # bundler require hacks
@@ -1368,7 +1283,7 @@ module Sinatra
1368
1283
  def self.force_encoding(data, encoding = default_encoding)
1369
1284
  return if data == settings || data.is_a?(Tempfile)
1370
1285
  if data.respond_to? :force_encoding
1371
- data.force_encoding encoding
1286
+ data.force_encoding(encoding).encode!
1372
1287
  elsif data.respond_to? :each_value
1373
1288
  data.each_value { |v| force_encoding(v, encoding) }
1374
1289
  elsif data.respond_to? :each
@@ -1390,10 +1305,17 @@ module Sinatra
1390
1305
  set :logging, false
1391
1306
  set :method_override, false
1392
1307
  set :default_encoding, "utf-8"
1393
- set :add_charset, [/^text\//, 'application/javascript', 'application/xml', 'application/xhtml+xml']
1308
+ set :add_charset, %w[javascript xml xhtml+xml json].map { |t| "application/#{t}" }
1309
+ settings.add_charset << /^text\//
1394
1310
 
1395
- # explicitly generating this eagerly to play nice with preforking
1396
- set :session_secret, '%x' % rand(2**255)
1311
+ # explicitly generating a session secret eagerly to play nice with preforking
1312
+ begin
1313
+ require 'securerandom'
1314
+ set :session_secret, SecureRandom.hex(64)
1315
+ rescue LoadError, NotImplementedError
1316
+ # SecureRandom raises a NotImplementedError if no random device is available
1317
+ set :session_secret, "%064x" % Kernel.rand(2**256-1)
1318
+ end
1397
1319
 
1398
1320
  class << self
1399
1321
  alias_method :methodoverride?, :method_override?
@@ -1418,6 +1340,7 @@ module Sinatra
1418
1340
 
1419
1341
  set :public, Proc.new { root && File.join(root, 'public') }
1420
1342
  set :static, Proc.new { public && File.exist?(public) }
1343
+ set :static_cache_control, false
1421
1344
 
1422
1345
  error ::Exception do
1423
1346
  response.status = 500
@@ -1464,13 +1387,14 @@ module Sinatra
1464
1387
  #
1465
1388
  # The Application class should not be subclassed, unless you want to
1466
1389
  # inherit all settings, routes, handlers, and error pages from the
1467
- # top-level. Subclassing Sinatra::Base is heavily recommended for
1390
+ # top-level. Subclassing Sinatra::Base is highly recommended for
1468
1391
  # modular applications.
1469
1392
  class Application < Base
1470
1393
  set :logging, Proc.new { ! test? }
1471
1394
  set :method_override, true
1472
1395
  set :run, Proc.new { ! test? }
1473
1396
  set :session_secret, Proc.new { super() unless development? }
1397
+ set :app_file, nil
1474
1398
 
1475
1399
  def self.register(*extensions, &block) #:nodoc:
1476
1400
  added_methods = extensions.map {|m| m.public_instance_methods }.flatten