5 require 'swiftcore/swiftiplied_mongrel'
6 puts "Using Swiftiplied Mongrel"
8 require 'swiftcore/evented_mongrel'
9 puts "Using Evented Mongrel"
16 def dslify_writter(*syms)
18 class_eval <<-end_eval
20 self.send "#{sym}=", v if v
31 Result = Struct.new(:block, :params, :status) unless defined?(Result)
34 @app ||= Application.new
42 application.options.port
46 application.options.env
50 Rack::CommonLogger.new(application)
56 puts "== Sinatra has taken the stage on port #{port} for #{env}"
58 Rack::Handler::Mongrel.run(build_application, :Port => port) do |server|
61 puts "\n== Sinatra has ended his set (crowd applauds)"
64 rescue Errno::EADDRINUSE => e
65 puts "== Someone is already performing on port #{port}!"
72 URI_CHAR = '[^/?:,&#\.]'.freeze unless defined?(URI_CHAR)
73 PARAM = /:(#{URI_CHAR}+)/.freeze unless defined?(PARAM)
75 attr_reader :path, :block, :param_keys, :pattern, :options
77 def initialize(path, options = {}, &b)
82 regex = @path.to_s.gsub(PARAM) do
83 @param_keys << $1.intern
87 regex.gsub!('*', SPLAT.to_s)
89 @pattern = /^#{regex}$/
94 return unless env['HTTP_USER_AGENT'] =~ options[:agent]
96 return unless pattern =~ env['PATH_INFO'].squeeze('/')
97 params = param_keys.zip($~.captures.map(&:from_param)).to_hash
98 Result.new(block, params, 200)
105 attr_reader :code, :block
107 def initialize(code, &b)
108 @code, @block = code, b
112 Result.new(block, {}, 404)
120 return unless File.file?(
121 Sinatra.application.options.public + env['PATH_INFO']
123 Result.new(block, {}, 200)
128 send_file Sinatra.application.options.public +
129 request.env['PATH_INFO']
135 module ResponseHelpers
138 throw :halt, Redirect.new(path)
141 def send_file(filename)
142 throw :halt, SendFile.new(filename)
147 module RenderingHelpers
149 def render(content, options={})
150 template = resolve_template(content, options)
151 @content = _evaluate_render(template)
152 layout = resolve_layout(options[:layout], options)
153 @content = _evaluate_render(layout) if layout
159 def _evaluate_render(content, options={})
162 instance_eval(%Q{"#{content}"})
164 instance_eval(&content)
166 instance_eval(%Q{"#{content.read}"})
170 def resolve_template(content, options={})
175 File.new(filename_for(content, options))
179 def resolve_layout(name, options={})
180 return if name == false
181 if layout = layouts[name || :layout]
184 if File.file?(filename = filename_for(name, options))
189 def filename_for(name, options={})
190 (options[:views_directory] || 'views') + "/#{name}.#{ext}"
198 Sinatra.application.layouts
205 include ResponseHelpers
206 include RenderingHelpers
208 attr_accessor :request, :response
210 dslify_writter :status, :body
212 def initialize(request, response, route_params)
215 @route_params = route_params
220 @params ||= @route_params.merge(@request.params).symbolize_keys
227 def complete(returned)
228 @response.body || returned
233 def method_missing(name, *args, &b)
234 @response.send(name, *args, &b)
244 def to_result(cx, *args)
246 cx.header.merge!('Location' => @path)
252 def initialize(filename)
256 def to_result(cx, *args)
257 cx.body = File.read(@filename)
263 attr_reader :events, :layouts, :default_options
265 def self.default_options
266 @@default_options ||= {
269 :env => :development,
271 :public => Dir.pwd + '/public'
276 self.class.default_options
280 @events = Hash.new { |hash, key| hash[key] = [] }
284 def define_event(method, path, options = {}, &b)
285 events[method] << event = Event.new(path, options, &b)
289 def define_layout(name=:layout, &b)
293 def define_error(code, options = {}, &b)
294 events[:errors][code] = Error.new(code, &b)
298 @static ||= Static.new
302 method = env['REQUEST_METHOD'].downcase.to_sym
303 e = static.invoke(env)
304 e ||= events[method].eject(&[:invoke, env])
305 e ||= (events[:errors][404] || basic_not_found).invoke(env)
317 '<h1>Internal Server Error</h1>'
322 @options ||= OpenStruct.new(default_options)
327 context = EventContext.new(
328 Rack::Request.new(env),
333 context.status(result.status)
334 returned = catch(:halt) do
335 [:complete, context.instance_eval(&result.block)]
337 body = returned.to_result(context)
338 context.body = String === body ? [*body] : body
341 raise e if options.raise_errors
342 env['sinatra.error'] = e
343 result = (events[:errors][500] || basic_error).invoke(env)
344 returned = catch(:halt) do
345 [:complete, context.instance_eval(&result.block)]
347 body = returned.to_result(context)
349 context.body = String === body ? [*body] : body
358 def get(path, options ={}, &b)
359 Sinatra.application.define_event(:get, path, options, &b)
362 def post(path, options ={}, &b)
363 Sinatra.application.define_event(:post, path, options, &b)
366 def put(path, options ={}, &b)
367 Sinatra.application.define_event(:put, path, options, &b)
370 def delete(path, options ={}, &b)
371 Sinatra.application.define_event(:delete, path, options, &b)
375 Sinatra::EventContext.class_eval(&b)
378 def error(code, options = {}, &b)
379 Sinatra.application.define_error(code, options, &b)
382 def layout(name = :layout, &b)
383 Sinatra.application.define_layout(name, &b)
386 def configures(*envs, &b)
387 yield if envs.include?(Sinatra.application.options.env) ||
390 alias :configure :configures
392 ### Misc Core Extensions
397 old_verbose, $VERBOSE = $VERBOSE, nil
400 $VERBOSE = old_verbose
407 # Converts +self+ to an escaped URI parameter value
408 # 'Foo Bar'.to_param # => 'Foo%20Bar'
413 # Converts +self+ from an escaped URI parameter value
414 # 'Foo%20Bar'.from_param # => 'Foo Bar'
424 map { |k,v| "#{k}=#{URI.escape(v)}" }.join('&')
428 self.inject({}) { |h,(k,v)| h[k.to_sym] = v; h }
432 reject { |k,v| !keys.include?(k) }
440 Proc.new { |*args| args.shift.__send__(self, *args) }
448 self.inject({}) { |h, (k, v)| h[k] = v; h }
452 Proc.new { |*args| args.shift.__send__(self[0], *(args + self[1..-1])) }
460 find { |e| result = block[e] and break result }
465 ### Core Extension results for throw :halt
468 def to_result(cx, *args)
469 cx.instance_eval(&self)
474 def to_result(cx, *args)
480 def to_result(cx, *args)
481 self.shift.to_result(cx, *self)
486 def to_result(cx, *args)
492 def to_result(cx, *args)
499 def to_result(cx, *args)
507 Sinatra.run if Sinatra.application.options.run
510 ENV['SINATRA_ENV'] = 'test' if $0 =~ /_test\.rb$/
511 Sinatra::Application.default_options.merge!(
512 :env => (ENV['SINATRA_ENV'] || 'development').to_sym
515 configures :development do
517 get '/sinatra_custom_images/:image.png' do
518 File.read(File.dirname(__FILE__) + "/../images/#{params[:image]}.png")
524 <body style='text-align: center; color: #888; font-family: Arial; font-size: 22px; margin: 20px'>
525 <h2>Sinatra doesn't know this diddy.</h2>
526 <img src='/sinatra_custom_images/404.png'></img>
533 @error = request.env['sinatra.error']
537 <style type="text/css" media="screen">
539 font-family: Verdana;
560 border-left: 2px solid #ddd;
569 <img src="/sinatra_custom_images/500.png" />
570 <div id="stacktrace">
571 <h1>#{@error.message}</h1>
572 <pre><code>#{@error.backtrace.join("\n")}</code></pre>