rtomayko-sinatra 0.9.0 → 0.9.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Rakefile CHANGED
@@ -57,16 +57,17 @@ file package('.gem') => %w[dist/ sinatra.gemspec] + spec.files do |f|
57
57
  end
58
58
 
59
59
  file package('.tar.gz') => %w[dist/] + spec.files do |f|
60
- sh "git archive --format=tar HEAD | gzip > #{f.name}"
60
+ sh <<-SH
61
+ git archive \
62
+ --prefix=sinatra-#{source_version}/ \
63
+ --format=tar \
64
+ HEAD | gzip > #{f.name}
65
+ SH
61
66
  end
62
67
 
63
68
  # Rubyforge Release / Publish Tasks ==================================
64
69
 
65
- desc 'Publish website to rubyforge'
66
- task 'publish:doc' => 'doc/api/index.html' do
67
- sh 'scp -rp doc/* rubyforge.org:/var/www/gforge-projects/sinatra/'
68
- end
69
-
70
+ desc 'Publish gem and tarball to rubyforge'
70
71
  task 'publish:gem' => [package('.gem'), package('.tar.gz')] do |t|
71
72
  sh <<-end
72
73
  rubyforge add_release sinatra sinatra #{spec.version} #{package('.gem')} &&
@@ -78,7 +79,7 @@ end
78
79
  # Building docs requires HAML and the hanna gem:
79
80
  # gem install mislav-hanna --source=http://gems.github.com
80
81
 
81
- task 'doc' => ['doc:api','doc:site']
82
+ task 'doc' => ['doc:api']
82
83
 
83
84
  desc 'Generate Hanna RDoc under doc/api'
84
85
  task 'doc:api' => ['doc/api/index.html']
@@ -104,47 +105,6 @@ def rdoc_to_html(file_name)
104
105
  rdoc.convert(File.read(file_name))
105
106
  end
106
107
 
107
- def haml(locals={})
108
- require 'haml'
109
- template = File.read('doc/template.haml')
110
- haml = Haml::Engine.new(template, :format => :html4, :attr_wrapper => '"')
111
- haml.render(Object.new, locals)
112
- end
113
-
114
- desc 'Build website HTML and stuff'
115
- task 'doc:site' => ['doc/index.html', 'doc/book.html']
116
-
117
- file 'doc/index.html' => %w[README.rdoc doc/template.haml] do |file|
118
- File.open(file.name, 'w') do |file|
119
- file << haml(:title => 'Sinatra', :content => rdoc_to_html('README.rdoc'))
120
- end
121
- end
122
- CLEAN.include 'doc/index.html'
123
-
124
- file 'doc/book.html' => ['book/output/sinatra-book.html'] do |file|
125
- File.open(file.name, 'w') do |file|
126
- book_content = File.read('book/output/sinatra-book.html')
127
- file << haml(:title => 'Sinatra Book', :content => book_content)
128
- end
129
- end
130
- CLEAN.include 'doc/book.html'
131
-
132
- file 'book/output/sinatra-book.html' => FileList['book/**'] do |f|
133
- unless File.directory?('book')
134
- sh 'git clone git://github.com/cschneid/sinatra-book.git book'
135
- end
136
- sh((<<-SH).strip.gsub(/\s+/, ' '))
137
- cd book &&
138
- git fetch origin &&
139
- git rebase origin/master &&
140
- thor book:build
141
- SH
142
- end
143
- CLEAN.include 'book/output/sinatra-book.html'
144
-
145
- desc 'Build the Sinatra book'
146
- task 'doc:book' => ['book/output/sinatra-book.html']
147
-
148
108
  # Gemspec Helpers ====================================================
149
109
 
150
110
  def source_version
@@ -152,7 +112,12 @@ def source_version
152
112
  line.match(/.*VERSION = '(.*)'/)[1]
153
113
  end
154
114
 
155
- file 'sinatra.gemspec' => FileList['{lib,test,images}/**','Rakefile'] do |f|
115
+ project_files =
116
+ FileList[
117
+ '{lib,test,compat,images}/**',
118
+ 'Rakefile', 'CHANGES', 'README.rdoc'
119
+ ]
120
+ file 'sinatra.gemspec' => project_files do |f|
156
121
  # read spec file and split out manifest section
157
122
  spec = File.read(f.name)
158
123
  head, manifest, tail = spec.split(" # = MANIFEST =\n")
@@ -1,7 +1,6 @@
1
1
  require File.dirname(__FILE__) + '/helper'
2
2
 
3
3
  context "Simple Events" do
4
-
5
4
  def simple_request_hash(method, path)
6
5
  Rack::Request.new({
7
6
  'REQUEST_METHOD' => method.to_s.upcase,
@@ -14,16 +13,16 @@ context "Simple Events" do
14
13
 
15
14
  def invoke_simple(path, request_path, &b)
16
15
  params = nil
17
- mock_app {
18
- get path do
19
- params = self.params
20
- b.call if b
21
- end
22
- }
16
+ get path do
17
+ params = self.params
18
+ b.call if b
19
+ end
23
20
  get_it request_path
24
21
  MockResult.new(b, params)
25
22
  end
26
23
 
24
+ setup { Sinatra.application = nil }
25
+
27
26
  specify "return last value" do
28
27
  block = Proc.new { 'Simple' }
29
28
  result = invoke_simple('/', '/', &block)
@@ -38,6 +37,7 @@ context "Simple Events" do
38
37
  result.params.should.equal "foo" => 'a', "bar" => 'b'
39
38
 
40
39
  # unscapes
40
+ Sinatra.application = nil
41
41
  result = invoke_simple('/:foo/:bar', '/a/blake%20mizerany')
42
42
  result.should.not.be.nil
43
43
  result.params.should.equal "foo" => 'a', "bar" => 'blake mizerany'
@@ -48,14 +48,17 @@ context "Simple Events" do
48
48
  result.should.not.be.nil
49
49
  result.params.should.equal "foo" => 'a', "bar" => 'b'
50
50
 
51
+ Sinatra.application = nil
51
52
  result = invoke_simple('/?:foo?/?:bar?', '/a/')
52
53
  result.should.not.be.nil
53
54
  result.params.should.equal "foo" => 'a', "bar" => nil
54
55
 
56
+ Sinatra.application = nil
55
57
  result = invoke_simple('/?:foo?/?:bar?', '/a')
56
58
  result.should.not.be.nil
57
59
  result.params.should.equal "foo" => 'a', "bar" => nil
58
60
 
61
+ Sinatra.application = nil
59
62
  result = invoke_simple('/:foo?/?:bar?', '/')
60
63
  result.should.not.be.nil
61
64
  result.params.should.equal "foo" => nil, "bar" => nil
data/compat/helper.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  require 'rubygems'
2
2
  require 'mocha'
3
3
 
4
+ # disable warnings in compat specs.
5
+ $VERBOSE = nil
6
+
4
7
  $:.unshift File.dirname(File.dirname(__FILE__)) + "/lib"
5
8
 
6
9
  ENV['RACK_ENV'] ||= 'test'
@@ -10,9 +13,18 @@ require 'sinatra/test'
10
13
  require 'sinatra/test/unit'
11
14
  require 'sinatra/test/spec'
12
15
 
16
+ module Sinatra::Test
17
+ # we need to remove the new test helper methods since they conflict with
18
+ # the top-level methods of the same name.
19
+ %w(get head post put delete).each do |verb|
20
+ remove_method verb
21
+ end
22
+ include Sinatra::Delegator
23
+ end
24
+
13
25
  class Test::Unit::TestCase
26
+ include Sinatra::Test
14
27
  def setup
15
28
  @app = lambda { |env| Sinatra::Application.call(env) }
16
29
  end
17
- include Sinatra::Test
18
30
  end
data/lib/sinatra.rb CHANGED
@@ -1,3 +1,8 @@
1
+ libdir = File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
3
+
1
4
  require 'sinatra/base'
2
5
  require 'sinatra/main'
3
6
  require 'sinatra/compat'
7
+
8
+ use_in_file_templates!
data/lib/sinatra/base.rb CHANGED
@@ -4,12 +4,23 @@ require 'rack'
4
4
  require 'rack/builder'
5
5
 
6
6
  module Sinatra
7
- VERSION = '0.9.0'
7
+ VERSION = '0.9.0.2'
8
8
 
9
9
  class Request < Rack::Request
10
10
  def user_agent
11
11
  @env['HTTP_USER_AGENT']
12
12
  end
13
+
14
+ def accept
15
+ @env['HTTP_ACCEPT'].split(',').map { |a| a.strip }
16
+ end
17
+
18
+ # Override Rack 0.9.x's #params implementation (see #72 in lighthouse)
19
+ def params
20
+ self.GET.update(self.POST)
21
+ rescue EOFError => boom
22
+ self.GET
23
+ end
13
24
  end
14
25
 
15
26
  class Response < Rack::Response
@@ -66,7 +77,7 @@ module Sinatra
66
77
  def redirect(uri, *args)
67
78
  status 302
68
79
  response['Location'] = uri
69
- halt *args
80
+ halt(*args)
70
81
  end
71
82
 
72
83
  # Halt processing and return the error status provided.
@@ -88,9 +99,7 @@ module Sinatra
88
99
 
89
100
  # Look up a media type by file extension in Rack's mime registry.
90
101
  def media_type(type)
91
- return type if type.nil? || type.to_s.include?('/')
92
- type = ".#{type}" unless type.to_s[0] == ?.
93
- Rack::Mime.mime_type(type, nil)
102
+ Base.media_type(type)
94
103
  end
95
104
 
96
105
  # Set the Content-Type of the response body given a media type or file
@@ -133,7 +142,7 @@ module Sinatra
133
142
  class StaticFile < ::File #:nodoc:
134
143
  alias_method :to_path, :path
135
144
  def each
136
- while buf = read(8196)
145
+ while buf = read(8192)
137
146
  yield buf
138
147
  end
139
148
  end
@@ -208,6 +217,7 @@ module Sinatra
208
217
 
209
218
  def lookup_layout(engine, options)
210
219
  return if options[:layout] == false
220
+ options.delete(:layout) if options[:layout] == true
211
221
  template = options[:layout] || :layout
212
222
  data = lookup_template(engine, template, options)
213
223
  [template, data]
@@ -296,10 +306,10 @@ module Sinatra
296
306
  attr_accessor :env, :request, :response, :params
297
307
 
298
308
  def call!(env)
299
- @env = env
300
- @request = Request.new(env)
309
+ @env = env
310
+ @request = Request.new(env)
301
311
  @response = Response.new
302
- @params = nil
312
+ @params = nil
303
313
  error_detection { dispatch! }
304
314
  @response.finish
305
315
  end
@@ -318,13 +328,16 @@ module Sinatra
318
328
 
319
329
  private
320
330
  def dispatch!
321
- self.class.filters.each {|block| instance_eval(&block)}
331
+ self.class.filters.each do |block|
332
+ res = catch(:halt) { instance_eval(&block) ; :continue }
333
+ return unless res == :continue
334
+ end
335
+
322
336
  if routes = self.class.routes[@request.request_method]
323
337
  path = @request.path_info
324
- original_params = Hash.new{ |hash,k| hash[k.to_s] if Symbol === k }
325
- original_params.merge! @request.params
338
+ original_params = nested_params(@request.params)
326
339
 
327
- routes.each do |pattern, keys, conditions, block|
340
+ routes.each do |pattern, keys, conditions, method_name|
328
341
  if pattern =~ path
329
342
  values = $~.captures.map{|val| val && unescape(val) }
330
343
  params =
@@ -342,13 +355,12 @@ module Sinatra
342
355
  else
343
356
  {}
344
357
  end
345
- @params = original_params.dup
346
- @params.merge!(params)
358
+ @params = original_params.merge(params)
347
359
 
348
360
  catch(:pass) {
349
361
  conditions.each { |cond|
350
362
  throw :pass if instance_eval(&cond) == false }
351
- return invoke(block)
363
+ return invoke(method_name)
352
364
  }
353
365
  end
354
366
  end
@@ -356,8 +368,26 @@ module Sinatra
356
368
  raise NotFound
357
369
  end
358
370
 
371
+ def nested_params(params)
372
+ return indifferent_hash.merge(params) if !params.keys.join.include?('[')
373
+ params.inject indifferent_hash do |res, (key,val)|
374
+ if key =~ /\[.*\]/
375
+ splat = key.scan(/(^[^\[]+)|\[([^\]]+)\]/).flatten.compact
376
+ head, last = splat[0..-2], splat[-1]
377
+ head.inject(res){ |s,v| s[v] ||= indifferent_hash }[last] = val
378
+ end
379
+ res
380
+ end
381
+ end
382
+
383
+ def indifferent_hash
384
+ Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
385
+ end
386
+
359
387
  def invoke(block)
360
388
  res = catch(:halt) { instance_eval(&block) }
389
+ return if res.nil?
390
+
361
391
  case
362
392
  when res.respond_to?(:to_str)
363
393
  @response.body = [res]
@@ -377,17 +407,12 @@ module Sinatra
377
407
  else
378
408
  @response.body = res
379
409
  end
380
- when res.kind_of?(Symbol) # TODO: deprecate this.
381
- @response.body = __send__(res)
382
410
  when res.respond_to?(:each)
383
411
  @response.body = res
384
412
  when (100...599) === res
385
413
  @response.status = res
386
- when res.nil?
387
- @response.body = []
388
- else
389
- raise TypeError, "#{res.inspect} not supported"
390
414
  end
415
+
391
416
  res
392
417
  end
393
418
 
@@ -402,6 +427,12 @@ module Sinatra
402
427
  invoke handler unless handler.nil?
403
428
  rescue ::Exception => boom
404
429
  @env['sinatra.error'] = boom
430
+
431
+ if options.dump_errors?
432
+ msg = ["#{boom.class} - #{boom.message}:", *boom.backtrace].join("\n ")
433
+ @env['rack.errors'] << msg
434
+ end
435
+
405
436
  raise boom if options.raise_errors?
406
437
  @response.status = 500
407
438
  invoke errmap[boom.class] || errmap[Exception]
@@ -454,6 +485,10 @@ module Sinatra
454
485
  end
455
486
  end
456
487
 
488
+ def not_found(&block)
489
+ error 404, &block
490
+ end
491
+
457
492
  def template(name, &block)
458
493
  templates[name] = block
459
494
  end
@@ -463,11 +498,16 @@ module Sinatra
463
498
  end
464
499
 
465
500
  def use_in_file_templates!
466
- line = caller.detect { |s| s !~ /lib\/sinatra.*\.rb/ &&
467
- s !~ /\(.*\)/ }
501
+ line = caller.detect do |s|
502
+ [
503
+ /lib\/sinatra.*\.rb/,
504
+ /\(.*\)/,
505
+ /rubygems\/custom_require\.rb/
506
+ ].all? { |x| s !~ x }
507
+ end
468
508
  file = line.sub(/:\d+.*$/, '')
469
509
  if data = ::IO.read(file).split('__END__')[1]
470
- data.gsub! /\r\n/, "\n"
510
+ data.gsub!(/\r\n/, "\n")
471
511
  template = nil
472
512
  data.each_line do |line|
473
513
  if line =~ /^@@\s*(.*)/
@@ -479,6 +519,13 @@ module Sinatra
479
519
  end
480
520
  end
481
521
 
522
+ # Look up a media type by file extension in Rack's mime registry.
523
+ def media_type(type)
524
+ return type if type.nil? || type.to_s.include?('/')
525
+ type = ".#{type}" unless type.to_s[0] == ?.
526
+ Rack::Mime.mime_type(type, nil)
527
+ end
528
+
482
529
  def before(&block)
483
530
  @filters << block
484
531
  end
@@ -502,9 +549,24 @@ module Sinatra
502
549
  }
503
550
  end
504
551
 
552
+ def accept_mime_types(types)
553
+ types = [types] unless types.kind_of? Array
554
+ types.map!{|t| media_type(t)}
555
+
556
+ condition {
557
+ matching_types = (request.accept & types)
558
+ unless matching_types.empty?
559
+ response.headers['Content-Type'] = matching_types.first
560
+ true
561
+ else
562
+ false
563
+ end
564
+ }
565
+ end
566
+
505
567
  def get(path, opts={}, &block)
506
568
  conditions = @conditions.dup
507
- route 'GET', path, opts, &block
569
+ route('GET', path, opts, &block)
508
570
 
509
571
  @conditions = conditions
510
572
  head(path, opts) { invoke(block) ; [] }
@@ -516,14 +578,20 @@ module Sinatra
516
578
  def head(path, opts={}, &bk); route 'HEAD', path, opts, &bk; end
517
579
 
518
580
  private
519
- def route(method, path, opts={}, &block)
581
+ def route(verb, path, opts={}, &block)
520
582
  host_name opts[:host] if opts.key?(:host)
521
583
  user_agent opts[:agent] if opts.key?(:agent)
584
+ accept_mime_types opts[:provides] if opts.key?(:provides)
522
585
 
523
586
  pattern, keys = compile(path)
524
587
  conditions, @conditions = @conditions, []
525
- (routes[method] ||= []).
526
- push [pattern, keys, conditions, block]
588
+
589
+ define_method "#{verb} #{path}", &block
590
+ unbound_method = instance_method("#{verb} #{path}")
591
+ block = lambda { unbound_method.bind(self).call }
592
+
593
+ (routes[verb] ||= []).
594
+ push([pattern, keys, conditions, block]).last
527
595
  end
528
596
 
529
597
  def compile(path)
@@ -562,8 +630,8 @@ module Sinatra
562
630
  end
563
631
 
564
632
  def run!(options={})
565
- set(options)
566
- handler = Rack::Handler.get(server)
633
+ set options
634
+ handler = detect_rack_handler
567
635
  handler_name = handler.name.gsub(/.*::/, '')
568
636
  puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " +
569
637
  "on #{port} for #{environment} with backup from #{handler_name}"
@@ -584,6 +652,17 @@ module Sinatra
584
652
  end
585
653
 
586
654
  private
655
+ def detect_rack_handler
656
+ servers = Array(self.server)
657
+ servers.each do |server_name|
658
+ begin
659
+ return Rack::Handler.get(server_name)
660
+ rescue LoadError
661
+ end
662
+ end
663
+ fail "Server handler (#{servers.join(',')}) not found."
664
+ end
665
+
587
666
  def construct_middleware(builder=Rack::Builder.new)
588
667
  builder.use Rack::Session::Cookie if sessions?
589
668
  builder.use Rack::CommonLogger if logging?
@@ -622,6 +701,7 @@ module Sinatra
622
701
  end
623
702
 
624
703
  set :raise_errors, true
704
+ set :dump_errors, false
625
705
  set :sessions, false
626
706
  set :logging, false
627
707
  set :methodoverride, false
@@ -629,7 +709,7 @@ module Sinatra
629
709
  set :environment, (ENV['RACK_ENV'] || :development).to_sym
630
710
 
631
711
  set :run, false
632
- set :server, (defined?(Rack::Handler::Thin) ? "thin" : "mongrel")
712
+ set :server, %w[thin mongrel webrick]
633
713
  set :host, '0.0.0.0'
634
714
  set :port, 4567
635
715
 
@@ -715,34 +795,45 @@ module Sinatra
715
795
 
716
796
  class Default < Base
717
797
  set :raise_errors, false
798
+ set :dump_errors, true
718
799
  set :sessions, false
719
800
  set :logging, true
720
801
  set :methodoverride, true
721
802
  set :static, true
722
803
  set :run, false
723
804
  set :reload, Proc.new { app_file? && development? }
805
+ set :lock, Proc.new { reload? }
724
806
 
725
- @reloading = false
726
-
727
- class << self
728
- def reloading?
729
- @reloading
730
- end
807
+ def self.reloading?
808
+ @reloading ||= false
809
+ end
731
810
 
732
- def configure(*envs)
733
- super unless reloading?
734
- end
811
+ def self.configure(*envs)
812
+ super unless reloading?
813
+ end
735
814
 
736
- def call(env)
815
+ def self.call(env)
816
+ synchronize do
737
817
  reload! if reload?
738
818
  super
739
819
  end
820
+ end
821
+
822
+ def self.reload!
823
+ @reloading = true
824
+ superclass.send :inherited, self
825
+ $LOADED_FEATURES.delete("sinatra.rb")
826
+ ::Kernel.load app_file
827
+ @reloading = false
828
+ end
740
829
 
741
- def reload!
742
- @reloading = true
743
- superclass.send :inherited, self
744
- ::Kernel.load app_file
745
- @reloading = false
830
+ private
831
+ @@mutex = Mutex.new
832
+ def self.synchronize(&block)
833
+ if lock?
834
+ @@mutex.synchronize(&block)
835
+ else
836
+ yield
746
837
  end
747
838
  end
748
839
  end