devcenter 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. data/CONTRIBUTING.md +14 -0
  2. data/Gemfile +0 -4
  3. data/devcenter.gemspec +2 -1
  4. data/lib/devcenter.rb +0 -1
  5. data/lib/devcenter/previewer/web_app.rb +3 -2
  6. data/lib/devcenter/version.rb +1 -1
  7. metadata +19 -124
  8. data/vendor/sinatra/.gitignore +0 -6
  9. data/vendor/sinatra/.travis.yml +0 -16
  10. data/vendor/sinatra/.yardopts +0 -4
  11. data/vendor/sinatra/AUTHORS +0 -61
  12. data/vendor/sinatra/Gemfile +0 -91
  13. data/vendor/sinatra/LICENSE +0 -22
  14. data/vendor/sinatra/README.de.rdoc +0 -2116
  15. data/vendor/sinatra/README.es.rdoc +0 -2106
  16. data/vendor/sinatra/README.fr.rdoc +0 -2133
  17. data/vendor/sinatra/README.hu.rdoc +0 -608
  18. data/vendor/sinatra/README.jp.rdoc +0 -1056
  19. data/vendor/sinatra/README.ko.rdoc +0 -1932
  20. data/vendor/sinatra/README.pt-br.rdoc +0 -778
  21. data/vendor/sinatra/README.pt-pt.rdoc +0 -647
  22. data/vendor/sinatra/README.rdoc +0 -2049
  23. data/vendor/sinatra/README.ru.rdoc +0 -2033
  24. data/vendor/sinatra/README.zh.rdoc +0 -1816
  25. data/vendor/sinatra/Rakefile +0 -182
  26. data/vendor/sinatra/examples/chat.rb +0 -61
  27. data/vendor/sinatra/examples/simple.rb +0 -3
  28. data/vendor/sinatra/examples/stream.ru +0 -26
  29. data/vendor/sinatra/lib/sinatra.rb +0 -5
  30. data/vendor/sinatra/lib/sinatra/base.rb +0 -1820
  31. data/vendor/sinatra/lib/sinatra/images/404.png +0 -0
  32. data/vendor/sinatra/lib/sinatra/images/500.png +0 -0
  33. data/vendor/sinatra/lib/sinatra/main.rb +0 -30
  34. data/vendor/sinatra/lib/sinatra/showexceptions.rb +0 -345
  35. data/vendor/sinatra/lib/sinatra/version.rb +0 -3
  36. data/vendor/sinatra/sinatra.gemspec +0 -18
  37. data/vendor/sinatra/test/base_test.rb +0 -172
  38. data/vendor/sinatra/test/builder_test.rb +0 -91
  39. data/vendor/sinatra/test/coffee_test.rb +0 -90
  40. data/vendor/sinatra/test/compile_test.rb +0 -139
  41. data/vendor/sinatra/test/contest.rb +0 -98
  42. data/vendor/sinatra/test/creole_test.rb +0 -65
  43. data/vendor/sinatra/test/delegator_test.rb +0 -160
  44. data/vendor/sinatra/test/encoding_test.rb +0 -20
  45. data/vendor/sinatra/test/erb_test.rb +0 -98
  46. data/vendor/sinatra/test/extensions_test.rb +0 -98
  47. data/vendor/sinatra/test/filter_test.rb +0 -437
  48. data/vendor/sinatra/test/haml_test.rb +0 -91
  49. data/vendor/sinatra/test/helper.rb +0 -123
  50. data/vendor/sinatra/test/helpers_test.rb +0 -1768
  51. data/vendor/sinatra/test/integration/app.rb +0 -62
  52. data/vendor/sinatra/test/integration_helper.rb +0 -222
  53. data/vendor/sinatra/test/integration_test.rb +0 -87
  54. data/vendor/sinatra/test/less_test.rb +0 -69
  55. data/vendor/sinatra/test/liquid_test.rb +0 -59
  56. data/vendor/sinatra/test/mapped_error_test.rb +0 -305
  57. data/vendor/sinatra/test/markaby_test.rb +0 -80
  58. data/vendor/sinatra/test/markdown_test.rb +0 -82
  59. data/vendor/sinatra/test/middleware_test.rb +0 -68
  60. data/vendor/sinatra/test/nokogiri_test.rb +0 -67
  61. data/vendor/sinatra/test/public/favicon.ico +0 -0
  62. data/vendor/sinatra/test/rabl_test.rb +0 -89
  63. data/vendor/sinatra/test/rack_test.rb +0 -45
  64. data/vendor/sinatra/test/radius_test.rb +0 -59
  65. data/vendor/sinatra/test/rdoc_test.rb +0 -66
  66. data/vendor/sinatra/test/readme_test.rb +0 -120
  67. data/vendor/sinatra/test/request_test.rb +0 -45
  68. data/vendor/sinatra/test/response_test.rb +0 -64
  69. data/vendor/sinatra/test/result_test.rb +0 -76
  70. data/vendor/sinatra/test/route_added_hook_test.rb +0 -59
  71. data/vendor/sinatra/test/routing_test.rb +0 -1175
  72. data/vendor/sinatra/test/sass_test.rb +0 -116
  73. data/vendor/sinatra/test/scss_test.rb +0 -89
  74. data/vendor/sinatra/test/server_test.rb +0 -48
  75. data/vendor/sinatra/test/settings_test.rb +0 -561
  76. data/vendor/sinatra/test/sinatra_test.rb +0 -12
  77. data/vendor/sinatra/test/slim_test.rb +0 -84
  78. data/vendor/sinatra/test/static_test.rb +0 -219
  79. data/vendor/sinatra/test/streaming_test.rb +0 -149
  80. data/vendor/sinatra/test/templates_test.rb +0 -333
  81. data/vendor/sinatra/test/textile_test.rb +0 -65
  82. data/vendor/sinatra/test/views/a/in_a.str +0 -1
  83. data/vendor/sinatra/test/views/ascii.erb +0 -2
  84. data/vendor/sinatra/test/views/b/in_b.str +0 -1
  85. data/vendor/sinatra/test/views/calc.html.erb +0 -1
  86. data/vendor/sinatra/test/views/error.builder +0 -3
  87. data/vendor/sinatra/test/views/error.erb +0 -3
  88. data/vendor/sinatra/test/views/error.haml +0 -3
  89. data/vendor/sinatra/test/views/error.sass +0 -2
  90. data/vendor/sinatra/test/views/explicitly_nested.str +0 -1
  91. data/vendor/sinatra/test/views/foo/hello.test +0 -1
  92. data/vendor/sinatra/test/views/hello.builder +0 -1
  93. data/vendor/sinatra/test/views/hello.coffee +0 -1
  94. data/vendor/sinatra/test/views/hello.creole +0 -1
  95. data/vendor/sinatra/test/views/hello.erb +0 -1
  96. data/vendor/sinatra/test/views/hello.haml +0 -1
  97. data/vendor/sinatra/test/views/hello.less +0 -5
  98. data/vendor/sinatra/test/views/hello.liquid +0 -1
  99. data/vendor/sinatra/test/views/hello.mab +0 -1
  100. data/vendor/sinatra/test/views/hello.md +0 -1
  101. data/vendor/sinatra/test/views/hello.nokogiri +0 -1
  102. data/vendor/sinatra/test/views/hello.rabl +0 -2
  103. data/vendor/sinatra/test/views/hello.radius +0 -1
  104. data/vendor/sinatra/test/views/hello.rdoc +0 -1
  105. data/vendor/sinatra/test/views/hello.sass +0 -2
  106. data/vendor/sinatra/test/views/hello.scss +0 -3
  107. data/vendor/sinatra/test/views/hello.slim +0 -1
  108. data/vendor/sinatra/test/views/hello.str +0 -1
  109. data/vendor/sinatra/test/views/hello.test +0 -1
  110. data/vendor/sinatra/test/views/hello.textile +0 -1
  111. data/vendor/sinatra/test/views/hello.wlang +0 -1
  112. data/vendor/sinatra/test/views/hello.yajl +0 -1
  113. data/vendor/sinatra/test/views/layout2.builder +0 -3
  114. data/vendor/sinatra/test/views/layout2.erb +0 -2
  115. data/vendor/sinatra/test/views/layout2.haml +0 -2
  116. data/vendor/sinatra/test/views/layout2.liquid +0 -2
  117. data/vendor/sinatra/test/views/layout2.mab +0 -2
  118. data/vendor/sinatra/test/views/layout2.nokogiri +0 -3
  119. data/vendor/sinatra/test/views/layout2.rabl +0 -3
  120. data/vendor/sinatra/test/views/layout2.radius +0 -2
  121. data/vendor/sinatra/test/views/layout2.slim +0 -3
  122. data/vendor/sinatra/test/views/layout2.str +0 -2
  123. data/vendor/sinatra/test/views/layout2.test +0 -1
  124. data/vendor/sinatra/test/views/layout2.wlang +0 -2
  125. data/vendor/sinatra/test/views/nested.str +0 -1
  126. data/vendor/sinatra/test/views/utf8.erb +0 -2
  127. data/vendor/sinatra/test/wlang_test.rb +0 -70
  128. data/vendor/sinatra/test/yajl_test.rb +0 -86
@@ -1,182 +0,0 @@
1
- require 'rake/clean'
2
- require 'rake/testtask'
3
- require 'fileutils'
4
- require 'date'
5
-
6
- # CI Reporter is only needed for the CI
7
- begin
8
- require 'ci/reporter/rake/test_unit'
9
- rescue LoadError
10
- end
11
-
12
- task :default => :test
13
- task :spec => :test
14
-
15
- CLEAN.include "**/*.rbc"
16
-
17
- def source_version
18
- @source_version ||= begin
19
- load './lib/sinatra/version.rb'
20
- Sinatra::VERSION
21
- end
22
- end
23
-
24
- def prev_feature
25
- source_version.gsub(/^(\d\.)(\d+)\..*$/) { $1 + ($2.to_i - 1).to_s }
26
- end
27
-
28
- def prev_version
29
- return prev_feature + '.0' if source_version.end_with? '.0'
30
- source_version.gsub(/\d+$/) { |s| s.to_i - 1 }
31
- end
32
-
33
- # SPECS ===============================================================
34
-
35
- task :test do
36
- ENV['LANG'] = 'C'
37
- ENV.delete 'LC_CTYPE'
38
- end
39
-
40
- Rake::TestTask.new(:test) do |t|
41
- t.test_files = FileList['test/*_test.rb']
42
- t.ruby_opts = ['-rubygems'] if defined? Gem
43
- t.ruby_opts << '-I.'
44
- end
45
-
46
- Rake::TestTask.new(:"test:core") do |t|
47
- core_tests = %w[base delegator encoding extensions filter
48
- helpers mapped_error middleware radius rdoc
49
- readme request response result route_added_hook
50
- routing server settings sinatra static templates]
51
- t.test_files = core_tests.map {|n| "test/#{n}_test.rb"}
52
- t.ruby_opts = ["-rubygems"] if defined? Gem
53
- t.ruby_opts << "-I."
54
- end
55
-
56
- # Rcov ================================================================
57
-
58
- namespace :test do
59
- desc 'Measures test coverage'
60
- task :coverage do
61
- rm_f "coverage"
62
- sh "rcov -Ilib test/*_test.rb"
63
- end
64
- end
65
-
66
- # Website =============================================================
67
-
68
- desc 'Generate RDoc under doc/api'
69
- task 'doc' => ['doc:api']
70
- task('doc:api') { sh "yardoc -o doc/api" }
71
- CLEAN.include 'doc/api'
72
-
73
- # README ===============================================================
74
-
75
- task :add_template, [:name] do |t, args|
76
- Dir.glob('README.*') do |file|
77
- code = File.read(file)
78
- if code =~ /^===.*#{args.name.capitalize}/
79
- puts "Already covered in #{file}"
80
- else
81
- template = code[/===[^\n]*Liquid.*index\.liquid<\/tt>[^\n]*/m]
82
- if !template
83
- puts "Liquid not found in #{file}"
84
- else
85
- puts "Adding section to #{file}"
86
- template = template.gsub(/Liquid/, args.name.capitalize).gsub(/liquid/, args.name.downcase)
87
- code.gsub! /^(\s*===.*CoffeeScript)/, "\n" << template << "\n\\1"
88
- File.open(file, "w") { |f| f << code }
89
- end
90
- end
91
- end
92
- end
93
-
94
- # Thanks in announcement ===============================================
95
-
96
- team = ["Ryan Tomayko", "Blake Mizerany", "Simon Rozet", "Konstantin Haase"]
97
- desc "list of contributors"
98
- task :thanks, [:release,:backports] do |t, a|
99
- a.with_defaults :release => "#{prev_version}..HEAD",
100
- :backports => "#{prev_feature}.0..#{prev_feature}.x"
101
- included = `git log --format=format:"%aN\t%s" #{a.release}`.lines.to_a
102
- excluded = `git log --format=format:"%aN\t%s" #{a.backports}`.lines.to_a
103
- commits = (included - excluded).group_by { |c| c[/^[^\t]+/] }
104
- authors = commits.keys.sort_by { |n| - commits[n].size } - team
105
- puts authors[0..-2].join(', ') << " and " << authors.last,
106
- "(based on commits included in #{a.release}, but not in #{a.backports})"
107
- end
108
-
109
- desc "list of authors"
110
- task :authors, [:commit_range, :format, :sep] do |t, a|
111
- a.with_defaults :format => "%s (%d)", :sep => ", ", :commit_range => '--all'
112
- authors = Hash.new { |h,k| h[k] = 0 }
113
- blake = "Blake Mizerany"
114
- overall = 0
115
- mapping = {
116
- "blake.mizerany@gmail.com" => blake, "bmizerany" => blake,
117
- "a_user@mac.com" => blake, "ichverstehe" => "Harry Vangberg",
118
- "Wu Jiang (nouse)" => "Wu Jiang" }
119
- `git shortlog -s #{a.commit_range}`.lines.map do |line|
120
- line = line.force_encoding 'binary' if line.respond_to? :force_encoding
121
- num, name = line.split("\t", 2).map(&:strip)
122
- authors[mapping[name] || name] += num.to_i
123
- overall += num.to_i
124
- end
125
- puts "#{overall} commits by #{authors.count} authors:"
126
- puts authors.sort_by { |n,c| -c }.map { |e| a.format % e }.join(a.sep)
127
- end
128
-
129
- # PACKAGING ============================================================
130
-
131
- if defined?(Gem)
132
- # Load the gemspec using the same limitations as github
133
- def spec
134
- require 'rubygems' unless defined? Gem::Specification
135
- @spec ||= eval(File.read('sinatra.gemspec'))
136
- end
137
-
138
- def package(ext='')
139
- "pkg/sinatra-#{spec.version}" + ext
140
- end
141
-
142
- desc 'Build packages'
143
- task :package => %w[.gem .tar.gz].map {|e| package(e)}
144
-
145
- desc 'Build and install as local gem'
146
- task :install => package('.gem') do
147
- sh "gem install #{package('.gem')}"
148
- end
149
-
150
- directory 'pkg/'
151
- CLOBBER.include('pkg')
152
-
153
- file package('.gem') => %w[pkg/ sinatra.gemspec] + spec.files do |f|
154
- sh "gem build sinatra.gemspec"
155
- mv File.basename(f.name), f.name
156
- end
157
-
158
- file package('.tar.gz') => %w[pkg/] + spec.files do |f|
159
- sh <<-SH
160
- git archive \
161
- --prefix=sinatra-#{source_version}/ \
162
- --format=tar \
163
- HEAD | gzip > #{f.name}
164
- SH
165
- end
166
-
167
- task 'release' => ['test', package('.gem')] do
168
- if File.read("CHANGES") =~ /= \d\.\d\.\d . not yet released$/i
169
- fail 'please update changes first'
170
- end
171
-
172
- sh <<-SH
173
- gem install #{package('.gem')} --local &&
174
- gem push #{package('.gem')} &&
175
- git commit --allow-empty -a -m '#{source_version} release' &&
176
- git tag -s v#{source_version} -m '#{source_version} release' &&
177
- git tag -s #{source_version} -m '#{source_version} release' &&
178
- git push && (git push sinatra || true) &&
179
- git push --tags && (git push sinatra --tags || true)
180
- SH
181
- end
182
- end
@@ -1,61 +0,0 @@
1
- #!/usr/bin/env ruby -I ../lib -I lib
2
- # coding: utf-8
3
- require 'sinatra'
4
- set :server, 'thin'
5
- connections = []
6
-
7
- get '/' do
8
- halt erb(:login) unless params[:user]
9
- erb :chat, :locals => { :user => params[:user].gsub(/\W/, '') }
10
- end
11
-
12
- get '/stream', :provides => 'text/event-stream' do
13
- stream :keep_open do |out|
14
- connections << out
15
- out.callback { connections.delete(out) }
16
- end
17
- end
18
-
19
- post '/' do
20
- connections.each { |out| out << "data: #{params[:msg]}\n\n" }
21
- 204 # response without entity body
22
- end
23
-
24
- __END__
25
-
26
- @@ layout
27
- <html>
28
- <head>
29
- <title>Super Simple Chat with Sinatra</title>
30
- <meta charset="utf-8" />
31
- <script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
32
- </head>
33
- <body><%= yield %></body>
34
- </html>
35
-
36
- @@ login
37
- <form action='/'>
38
- <label for='user'>User Name:</label>
39
- <input name='user' value='' />
40
- <input type='submit' value="GO!" />
41
- </form>
42
-
43
- @@ chat
44
- <pre id='chat'></pre>
45
-
46
- <script>
47
- // reading
48
- var es = new EventSource('/stream');
49
- es.onmessage = function(e) { $('#chat').append(e.data + "\n") };
50
-
51
- // writing
52
- $("form").live("submit", function(e) {
53
- $.post('/', {msg: "<%= user %>: " + $('#msg').val()});
54
- $('#msg').val(''); $('#msg').focus();
55
- e.preventDefault();
56
- });
57
- </script>
58
-
59
- <form>
60
- <input id='msg' placeholder='type message here...' />
61
- </form>
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env ruby -I ../lib -I lib
2
- require 'sinatra'
3
- get('/') { 'this is a simple app' }
@@ -1,26 +0,0 @@
1
- # this example does *not* work properly with WEBrick
2
- #
3
- # run *one* of these:
4
- #
5
- # rackup -s mongrel stream.ru # gem install mongrel
6
- # thin -R stream.ru start # gem install thin
7
- # unicorn stream.ru # gem install unicorn
8
- # puma stream.ru # gem install puma
9
-
10
- require 'sinatra/base'
11
-
12
- class Stream < Sinatra::Base
13
- get '/' do
14
- content_type :txt
15
-
16
- stream do |out|
17
- out << "It's gonna be legen -\n"
18
- sleep 0.5
19
- out << " (wait for it) \n"
20
- sleep 1
21
- out << "- dary!\n"
22
- end
23
- end
24
- end
25
-
26
- run Stream
@@ -1,5 +0,0 @@
1
- require_relative 'sinatra/version'
2
- require_relative 'sinatra/base'
3
- require_relative 'sinatra/main'
4
-
5
- enable :inline_templates
@@ -1,1820 +0,0 @@
1
- # external dependencies
2
- require 'rack'
3
- require 'tilt'
4
- require 'rack/protection'
5
-
6
- # stdlib dependencies
7
- require 'thread'
8
- require 'time'
9
- require 'uri'
10
-
11
- # other files we need
12
- require_relative 'showexceptions'
13
- require_relative 'version'
14
-
15
- module Sinatra
16
- # The request object. See Rack::Request for more info:
17
- # http://rack.rubyforge.org/doc/classes/Rack/Request.html
18
- class Request < Rack::Request
19
- # Returns an array of acceptable media types for the response
20
- def accept
21
- @env['sinatra.accept'] ||= begin
22
- entries = @env['HTTP_ACCEPT'].to_s.split(',')
23
- entries.map { |e| accept_entry(e) }.sort_by(&:last).map(&:first)
24
- end
25
- end
26
-
27
- def preferred_type(*types)
28
- return accept.first if types.empty?
29
- types.flatten!
30
- accept.detect do |pattern|
31
- type = types.detect { |t| File.fnmatch(pattern, t) }
32
- return type if type
33
- end
34
- end
35
-
36
- alias accept? preferred_type
37
- alias secure? ssl?
38
-
39
- def forwarded?
40
- @env.include? "HTTP_X_FORWARDED_HOST"
41
- end
42
-
43
- def safe?
44
- get? or head? or options? or trace?
45
- end
46
-
47
- def idempotent?
48
- safe? or put? or delete?
49
- end
50
-
51
- private
52
-
53
- def accept_entry(entry)
54
- type, *options = entry.delete(' ').split(';')
55
- quality = 0 # we sort smallest first
56
- options.delete_if { |e| quality = 1 - e[2..-1].to_f if e.start_with? 'q=' }
57
- [type, [quality, type.count('*'), 1 - options.size]]
58
- end
59
- end
60
-
61
- # The response object. See Rack::Response and Rack::ResponseHelpers for
62
- # more info:
63
- # http://rack.rubyforge.org/doc/classes/Rack/Response.html
64
- # http://rack.rubyforge.org/doc/classes/Rack/Response/Helpers.html
65
- class Response < Rack::Response
66
- def body=(value)
67
- value = value.body while Rack::Response === value
68
- @body = String === value ? [value.to_str] : value
69
- end
70
-
71
- def each
72
- block_given? ? super : enum_for(:each)
73
- end
74
-
75
- def finish
76
- if status.to_i / 100 == 1
77
- headers.delete "Content-Length"
78
- headers.delete "Content-Type"
79
- elsif Array === body and not [204, 205, 304].include?(status.to_i)
80
- # if some other code has already set Content-Length, don't muck with it
81
- # currently, this would be the static file-handler
82
- headers["Content-Length"] ||= body.inject(0) { |l, p| l + Rack::Utils.bytesize(p) }.to_s
83
- end
84
-
85
- # Rack::Response#finish sometimes returns self as response body. We don't want that.
86
- status, headers, result = super
87
- result = body if result == self || Rack::BodyProxy === result
88
- [status, headers, result]
89
- end
90
- end
91
-
92
- # Some Rack handlers (Thin, Rainbows!) implement an extended body object protocol, however,
93
- # some middleware (namely Rack::Lint) will break it by not mirroring the methods in question.
94
- # This middleware will detect an extended body object and will make sure it reaches the
95
- # handler directly. We do this here, so our middleware and middleware set up by the app will
96
- # still be able to run.
97
- class ExtendedRack < Struct.new(:app)
98
- def call(env)
99
- result, callback = app.call(env), env['async.callback']
100
- return result unless callback and async?(*result)
101
- after_response { callback.call result }
102
- setup_close(env, *result)
103
- throw :async
104
- end
105
-
106
- private
107
-
108
- def setup_close(env, status, header, body)
109
- return unless body.respond_to? :close and env.include? 'async.close'
110
- env['async.close'].callback { body.close }
111
- env['async.close'].errback { body.close }
112
- end
113
-
114
- def after_response(&block)
115
- raise NotImplementedError, "only supports EventMachine at the moment" unless defined? EventMachine
116
- EventMachine.next_tick(&block)
117
- end
118
-
119
- def async?(status, headers, body)
120
- return true if status == -1
121
- body.respond_to? :callback and body.respond_to? :errback
122
- end
123
- end
124
-
125
- # Behaves exactly like Rack::CommonLogger with the notable exception that it does nothing,
126
- # if another CommonLogger is already in the middleware chain.
127
- class CommonLogger < Rack::CommonLogger
128
- def call(env)
129
- env['sinatra.commonlogger'] ? @app.call(env) : super
130
- end
131
-
132
- superclass.class_eval do
133
- alias call_without_check call unless method_defined? :call_without_check
134
- def call(env)
135
- env['sinatra.commonlogger'] = true
136
- call_without_check(env)
137
- end
138
- end
139
- end
140
-
141
- class NotFound < NameError #:nodoc:
142
- def http_status; 404 end
143
- end
144
-
145
- # Methods available to routes, before/after filters, and views.
146
- module Helpers
147
- # Set or retrieve the response status code.
148
- def status(value=nil)
149
- response.status = value if value
150
- response.status
151
- end
152
-
153
- # Set or retrieve the response body. When a block is given,
154
- # evaluation is deferred until the body is read with #each.
155
- def body(value=nil, &block)
156
- if block_given?
157
- def block.each; yield(call) end
158
- response.body = block
159
- elsif value
160
- response.body = value
161
- else
162
- response.body
163
- end
164
- end
165
-
166
- # Halt processing and redirect to the URI provided.
167
- def redirect(uri, *args)
168
- if env['HTTP_VERSION'] == 'HTTP/1.1' and env["REQUEST_METHOD"] != 'GET'
169
- status 303
170
- else
171
- status 302
172
- end
173
-
174
- # According to RFC 2616 section 14.30, "the field value consists of a
175
- # single absolute URI"
176
- response['Location'] = uri(uri.to_s, settings.absolute_redirects?, settings.prefixed_redirects?)
177
- halt(*args)
178
- end
179
-
180
- # Generates the absolute URI for a given path in the app.
181
- # Takes Rack routers and reverse proxies into account.
182
- def uri(addr = nil, absolute = true, add_script_name = true)
183
- return addr if addr =~ /\A[A-z][A-z0-9\+\.\-]*:/
184
- uri = [host = ""]
185
- if absolute
186
- host << "http#{'s' if request.secure?}://"
187
- if request.forwarded? or request.port != (request.secure? ? 443 : 80)
188
- host << request.host_with_port
189
- else
190
- host << request.host
191
- end
192
- end
193
- uri << request.script_name.to_s if add_script_name
194
- uri << (addr ? addr : request.path_info).to_s
195
- File.join uri
196
- end
197
-
198
- alias url uri
199
- alias to uri
200
-
201
- # Halt processing and return the error status provided.
202
- def error(code, body=nil)
203
- code, body = 500, code.to_str if code.respond_to? :to_str
204
- response.body = body unless body.nil?
205
- halt code
206
- end
207
-
208
- # Halt processing and return a 404 Not Found.
209
- def not_found(body=nil)
210
- error 404, body
211
- end
212
-
213
- # Set multiple response headers with Hash.
214
- def headers(hash=nil)
215
- response.headers.merge! hash if hash
216
- response.headers
217
- end
218
-
219
- # Access the underlying Rack session.
220
- def session
221
- request.session
222
- end
223
-
224
- # Access shared logger object.
225
- def logger
226
- request.logger
227
- end
228
-
229
- # Look up a media type by file extension in Rack's mime registry.
230
- def mime_type(type)
231
- Base.mime_type(type)
232
- end
233
-
234
- # Set the Content-Type of the response body given a media type or file
235
- # extension.
236
- def content_type(type = nil, params={})
237
- return response['Content-Type'] unless type
238
- default = params.delete :default
239
- mime_type = mime_type(type) || default
240
- fail "Unknown media type: %p" % type if mime_type.nil?
241
- mime_type = mime_type.dup
242
- unless params.include? :charset or settings.add_charset.all? { |p| not p === mime_type }
243
- params[:charset] = params.delete('charset') || settings.default_encoding
244
- end
245
- params.delete :charset if mime_type.include? 'charset'
246
- unless params.empty?
247
- mime_type << (mime_type.include?(';') ? ', ' : ';')
248
- mime_type << params.map { |kv| kv.join('=') }.join(', ')
249
- end
250
- response['Content-Type'] = mime_type
251
- end
252
-
253
- # Set the Content-Disposition to "attachment" with the specified filename,
254
- # instructing the user agents to prompt to save.
255
- def attachment(filename = nil, disposition = 'attachment')
256
- response['Content-Disposition'] = disposition.to_s
257
- if filename
258
- params = '; filename="%s"' % File.basename(filename)
259
- response['Content-Disposition'] << params
260
- ext = File.extname(filename)
261
- content_type(ext) unless response['Content-Type'] or ext.empty?
262
- end
263
- end
264
-
265
- # Use the contents of the file at +path+ as the response body.
266
- def send_file(path, opts={})
267
- if opts[:type] or not response['Content-Type']
268
- content_type opts[:type] || File.extname(path), :default => 'application/octet-stream'
269
- end
270
-
271
- disposition = opts[:disposition]
272
- filename = opts[:filename]
273
- disposition = 'attachment' if disposition.nil? and filename
274
- filename = path if filename.nil?
275
- attachment(filename, disposition) if disposition
276
-
277
- last_modified opts[:last_modified] if opts[:last_modified]
278
-
279
- file = Rack::File.new nil
280
- file.path = path
281
- result = file.serving env
282
- result[1].each { |k,v| headers[k] ||= v }
283
- headers['Content-Length'] = result[1]['Content-Length']
284
- halt opts[:status] || result[0], result[2]
285
- rescue Errno::ENOENT
286
- not_found
287
- end
288
-
289
- # Class of the response body in case you use #stream.
290
- #
291
- # Three things really matter: The front and back block (back being the
292
- # block generating content, front the one sending it to the client) and
293
- # the scheduler, integrating with whatever concurrency feature the Rack
294
- # handler is using.
295
- #
296
- # Scheduler has to respond to defer and schedule.
297
- class Stream
298
- def self.schedule(*) yield end
299
- def self.defer(*) yield end
300
-
301
- def initialize(scheduler = self.class, keep_open = false, &back)
302
- @back, @scheduler, @keep_open = back.to_proc, scheduler, keep_open
303
- @callbacks, @closed = [], false
304
- end
305
-
306
- def close
307
- return if @closed
308
- @closed = true
309
- @scheduler.schedule { @callbacks.each { |c| c.call }}
310
- end
311
-
312
- def each(&front)
313
- @front = front
314
- @scheduler.defer do
315
- begin
316
- @back.call(self)
317
- rescue Exception => e
318
- @scheduler.schedule { raise e }
319
- end
320
- close unless @keep_open
321
- end
322
- end
323
-
324
- def <<(data)
325
- @scheduler.schedule { @front.call(data.to_s) }
326
- self
327
- end
328
-
329
- def callback(&block)
330
- return yield if @closed
331
- @callbacks << block
332
- end
333
-
334
- alias errback callback
335
-
336
- def closed?
337
- @closed
338
- end
339
- end
340
-
341
- # Allows to start sending data to the client even though later parts of
342
- # the response body have not yet been generated.
343
- #
344
- # The close parameter specifies whether Stream#close should be called
345
- # after the block has been executed. This is only relevant for evented
346
- # servers like Thin or Rainbows.
347
- def stream(keep_open = false)
348
- scheduler = env['async.callback'] ? EventMachine : Stream
349
- current = @params.dup
350
- body Stream.new(scheduler, keep_open) { |out| with_params(current) { yield(out) } }
351
- end
352
-
353
- # Specify response freshness policy for HTTP caches (Cache-Control header).
354
- # Any number of non-value directives (:public, :private, :no_cache,
355
- # :no_store, :must_revalidate, :proxy_revalidate) may be passed along with
356
- # a Hash of value directives (:max_age, :min_stale, :s_max_age).
357
- #
358
- # cache_control :public, :must_revalidate, :max_age => 60
359
- # => Cache-Control: public, must-revalidate, max-age=60
360
- #
361
- # See RFC 2616 / 14.9 for more on standard cache control directives:
362
- # http://tools.ietf.org/html/rfc2616#section-14.9.1
363
- def cache_control(*values)
364
- if values.last.kind_of?(Hash)
365
- hash = values.pop
366
- hash.reject! { |k,v| v == false }
367
- hash.reject! { |k,v| values << k if v == true }
368
- else
369
- hash = {}
370
- end
371
-
372
- values.map! { |value| value.to_s.tr('_','-') }
373
- hash.each do |key, value|
374
- key = key.to_s.tr('_', '-')
375
- value = value.to_i if key == "max-age"
376
- values << [key, value].join('=')
377
- end
378
-
379
- response['Cache-Control'] = values.join(', ') if values.any?
380
- end
381
-
382
- # Set the Expires header and Cache-Control/max-age directive. Amount
383
- # can be an integer number of seconds in the future or a Time object
384
- # indicating when the response should be considered "stale". The remaining
385
- # "values" arguments are passed to the #cache_control helper:
386
- #
387
- # expires 500, :public, :must_revalidate
388
- # => Cache-Control: public, must-revalidate, max-age=60
389
- # => Expires: Mon, 08 Jun 2009 08:50:17 GMT
390
- #
391
- def expires(amount, *values)
392
- values << {} unless values.last.kind_of?(Hash)
393
-
394
- if amount.is_a? Integer
395
- time = Time.now + amount.to_i
396
- max_age = amount
397
- else
398
- time = time_for amount
399
- max_age = time - Time.now
400
- end
401
-
402
- values.last.merge!(:max_age => max_age)
403
- cache_control(*values)
404
-
405
- response['Expires'] = time.httpdate
406
- end
407
-
408
- # Set the last modified time of the resource (HTTP 'Last-Modified' header)
409
- # and halt if conditional GET matches. The +time+ argument is a Time,
410
- # DateTime, or other object that responds to +to_time+.
411
- #
412
- # When the current request includes an 'If-Modified-Since' header that is
413
- # equal or later than the time specified, execution is immediately halted
414
- # with a '304 Not Modified' response.
415
- def last_modified(time)
416
- return unless time
417
- time = time_for time
418
- response['Last-Modified'] = time.httpdate
419
- return if env['HTTP_IF_NONE_MATCH']
420
-
421
- if status == 200 and env['HTTP_IF_MODIFIED_SINCE']
422
- # compare based on seconds since epoch
423
- since = Time.httpdate(env['HTTP_IF_MODIFIED_SINCE']).to_i
424
- halt 304 if since >= time.to_i
425
- end
426
-
427
- if (success? or status == 412) and env['HTTP_IF_UNMODIFIED_SINCE']
428
- # compare based on seconds since epoch
429
- since = Time.httpdate(env['HTTP_IF_UNMODIFIED_SINCE']).to_i
430
- halt 412 if since < time.to_i
431
- end
432
- rescue ArgumentError
433
- end
434
-
435
- # Set the response entity tag (HTTP 'ETag' header) and halt if conditional
436
- # GET matches. The +value+ argument is an identifier that uniquely
437
- # identifies the current version of the resource. The +kind+ argument
438
- # indicates whether the etag should be used as a :strong (default) or :weak
439
- # cache validator.
440
- #
441
- # When the current request includes an 'If-None-Match' header with a
442
- # matching etag, execution is immediately halted. If the request method is
443
- # GET or HEAD, a '304 Not Modified' response is sent.
444
- def etag(value, options = {})
445
- # Before touching this code, please double check RFC 2616 14.24 and 14.26.
446
- options = {:kind => options} unless Hash === options
447
- kind = options[:kind] || :strong
448
- new_resource = options.fetch(:new_resource) { request.post? }
449
-
450
- unless [:strong, :weak].include?(kind)
451
- raise ArgumentError, ":strong or :weak expected"
452
- end
453
-
454
- value = '"%s"' % value
455
- value = 'W/' + value if kind == :weak
456
- response['ETag'] = value
457
-
458
- if success? or status == 304
459
- if etag_matches? env['HTTP_IF_NONE_MATCH'], new_resource
460
- halt(request.safe? ? 304 : 412)
461
- end
462
-
463
- if env['HTTP_IF_MATCH']
464
- halt 412 unless etag_matches? env['HTTP_IF_MATCH'], new_resource
465
- end
466
- end
467
- end
468
-
469
- # Sugar for redirect (example: redirect back)
470
- def back
471
- request.referer
472
- end
473
-
474
- # whether or not the status is set to 1xx
475
- def informational?
476
- status.between? 100, 199
477
- end
478
-
479
- # whether or not the status is set to 2xx
480
- def success?
481
- status.between? 200, 299
482
- end
483
-
484
- # whether or not the status is set to 3xx
485
- def redirect?
486
- status.between? 300, 399
487
- end
488
-
489
- # whether or not the status is set to 4xx
490
- def client_error?
491
- status.between? 400, 499
492
- end
493
-
494
- # whether or not the status is set to 5xx
495
- def server_error?
496
- status.between? 500, 599
497
- end
498
-
499
- # whether or not the status is set to 404
500
- def not_found?
501
- status == 404
502
- end
503
-
504
- # Generates a Time object from the given value.
505
- # Used by #expires and #last_modified.
506
- def time_for(value)
507
- if value.respond_to? :to_time
508
- value.to_time
509
- elsif value.is_a? Time
510
- value
511
- elsif value.respond_to? :new_offset
512
- # DateTime#to_time does the same on 1.9
513
- d = value.new_offset 0
514
- t = Time.utc d.year, d.mon, d.mday, d.hour, d.min, d.sec + d.sec_fraction
515
- t.getlocal
516
- elsif value.respond_to? :mday
517
- # Date#to_time does the same on 1.9
518
- Time.local(value.year, value.mon, value.mday)
519
- elsif value.is_a? Numeric
520
- Time.at value
521
- else
522
- Time.parse value.to_s
523
- end
524
- rescue ArgumentError => boom
525
- raise boom
526
- rescue Exception
527
- raise ArgumentError, "unable to convert #{value.inspect} to a Time object"
528
- end
529
-
530
- private
531
-
532
- # Helper method checking if a ETag value list includes the current ETag.
533
- def etag_matches?(list, new_resource = request.post?)
534
- return !new_resource if list == '*'
535
- list.to_s.split(/\s*,\s*/).include? response['ETag']
536
- end
537
-
538
- def with_params(temp_params)
539
- original, @params = @params, temp_params
540
- yield
541
- ensure
542
- @params = original if original
543
- end
544
- end
545
-
546
- private
547
-
548
- # Template rendering methods. Each method takes the name of a template
549
- # to render as a Symbol and returns a String with the rendered output,
550
- # as well as an optional hash with additional options.
551
- #
552
- # `template` is either the name or path of the template as symbol
553
- # (Use `:'subdir/myview'` for views in subdirectories), or a string
554
- # that will be rendered.
555
- #
556
- # Possible options are:
557
- # :content_type The content type to use, same arguments as content_type.
558
- # :layout If set to false, no layout is rendered, otherwise
559
- # the specified layout is used (Ignored for `sass` and `less`)
560
- # :layout_engine Engine to use for rendering the layout.
561
- # :locals A hash with local variables that should be available
562
- # in the template
563
- # :scope If set, template is evaluate with the binding of the given
564
- # object rather than the application instance.
565
- # :views Views directory to use.
566
- module Templates
567
- module ContentTyped
568
- attr_accessor :content_type
569
- end
570
-
571
- def initialize
572
- super
573
- @default_layout = :layout
574
- end
575
-
576
- def erb(template, options={}, locals={})
577
- render :erb, template, options, locals
578
- end
579
-
580
- def erubis(template, options={}, locals={})
581
- warn "Sinatra::Templates#erubis is deprecated and will be removed, use #erb instead.\n" \
582
- "If you have Erubis installed, it will be used automatically."
583
- render :erubis, template, options, locals
584
- end
585
-
586
- def haml(template, options={}, locals={})
587
- render :haml, template, options, locals
588
- end
589
-
590
- def sass(template, options={}, locals={})
591
- options.merge! :layout => false, :default_content_type => :css
592
- render :sass, template, options, locals
593
- end
594
-
595
- def scss(template, options={}, locals={})
596
- options.merge! :layout => false, :default_content_type => :css
597
- render :scss, template, options, locals
598
- end
599
-
600
- def less(template, options={}, locals={})
601
- options.merge! :layout => false, :default_content_type => :css
602
- render :less, template, options, locals
603
- end
604
-
605
- def builder(template=nil, options={}, locals={}, &block)
606
- options[:default_content_type] = :xml
607
- render_ruby(:builder, template, options, locals, &block)
608
- end
609
-
610
- def liquid(template, options={}, locals={})
611
- render :liquid, template, options, locals
612
- end
613
-
614
- def markdown(template, options={}, locals={})
615
- render :markdown, template, options, locals
616
- end
617
-
618
- def textile(template, options={}, locals={})
619
- render :textile, template, options, locals
620
- end
621
-
622
- def rdoc(template, options={}, locals={})
623
- render :rdoc, template, options, locals
624
- end
625
-
626
- def radius(template, options={}, locals={})
627
- render :radius, template, options, locals
628
- end
629
-
630
- def markaby(template=nil, options={}, locals={}, &block)
631
- render_ruby(:mab, template, options, locals, &block)
632
- end
633
-
634
- def coffee(template, options={}, locals={})
635
- options.merge! :layout => false, :default_content_type => :js
636
- render :coffee, template, options, locals
637
- end
638
-
639
- def nokogiri(template=nil, options={}, locals={}, &block)
640
- options[:default_content_type] = :xml
641
- render_ruby(:nokogiri, template, options, locals, &block)
642
- end
643
-
644
- def slim(template, options={}, locals={})
645
- render :slim, template, options, locals
646
- end
647
-
648
- def creole(template, options={}, locals={})
649
- render :creole, template, options, locals
650
- end
651
-
652
- def wlang(template, options={}, locals={})
653
- render :wlang, template, options, locals
654
- end
655
-
656
- def yajl(template, options={}, locals={})
657
- options[:default_content_type] = :json
658
- render :yajl, template, options, locals
659
- end
660
-
661
- def rabl(template, options={}, locals={})
662
- Rabl.register!
663
- render :rabl, template, options, locals
664
- end
665
-
666
- # Calls the given block for every possible template file in views,
667
- # named name.ext, where ext is registered on engine.
668
- def find_template(views, name, engine)
669
- yield ::File.join(views, "#{name}.#{@preferred_extension}")
670
- Tilt.mappings.each do |ext, engines|
671
- next unless ext != @preferred_extension and engines.include? engine
672
- yield ::File.join(views, "#{name}.#{ext}")
673
- end
674
- end
675
-
676
- private
677
- # logic shared between builder and nokogiri
678
- def render_ruby(engine, template, options={}, locals={}, &block)
679
- options, template = template, nil if template.is_a?(Hash)
680
- template = Proc.new { block } if template.nil?
681
- render engine, template, options, locals
682
- end
683
-
684
- def render(engine, data, options={}, locals={}, &block)
685
- # merge app-level options
686
- engine_options = settings.respond_to?(engine) ? settings.send(engine) : {}
687
- options = engine_options.merge(options)
688
-
689
- # extract generic options
690
- locals = options.delete(:locals) || locals || {}
691
- views = options.delete(:views) || settings.views || "./views"
692
- layout = options.delete(:layout)
693
- eat_errors = layout.nil?
694
- layout = engine_options[:layout] if layout.nil? or layout == true
695
- layout = @default_layout if layout.nil? or layout == true
696
- content_type = options.delete(:content_type) || options.delete(:default_content_type)
697
- layout_engine = options.delete(:layout_engine) || engine
698
- scope = options.delete(:scope) || self
699
-
700
- # set some defaults
701
- options[:outvar] ||= '@_out_buf'
702
- options[:default_encoding] ||= settings.default_encoding
703
-
704
- # compile and render template
705
- begin
706
- layout_was = @default_layout
707
- @default_layout = false
708
- template = compile_template(engine, data, options, views)
709
- output = template.render(scope, locals, &block)
710
- ensure
711
- @default_layout = layout_was
712
- end
713
-
714
- # render layout
715
- if layout
716
- options = options.merge(:views => views, :layout => false, :eat_errors => eat_errors, :scope => scope)
717
- catch(:layout_missing) { return render(layout_engine, layout, options, locals) { output } }
718
- end
719
-
720
- output.extend(ContentTyped).content_type = content_type if content_type
721
- output
722
- end
723
-
724
- def compile_template(engine, data, options, views)
725
- eat_errors = options.delete :eat_errors
726
- template_cache.fetch engine, data, options do
727
- template = Tilt[engine]
728
- raise "Template engine not found: #{engine}" if template.nil?
729
-
730
- case data
731
- when Symbol
732
- body, path, line = settings.templates[data]
733
- if body
734
- body = body.call if body.respond_to?(:call)
735
- template.new(path, line.to_i, options) { body }
736
- else
737
- found = false
738
- @preferred_extension = engine.to_s
739
- find_template(views, data, template) do |file|
740
- path ||= file # keep the initial path rather than the last one
741
- if found = File.exists?(file)
742
- path = file
743
- break
744
- end
745
- end
746
- throw :layout_missing if eat_errors and not found
747
- template.new(path, 1, options)
748
- end
749
- when Proc, String
750
- body = data.is_a?(String) ? Proc.new { data } : data
751
- path, line = settings.caller_locations.first
752
- template.new(path, line.to_i, options, &body)
753
- else
754
- raise ArgumentError, "Sorry, don't know how to render #{data.inspect}."
755
- end
756
- end
757
- end
758
- end
759
-
760
- # Base class for all Sinatra applications and middleware.
761
- class Base
762
- include Rack::Utils
763
- include Helpers
764
- include Templates
765
-
766
- attr_accessor :app
767
- attr_reader :template_cache
768
-
769
- def initialize(app=nil)
770
- super()
771
- @app = app
772
- @template_cache = Tilt::Cache.new
773
- yield self if block_given?
774
- end
775
-
776
- # Rack call interface.
777
- def call(env)
778
- dup.call!(env)
779
- end
780
-
781
- attr_accessor :env, :request, :response, :params
782
-
783
- def call!(env) # :nodoc:
784
- @env = env
785
- @request = Request.new(env)
786
- @response = Response.new
787
- @params = indifferent_params(@request.params)
788
- template_cache.clear if settings.reload_templates
789
- force_encoding(@params)
790
-
791
- @response['Content-Type'] = nil
792
- invoke { dispatch! }
793
- invoke { error_block!(response.status) }
794
-
795
- unless @response['Content-Type']
796
- if Array === body and body[0].respond_to? :content_type
797
- content_type body[0].content_type
798
- else
799
- content_type :html
800
- end
801
- end
802
-
803
- @response.finish
804
- end
805
-
806
- # Access settings defined with Base.set.
807
- def self.settings
808
- self
809
- end
810
-
811
- # Access settings defined with Base.set.
812
- def settings
813
- self.class.settings
814
- end
815
-
816
- def options
817
- warn "Sinatra::Base#options is deprecated and will be removed, " \
818
- "use #settings instead."
819
- settings
820
- end
821
-
822
- # Exit the current block, halts any further processing
823
- # of the request, and returns the specified response.
824
- def halt(*response)
825
- response = response.first if response.length == 1
826
- throw :halt, response
827
- end
828
-
829
- # Pass control to the next matching route.
830
- # If there are no more matching routes, Sinatra will
831
- # return a 404 response.
832
- def pass(&block)
833
- throw :pass, block
834
- end
835
-
836
- # Forward the request to the downstream app -- middleware only.
837
- def forward
838
- fail "downstream app not set" unless @app.respond_to? :call
839
- status, headers, body = @app.call env
840
- @response.status = status
841
- @response.body = body
842
- @response.headers.merge! headers
843
- nil
844
- end
845
-
846
- private
847
- # Run filters defined on the class and all superclasses.
848
- def filter!(type, base = settings)
849
- filter! type, base.superclass if base.superclass.respond_to?(:filters)
850
- base.filters[type].each { |args| process_route(*args) }
851
- end
852
-
853
- # Run routes defined on the class and all superclasses.
854
- def route!(base = settings, pass_block=nil)
855
- if routes = base.routes[@request.request_method]
856
- routes.each do |pattern, keys, conditions, block|
857
- pass_block = process_route(pattern, keys, conditions) do |*args|
858
- route_eval { block[*args] }
859
- end
860
- end
861
- end
862
-
863
- # Run routes defined in superclass.
864
- if base.superclass.respond_to?(:routes)
865
- return route!(base.superclass, pass_block)
866
- end
867
-
868
- route_eval(&pass_block) if pass_block
869
- route_missing
870
- end
871
-
872
- # Run a route block and throw :halt with the result.
873
- def route_eval
874
- throw :halt, yield
875
- end
876
-
877
- # If the current request matches pattern and conditions, fill params
878
- # with keys and call the given block.
879
- # Revert params afterwards.
880
- #
881
- # Returns pass block.
882
- def process_route(pattern, keys, conditions, block = nil, values = [])
883
- route = @request.path_info
884
- route = '/' if route.empty? and not settings.empty_path_info?
885
- return unless match = pattern.match(route)
886
- values += match.captures.to_a.map { |v| force_encoding URI.decode_www_form_component(v) if v }
887
-
888
- if values.any?
889
- original, @params = params, params.merge('splat' => [], 'captures' => values)
890
- keys.zip(values) { |k,v| Array === @params[k] ? @params[k] << v : @params[k] = v if v }
891
- end
892
-
893
- catch(:pass) do
894
- conditions.each { |c| throw :pass if c.bind(self).call == false }
895
- block ? block[self, values] : yield(self, values)
896
- end
897
- ensure
898
- @params = original if original
899
- end
900
-
901
- # No matching route was found or all routes passed. The default
902
- # implementation is to forward the request downstream when running
903
- # as middleware (@app is non-nil); when no downstream app is set, raise
904
- # a NotFound exception. Subclasses can override this method to perform
905
- # custom route miss logic.
906
- def route_missing
907
- if @app
908
- forward
909
- else
910
- raise NotFound
911
- end
912
- end
913
-
914
- # Attempt to serve static files from public directory. Throws :halt when
915
- # a matching file is found, returns nil otherwise.
916
- def static!
917
- return if (public_dir = settings.public_folder).nil?
918
- public_dir = File.expand_path(public_dir)
919
-
920
- path = File.expand_path(public_dir + unescape(request.path_info))
921
- return unless path.start_with?(public_dir) and File.file?(path)
922
-
923
- env['sinatra.static_file'] = path
924
- cache_control(*settings.static_cache_control) if settings.static_cache_control?
925
- send_file path, :disposition => nil
926
- end
927
-
928
- # Enable string or symbol key access to the nested params hash.
929
- def indifferent_params(object)
930
- case object
931
- when Hash
932
- new_hash = indifferent_hash
933
- object.each { |key, value| new_hash[key] = indifferent_params(value) }
934
- new_hash
935
- when Array
936
- object.map { |item| indifferent_params(item) }
937
- else
938
- object
939
- end
940
- end
941
-
942
- # Creates a Hash with indifferent access.
943
- def indifferent_hash
944
- Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
945
- end
946
-
947
- # Run the block with 'throw :halt' support and apply result to the response.
948
- def invoke
949
- res = catch(:halt) { yield }
950
- res = [res] if Fixnum === res or String === res
951
- if Array === res and Fixnum === res.first
952
- res = res.dup
953
- status(res.shift)
954
- body(res.pop)
955
- headers(*res)
956
- elsif res.respond_to? :each
957
- body res
958
- end
959
- nil # avoid double setting the same response tuple twice
960
- end
961
-
962
- # Dispatch a request with error handling.
963
- def dispatch!
964
- invoke do
965
- static! if settings.static? && (request.get? || request.head?)
966
- filter! :before
967
- route!
968
- end
969
- rescue ::Exception => boom
970
- invoke { handle_exception!(boom) }
971
- ensure
972
- filter! :after unless env['sinatra.static_file']
973
- end
974
-
975
- # Error handling during requests.
976
- def handle_exception!(boom)
977
- @env['sinatra.error'] = boom
978
-
979
- if boom.respond_to? :http_status
980
- status(boom.http_status)
981
- elsif settings.use_code? and boom.respond_to? :code and boom.code.between? 400, 599
982
- status(boom.code)
983
- else
984
- status(500)
985
- end
986
-
987
- status(500) unless status.between? 400, 599
988
-
989
- if server_error?
990
- dump_errors! boom if settings.dump_errors?
991
- raise boom if settings.show_exceptions? and settings.show_exceptions != :after_handler
992
- end
993
-
994
- if not_found?
995
- headers['X-Cascade'] = 'pass'
996
- body '<h1>Not Found</h1>'
997
- end
998
-
999
- res = error_block!(boom.class, boom) || error_block!(status, boom)
1000
- return res if res or not server_error?
1001
- raise boom if settings.raise_errors? or settings.show_exceptions?
1002
- error_block! Exception, boom
1003
- end
1004
-
1005
- # Find an custom error block for the key(s) specified.
1006
- def error_block!(key, *block_params)
1007
- base = settings
1008
- while base.respond_to?(:errors)
1009
- next base = base.superclass unless args_array = base.errors[key]
1010
- args_array.reverse_each do |args|
1011
- first = args == args_array.first
1012
- args += [block_params]
1013
- resp = process_route(*args)
1014
- return resp unless resp.nil? && !first
1015
- end
1016
- end
1017
- return false unless key.respond_to? :superclass and key.superclass < Exception
1018
- error_block!(key.superclass, *block_params)
1019
- end
1020
-
1021
- def dump_errors!(boom)
1022
- msg = ["#{boom.class} - #{boom.message}:", *boom.backtrace].join("\n\t")
1023
- @env['rack.errors'].puts(msg)
1024
- end
1025
-
1026
- class << self
1027
- attr_reader :routes, :filters, :templates, :errors
1028
-
1029
- # Removes all routes, filters, middleware and extension hooks from the
1030
- # current class (not routes/filters/... defined by its superclass).
1031
- def reset!
1032
- @conditions = []
1033
- @routes = {}
1034
- @filters = {:before => [], :after => []}
1035
- @errors = {}
1036
- @middleware = []
1037
- @prototype = nil
1038
- @extensions = []
1039
-
1040
- if superclass.respond_to?(:templates)
1041
- @templates = Hash.new { |hash,key| superclass.templates[key] }
1042
- else
1043
- @templates = {}
1044
- end
1045
- end
1046
-
1047
- # Extension modules registered on this class and all superclasses.
1048
- def extensions
1049
- if superclass.respond_to?(:extensions)
1050
- (@extensions + superclass.extensions).uniq
1051
- else
1052
- @extensions
1053
- end
1054
- end
1055
-
1056
- # Middleware used in this class and all superclasses.
1057
- def middleware
1058
- if superclass.respond_to?(:middleware)
1059
- superclass.middleware + @middleware
1060
- else
1061
- @middleware
1062
- end
1063
- end
1064
-
1065
- # Sets an option to the given value. If the value is a proc,
1066
- # the proc will be called every time the option is accessed.
1067
- def set(option, value = (not_set = true), ignore_setter = false, &block)
1068
- raise ArgumentError if block and !not_set
1069
- value, not_set = block, false if block
1070
-
1071
- if not_set
1072
- raise ArgumentError unless option.respond_to?(:each)
1073
- option.each { |k,v| set(k, v) }
1074
- return self
1075
- end
1076
-
1077
- if respond_to?("#{option}=") and not ignore_setter
1078
- return __send__("#{option}=", value)
1079
- end
1080
-
1081
- setter = proc { |val| set option, val, true }
1082
- getter = proc { value }
1083
-
1084
- case value
1085
- when Proc
1086
- getter = value
1087
- when Symbol, Fixnum, FalseClass, TrueClass, NilClass
1088
- getter = value.inspect
1089
- when Hash
1090
- setter = proc do |val|
1091
- val = value.merge val if Hash === val
1092
- set option, val, true
1093
- end
1094
- end
1095
-
1096
- define_singleton_method("#{option}=", setter) if setter
1097
- define_singleton_method(option, getter) if getter
1098
- define_singleton_method("#{option}?", "!!#{option}") unless method_defined? "#{option}?"
1099
- self
1100
- end
1101
-
1102
- # Same as calling `set :option, true` for each of the given options.
1103
- def enable(*opts)
1104
- opts.each { |key| set(key, true) }
1105
- end
1106
-
1107
- # Same as calling `set :option, false` for each of the given options.
1108
- def disable(*opts)
1109
- opts.each { |key| set(key, false) }
1110
- end
1111
-
1112
- # Define a custom error handler. Optionally takes either an Exception
1113
- # class, or an HTTP status code to specify which errors should be
1114
- # handled.
1115
- def error(*codes, &block)
1116
- args = compile! "ERROR", //, block
1117
- codes = codes.map { |c| Array(c) }.flatten
1118
- codes << Exception if codes.empty?
1119
- codes.each { |c| (@errors[c] ||= []) << args }
1120
- end
1121
-
1122
- # Sugar for `error(404) { ... }`
1123
- def not_found(&block)
1124
- error 404, &block
1125
- end
1126
-
1127
- # Define a named template. The block must return the template source.
1128
- def template(name, &block)
1129
- filename, line = caller_locations.first
1130
- templates[name] = [block, filename, line.to_i]
1131
- end
1132
-
1133
- # Define the layout template. The block must return the template source.
1134
- def layout(name=:layout, &block)
1135
- template name, &block
1136
- end
1137
-
1138
- # Load embeded templates from the file; uses the caller's __FILE__
1139
- # when no file is specified.
1140
- def inline_templates=(file=nil)
1141
- file = (file.nil? || file == true) ? (caller_files.first || File.expand_path($0)) : file
1142
-
1143
- begin
1144
- io = ::IO.respond_to?(:binread) ? ::IO.binread(file) : ::IO.read(file)
1145
- app, data = io.gsub("\r\n", "\n").split(/^__END__$/, 2)
1146
- rescue Errno::ENOENT
1147
- app, data = nil
1148
- end
1149
-
1150
- if data
1151
- if app and app =~ /([^\n]*\n)?#[^\n]*coding: *(\S+)/m
1152
- encoding = $2
1153
- else
1154
- encoding = settings.default_encoding
1155
- end
1156
- lines = app.count("\n") + 1
1157
- template = nil
1158
- force_encoding data, encoding
1159
- data.each_line do |line|
1160
- lines += 1
1161
- if line =~ /^@@\s*(.*\S)\s*$/
1162
- template = force_encoding('', encoding)
1163
- templates[$1.to_sym] = [template, file, lines]
1164
- elsif template
1165
- template << line
1166
- end
1167
- end
1168
- end
1169
- end
1170
-
1171
- # Lookup or register a mime type in Rack's mime registry.
1172
- def mime_type(type, value=nil)
1173
- return type if type.nil? || type.to_s.include?('/')
1174
- type = ".#{type}" unless type.to_s[0] == ?.
1175
- return Rack::Mime.mime_type(type, nil) unless value
1176
- Rack::Mime::MIME_TYPES[type] = value
1177
- end
1178
-
1179
- # provides all mime types matching type, including deprecated types:
1180
- # mime_types :html # => ['text/html']
1181
- # mime_types :js # => ['application/javascript', 'text/javascript']
1182
- def mime_types(type)
1183
- type = mime_type type
1184
- type =~ /^application\/(xml|javascript)$/ ? [type, "text/#$1"] : [type]
1185
- end
1186
-
1187
- # Define a before filter; runs before all requests within the same
1188
- # context as route handlers and may access/modify the request and
1189
- # response.
1190
- def before(path = nil, options = {}, &block)
1191
- add_filter(:before, path, options, &block)
1192
- end
1193
-
1194
- # Define an after filter; runs after all requests within the same
1195
- # context as route handlers and may access/modify the request and
1196
- # response.
1197
- def after(path = nil, options = {}, &block)
1198
- add_filter(:after, path, options, &block)
1199
- end
1200
-
1201
- # add a filter
1202
- def add_filter(type, path = nil, options = {}, &block)
1203
- path, options = //, path if path.respond_to?(:each_pair)
1204
- filters[type] << compile!(type, path || //, block, options)
1205
- end
1206
-
1207
- # Add a route condition. The route is considered non-matching when the
1208
- # block returns false.
1209
- def condition(name = "#{caller.first[/`.*'/]} condition", &block)
1210
- @conditions << generate_method(name, &block)
1211
- end
1212
-
1213
- def public=(value)
1214
- warn ":public is no longer used to avoid overloading Module#public, use :public_dir instead"
1215
- set(:public_folder, value)
1216
- end
1217
-
1218
- def public_dir=(value)
1219
- self.public_folder = value
1220
- end
1221
-
1222
- def public_dir
1223
- public_folder
1224
- end
1225
-
1226
- private
1227
- # Dynamically defines a method on settings.
1228
- def define_singleton_method(name, content = Proc.new)
1229
- # replace with call to singleton_class once we're 1.9 only
1230
- (class << self; self; end).class_eval do
1231
- undef_method(name) if method_defined? name
1232
- String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content)
1233
- end
1234
- end
1235
-
1236
- # Condition for matching host name. Parameter might be String or Regexp.
1237
- def host_name(pattern)
1238
- condition { pattern === request.host }
1239
- end
1240
-
1241
- # Condition for matching user agent. Parameter should be Regexp.
1242
- # Will set params[:agent].
1243
- def user_agent(pattern)
1244
- condition do
1245
- if request.user_agent.to_s =~ pattern
1246
- @params[:agent] = $~[1..-1]
1247
- true
1248
- else
1249
- false
1250
- end
1251
- end
1252
- end
1253
- alias_method :agent, :user_agent
1254
-
1255
- # Condition for matching mimetypes. Accepts file extensions.
1256
- def provides(*types)
1257
- types.map! { |t| mime_types(t) }
1258
- types.flatten!
1259
- condition do
1260
- if type = response['Content-Type']
1261
- types.include? type or types.include? type[/^[^;]+/]
1262
- elsif type = request.preferred_type(types)
1263
- content_type(type)
1264
- true
1265
- else
1266
- false
1267
- end
1268
- end
1269
- end
1270
-
1271
- public
1272
- # Defining a `GET` handler also automatically defines
1273
- # a `HEAD` handler.
1274
- def get(path, opts={}, &block)
1275
- conditions = @conditions.dup
1276
- route('GET', path, opts, &block)
1277
-
1278
- @conditions = conditions
1279
- route('HEAD', path, opts, &block)
1280
- end
1281
-
1282
- def put(path, opts={}, &bk) route 'PUT', path, opts, &bk end
1283
- def post(path, opts={}, &bk) route 'POST', path, opts, &bk end
1284
- def delete(path, opts={}, &bk) route 'DELETE', path, opts, &bk end
1285
- def head(path, opts={}, &bk) route 'HEAD', path, opts, &bk end
1286
- def options(path, opts={}, &bk) route 'OPTIONS', path, opts, &bk end
1287
- def patch(path, opts={}, &bk) route 'PATCH', path, opts, &bk end
1288
-
1289
- private
1290
- def route(verb, path, options={}, &block)
1291
- # Because of self.options.host
1292
- host_name(options.delete(:host)) if options.key?(:host)
1293
- enable :empty_path_info if path == "" and empty_path_info.nil?
1294
- signature = compile!(verb, path, block, options)
1295
- (@routes[verb] ||= []) << signature
1296
- invoke_hook(:route_added, verb, path, block)
1297
- signature
1298
- end
1299
-
1300
- def invoke_hook(name, *args)
1301
- extensions.each { |e| e.send(name, *args) if e.respond_to?(name) }
1302
- end
1303
-
1304
- def generate_method(method_name, &block)
1305
- define_method(method_name, &block)
1306
- method = instance_method method_name
1307
- remove_method method_name
1308
- method
1309
- end
1310
-
1311
- def compile!(verb, path, block, options = {})
1312
- options.each_pair { |option, args| send(option, *args) }
1313
- method_name = "#{verb} #{path}"
1314
- unbound_method = generate_method(method_name, &block)
1315
- pattern, keys = compile path
1316
- conditions, @conditions = @conditions, []
1317
-
1318
- [ pattern, keys, conditions, block.arity != 0 ?
1319
- proc { |a,p| unbound_method.bind(a).call(*p) } :
1320
- proc { |a,p| unbound_method.bind(a).call } ]
1321
- end
1322
-
1323
- def compile(path)
1324
- keys = []
1325
- if path.respond_to? :to_str
1326
- ignore = ""
1327
- pattern = path.to_str.gsub(/[^\?\%\\\/\:\*\w]/) do |c|
1328
- ignore << escaped(c).join if c.match(/[\.@]/)
1329
- encoded(c)
1330
- end
1331
- pattern.gsub!(/((:\w+)|\*)/) do |match|
1332
- if match == "*"
1333
- keys << 'splat'
1334
- "(.*?)"
1335
- else
1336
- keys << $2[1..-1]
1337
- "([^#{ignore}/?#]+)"
1338
- end
1339
- end
1340
- [/\A#{pattern}\z/, keys]
1341
- elsif path.respond_to?(:keys) && path.respond_to?(:match)
1342
- [path, path.keys]
1343
- elsif path.respond_to?(:names) && path.respond_to?(:match)
1344
- [path, path.names]
1345
- elsif path.respond_to? :match
1346
- [path, keys]
1347
- else
1348
- raise TypeError, path
1349
- end
1350
- end
1351
-
1352
- URI = ::URI.const_defined?(:Parser) ? ::URI::Parser.new : ::URI
1353
-
1354
- def encoded(char)
1355
- enc = URI.escape(char)
1356
- enc = "(?:#{escaped(char, enc).join('|')})" if enc == char
1357
- enc = "(?:#{enc}|#{encoded('+')})" if char == " "
1358
- enc
1359
- end
1360
-
1361
- def escaped(char, enc = URI.escape(char))
1362
- [Regexp.escape(enc), URI.escape(char, /./)]
1363
- end
1364
-
1365
- public
1366
- # Makes the methods defined in the block and in the Modules given
1367
- # in `extensions` available to the handlers and templates
1368
- def helpers(*extensions, &block)
1369
- class_eval(&block) if block_given?
1370
- include(*extensions) if extensions.any?
1371
- end
1372
-
1373
- # Register an extension. Alternatively take a block from which an
1374
- # extension will be created and registered on the fly.
1375
- def register(*extensions, &block)
1376
- extensions << Module.new(&block) if block_given?
1377
- @extensions += extensions
1378
- extensions.each do |extension|
1379
- extend extension
1380
- extension.registered(self) if extension.respond_to?(:registered)
1381
- end
1382
- end
1383
-
1384
- def development?; environment == :development end
1385
- def production?; environment == :production end
1386
- def test?; environment == :test end
1387
-
1388
- # Set configuration options for Sinatra and/or the app.
1389
- # Allows scoping of settings for certain environments.
1390
- def configure(*envs, &block)
1391
- yield self if envs.empty? || envs.include?(environment.to_sym)
1392
- end
1393
-
1394
- # Use the specified Rack middleware
1395
- def use(middleware, *args, &block)
1396
- @prototype = nil
1397
- @middleware << [middleware, args, block]
1398
- end
1399
-
1400
- def quit!(server, handler_name)
1401
- # Use Thin's hard #stop! if available, otherwise just #stop.
1402
- server.respond_to?(:stop!) ? server.stop! : server.stop
1403
- $stderr.puts "\n== Sinatra has ended his set (crowd applauds)" unless handler_name =~/cgi/i
1404
- end
1405
-
1406
- # Run the Sinatra app as a self-hosted server using
1407
- # Thin, Puma, Mongrel, or WEBrick (in that order). If given a block, will call
1408
- # with the constructed handler once we have taken the stage.
1409
- def run!(options={})
1410
- set options
1411
- handler = detect_rack_handler
1412
- handler_name = handler.name.gsub(/.*::/, '')
1413
- server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {}
1414
- handler.run self, server_settings.merge(:Port => port, :Host => bind) do |server|
1415
- unless handler_name =~ /cgi/i
1416
- $stderr.puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " +
1417
- "on #{port} for #{environment} with backup from #{handler_name}"
1418
- end
1419
- [:INT, :TERM].each { |sig| trap(sig) { quit!(server, handler_name) } }
1420
- server.threaded = settings.threaded if server.respond_to? :threaded=
1421
- set :running, true
1422
- yield server if block_given?
1423
- end
1424
- rescue Errno::EADDRINUSE
1425
- $stderr.puts "== Someone is already performing on port #{port}!"
1426
- end
1427
-
1428
- # The prototype instance used to process requests.
1429
- def prototype
1430
- @prototype ||= new
1431
- end
1432
-
1433
- # Create a new instance without middleware in front of it.
1434
- alias new! new unless method_defined? :new!
1435
-
1436
- # Create a new instance of the class fronted by its middleware
1437
- # pipeline. The object is guaranteed to respond to #call but may not be
1438
- # an instance of the class new was called on.
1439
- def new(*args, &bk)
1440
- instance = new!(*args, &bk)
1441
- Wrapper.new(build(instance).to_app, instance)
1442
- end
1443
-
1444
- # Creates a Rack::Builder instance with all the middleware set up and
1445
- # the given +app+ as end point.
1446
- def build(app)
1447
- builder = Rack::Builder.new
1448
- setup_default_middleware builder
1449
- setup_middleware builder
1450
- builder.run app
1451
- builder
1452
- end
1453
-
1454
- def call(env)
1455
- synchronize { prototype.call(env) }
1456
- end
1457
-
1458
- private
1459
- def setup_default_middleware(builder)
1460
- builder.use ExtendedRack
1461
- builder.use ShowExceptions if show_exceptions?
1462
- builder.use Rack::MethodOverride if method_override?
1463
- builder.use Rack::Head
1464
- setup_logging builder
1465
- setup_sessions builder
1466
- setup_protection builder
1467
- end
1468
-
1469
- def setup_middleware(builder)
1470
- middleware.each { |c,a,b| builder.use(c, *a, &b) }
1471
- end
1472
-
1473
- def setup_logging(builder)
1474
- if logging?
1475
- setup_common_logger(builder)
1476
- setup_custom_logger(builder)
1477
- elsif logging == false
1478
- setup_null_logger(builder)
1479
- end
1480
- end
1481
-
1482
- def setup_null_logger(builder)
1483
- builder.use Rack::NullLogger
1484
- end
1485
-
1486
- def setup_common_logger(builder)
1487
- builder.use Sinatra::CommonLogger
1488
- end
1489
-
1490
- def setup_custom_logger(builder)
1491
- if logging.respond_to? :to_int
1492
- builder.use Rack::Logger, logging
1493
- else
1494
- builder.use Rack::Logger
1495
- end
1496
- end
1497
-
1498
- def setup_protection(builder)
1499
- return unless protection?
1500
- options = Hash === protection ? protection.dup : {}
1501
- options[:except] = Array options[:except]
1502
- options[:except] += [:session_hijacking, :remote_token] unless sessions?
1503
- options[:reaction] ||= :drop_session
1504
- builder.use Rack::Protection, options
1505
- end
1506
-
1507
- def setup_sessions(builder)
1508
- return unless sessions?
1509
- options = {}
1510
- options[:secret] = session_secret if session_secret?
1511
- options.merge! sessions.to_hash if sessions.respond_to? :to_hash
1512
- builder.use Rack::Session::Cookie, options
1513
- end
1514
-
1515
- def detect_rack_handler
1516
- servers = Array(server)
1517
- servers.each do |server_name|
1518
- begin
1519
- return Rack::Handler.get(server_name.to_s)
1520
- rescue LoadError, NameError
1521
- end
1522
- end
1523
- fail "Server handler (#{servers.join(',')}) not found."
1524
- end
1525
-
1526
- def inherited(subclass)
1527
- subclass.reset!
1528
- subclass.set :app_file, caller_files.first unless subclass.app_file?
1529
- super
1530
- end
1531
-
1532
- @@mutex = Mutex.new
1533
- def synchronize(&block)
1534
- if lock?
1535
- @@mutex.synchronize(&block)
1536
- else
1537
- yield
1538
- end
1539
- end
1540
-
1541
- public
1542
- CALLERS_TO_IGNORE = [ # :nodoc:
1543
- /\/sinatra(\/(base|main|showexceptions))?\.rb$/, # all sinatra code
1544
- /lib\/tilt.*\.rb$/, # all tilt code
1545
- /^\(.*\)$/, # generated code
1546
- /rubygems\/custom_require\.rb$/, # rubygems require hacks
1547
- /active_support/, # active_support require hacks
1548
- /bundler(\/runtime)?\.rb/, # bundler require hacks
1549
- /<internal:/, # internal in ruby >= 1.9.2
1550
- /src\/kernel\/bootstrap\/[A-Z]/ # maglev kernel files
1551
- ]
1552
-
1553
- # contrary to what the comment said previously, rubinius never supported this
1554
- if defined?(RUBY_IGNORE_CALLERS)
1555
- warn "RUBY_IGNORE_CALLERS is deprecated and will no longer be supported by Sinatra 2.0"
1556
- CALLERS_TO_IGNORE.concat(RUBY_IGNORE_CALLERS)
1557
- end
1558
-
1559
- # Like Kernel#caller but excluding certain magic entries and without
1560
- # line / method information; the resulting array contains filenames only.
1561
- def caller_files
1562
- cleaned_caller(1).flatten
1563
- end
1564
-
1565
- # Like caller_files, but containing Arrays rather than strings with the
1566
- # first element being the file, and the second being the line.
1567
- def caller_locations
1568
- cleaned_caller 2
1569
- end
1570
-
1571
- private
1572
- # used for deprecation warnings
1573
- def warn(message)
1574
- super message + "\n\tfrom #{cleaned_caller.first.join(':')}"
1575
- end
1576
-
1577
- # Like Kernel#caller but excluding certain magic entries
1578
- def cleaned_caller(keep = 3)
1579
- caller(1).
1580
- map { |line| line.split(/:(?=\d|in )/, 3)[0,keep] }.
1581
- reject { |file, *_| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } }
1582
- end
1583
- end
1584
-
1585
- # Fixes encoding issues by
1586
- # * defaulting to UTF-8
1587
- # * casting params to Encoding.default_external
1588
- #
1589
- # The latter might not be necessary if Rack handles it one day.
1590
- # Keep an eye on Rack's LH #100.
1591
- def force_encoding(*args) settings.force_encoding(*args) end
1592
- if defined? Encoding
1593
- def self.force_encoding(data, encoding = default_encoding)
1594
- return if data == settings || data.is_a?(Tempfile)
1595
- if data.respond_to? :force_encoding
1596
- data.force_encoding(encoding).encode!
1597
- elsif data.respond_to? :each_value
1598
- data.each_value { |v| force_encoding(v, encoding) }
1599
- elsif data.respond_to? :each
1600
- data.each { |v| force_encoding(v, encoding) }
1601
- end
1602
- data
1603
- end
1604
- else
1605
- def self.force_encoding(data, *) data end
1606
- end
1607
-
1608
- reset!
1609
-
1610
- set :environment, (ENV['RACK_ENV'] || :development).to_sym
1611
- set :raise_errors, Proc.new { test? }
1612
- set :dump_errors, Proc.new { !test? }
1613
- set :show_exceptions, Proc.new { development? }
1614
- set :sessions, false
1615
- set :logging, false
1616
- set :protection, true
1617
- set :method_override, false
1618
- set :use_code, false
1619
- set :default_encoding, "utf-8"
1620
- set :add_charset, %w[javascript xml xhtml+xml json].map { |t| "application/#{t}" }
1621
- settings.add_charset << /^text\//
1622
-
1623
- # explicitly generating a session secret eagerly to play nice with preforking
1624
- begin
1625
- require 'securerandom'
1626
- set :session_secret, SecureRandom.hex(64)
1627
- rescue LoadError, NotImplementedError
1628
- # SecureRandom raises a NotImplementedError if no random device is available
1629
- set :session_secret, "%064x" % Kernel.rand(2**256-1)
1630
- end
1631
-
1632
- class << self
1633
- alias_method :methodoverride?, :method_override?
1634
- alias_method :methodoverride=, :method_override=
1635
- end
1636
-
1637
- set :run, false # start server via at-exit hook?
1638
- set :running, false # is the built-in server running now?
1639
- set :server, %w[http webrick]
1640
- set :bind, '0.0.0.0'
1641
- set :port, Integer(ENV['PORT'] || 4567)
1642
-
1643
- ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
1644
-
1645
- if ruby_engine == 'macruby'
1646
- server.unshift 'control_tower'
1647
- else
1648
- server.unshift 'mongrel' if ruby_engine.nil?
1649
- server.unshift 'puma' if ruby_engine != 'rbx'
1650
- server.unshift 'thin' if ruby_engine != 'jruby'
1651
- server.unshift 'puma' if ruby_engine == 'rbx'
1652
- server.unshift 'trinidad' if ruby_engine =='jruby'
1653
- end
1654
-
1655
- set :absolute_redirects, true
1656
- set :prefixed_redirects, false
1657
- set :empty_path_info, nil
1658
-
1659
- set :app_file, nil
1660
- set :root, Proc.new { app_file && File.expand_path(File.dirname(app_file)) }
1661
- set :views, Proc.new { root && File.join(root, 'views') }
1662
- set :reload_templates, Proc.new { development? }
1663
- set :lock, false
1664
- set :threaded, true
1665
-
1666
- set :public_folder, Proc.new { root && File.join(root, 'public') }
1667
- set :static, Proc.new { public_folder && File.exist?(public_folder) }
1668
- set :static_cache_control, false
1669
-
1670
- error ::Exception do
1671
- response.status = 500
1672
- content_type 'text/html'
1673
- '<h1>Internal Server Error</h1>'
1674
- end
1675
-
1676
- configure :development do
1677
- get '/__sinatra__/:image.png' do
1678
- filename = File.dirname(__FILE__) + "/images/#{params[:image]}.png"
1679
- content_type :png
1680
- send_file filename
1681
- end
1682
-
1683
- error NotFound do
1684
- content_type 'text/html'
1685
-
1686
- if self.class == Sinatra::Application
1687
- code = <<-RUBY.gsub(/^ {12}/, '')
1688
- #{request.request_method.downcase} '#{request.path_info}' do
1689
- "Hello World"
1690
- end
1691
- RUBY
1692
- else
1693
- code = <<-RUBY.gsub(/^ {12}/, '')
1694
- class #{self.class}
1695
- #{request.request_method.downcase} '#{request.path_info}' do
1696
- "Hello World"
1697
- end
1698
- end
1699
- RUBY
1700
-
1701
- file = settings.app_file.to_s.sub(settings.root.to_s, '').sub(/^\//, '')
1702
- code = "# in #{file}\n#{code}" unless file.empty?
1703
- end
1704
-
1705
- (<<-HTML).gsub(/^ {10}/, '')
1706
- <!DOCTYPE html>
1707
- <html>
1708
- <head>
1709
- <style type="text/css">
1710
- body { text-align:center;font-family:helvetica,arial;font-size:22px;
1711
- color:#888;margin:20px}
1712
- #c {margin:0 auto;width:500px;text-align:left}
1713
- </style>
1714
- </head>
1715
- <body>
1716
- <h2>Sinatra doesn&rsquo;t know this ditty.</h2>
1717
- <img src='#{uri "/__sinatra__/404.png"}'>
1718
- <div id="c">
1719
- Try this:
1720
- <pre>#{code}</pre>
1721
- </div>
1722
- </body>
1723
- </html>
1724
- HTML
1725
- end
1726
- end
1727
- end
1728
-
1729
- # Execution context for classic style (top-level) applications. All
1730
- # DSL methods executed on main are delegated to this class.
1731
- #
1732
- # The Application class should not be subclassed, unless you want to
1733
- # inherit all settings, routes, handlers, and error pages from the
1734
- # top-level. Subclassing Sinatra::Base is highly recommended for
1735
- # modular applications.
1736
- class Application < Base
1737
- set :logging, Proc.new { ! test? }
1738
- set :method_override, true
1739
- set :run, Proc.new { ! test? }
1740
- set :session_secret, Proc.new { super() unless development? }
1741
- set :app_file, nil
1742
-
1743
- def self.register(*extensions, &block) #:nodoc:
1744
- added_methods = extensions.map {|m| m.public_instance_methods }.flatten
1745
- Delegator.delegate(*added_methods)
1746
- super(*extensions, &block)
1747
- end
1748
- end
1749
-
1750
- # Sinatra delegation mixin. Mixing this module into an object causes all
1751
- # methods to be delegated to the Sinatra::Application class. Used primarily
1752
- # at the top-level.
1753
- module Delegator #:nodoc:
1754
- def self.delegate(*methods)
1755
- methods.each do |method_name|
1756
- define_method(method_name) do |*args, &block|
1757
- return super(*args, &block) if respond_to? method_name
1758
- Delegator.target.send(method_name, *args, &block)
1759
- end
1760
- private method_name
1761
- end
1762
- end
1763
-
1764
- delegate :get, :patch, :put, :post, :delete, :head, :options, :template, :layout,
1765
- :before, :after, :error, :not_found, :configure, :set, :mime_type,
1766
- :enable, :disable, :use, :development?, :test?, :production?,
1767
- :helpers, :settings, :register
1768
-
1769
- class << self
1770
- attr_accessor :target
1771
- end
1772
-
1773
- self.target = Application
1774
- end
1775
-
1776
- class Wrapper
1777
- def initialize(stack, instance)
1778
- @stack, @instance = stack, instance
1779
- end
1780
-
1781
- def settings
1782
- @instance.settings
1783
- end
1784
-
1785
- def helpers
1786
- @instance
1787
- end
1788
-
1789
- def call(env)
1790
- @stack.call(env)
1791
- end
1792
-
1793
- def inspect
1794
- "#<#{@instance.class} app_file=#{settings.app_file.inspect}>"
1795
- end
1796
- end
1797
-
1798
- # Create a new Sinatra application. The block is evaluated in the new app's
1799
- # class scope.
1800
- def self.new(base=Base, options={}, &block)
1801
- base = Class.new(base)
1802
- base.class_eval(&block) if block_given?
1803
- base
1804
- end
1805
-
1806
- # Extend the top-level DSL with the modules provided.
1807
- def self.register(*extensions, &block)
1808
- Delegator.target.register(*extensions, &block)
1809
- end
1810
-
1811
- # Include the helper modules provided in Sinatra's request context.
1812
- def self.helpers(*extensions, &block)
1813
- Delegator.target.helpers(*extensions, &block)
1814
- end
1815
-
1816
- # Use the middleware for classic applications.
1817
- def self.use(*args, &block)
1818
- Delegator.target.use(*args, &block)
1819
- end
1820
- end