4 require 'swiftcore/swiftiplied_mongrel'
5 puts "Using Swiftiplied Mongrel"
7 require 'swiftcore/evented_mongrel'
8 puts "Using Evented Mongrel"
15 def dslify_writter(*syms)
17 class_eval <<-end_eval
19 self.send "#{sym}=", v if v
30 Result = Struct.new(:block, :params, :status) unless defined?(Result)
33 @app ||= Application.new
41 application.options.port
45 application.options.env
49 Rack::CommonLogger.new(application)
55 puts "== Sinatra has taken the stage on port #{port} for #{env}"
57 Rack::Handler::Mongrel.run(build_application, :Port => port) do |server|
60 puts "\n== Sinatra has ended his set (crowd applauds)"
63 rescue Errno::EADDRINUSE => e
64 puts "== Someone is already performing on port #{port}!"
71 URI_CHAR = '[^/?:,&#\.]'.freeze unless defined?(URI_CHAR)
72 PARAM = /:(#{URI_CHAR}+)/.freeze unless defined?(PARAM)
74 attr_reader :path, :block, :param_keys, :pattern, :options
76 def initialize(path, options = {}, &b)
81 regex = @path.to_s.gsub(PARAM) do
82 @param_keys << $1.intern
86 regex.gsub!('*', SPLAT.to_s)
88 @pattern = /^#{regex}$/
92 return unless pattern =~ env['PATH_INFO'].squeeze('/')
94 return unless env['HTTP_USER_AGENT'] =~ options[:agent]
96 params = param_keys.zip($~.captures.map(&:from_param)).to_hash
97 Result.new(block, params, 200)
104 attr_reader :code, :block
106 def initialize(code, &b)
107 @code, @block = code, b
111 Result.new(block, {}, 404)
119 return unless File.file?(
120 Sinatra.application.options.public + env['PATH_INFO']
122 Result.new(block, {}, 200)
127 send_file Sinatra.application.options.public +
128 request.env['PATH_INFO']
134 module ResponseHelpers
137 throw :halt, Redirect.new(path)
140 def send_file(filename)
141 throw :halt, SendFile.new(filename)
146 module RenderingHelpers
148 def render(content, options={})
149 template = resolve_template(content, options)
150 @content = _evaluate_render(template)
151 layout = resolve_layout(options[:layout], options)
152 @content = _evaluate_render(layout) if layout
158 def _evaluate_render(content, options={})
161 instance_eval(%Q{"#{content}"})
163 instance_eval(&content)
165 instance_eval(%Q{"#{content.read}"})
169 def resolve_template(content, options={})
174 File.new(filename_for(content, options))
178 def resolve_layout(name, options={})
179 return if name == false
180 if layout = layouts[name || :layout]
183 if File.file?(filename = filename_for(name, options))
188 def filename_for(name, options={})
189 (options[:views_directory] || 'views') + "/#{name}.#{ext}"
197 Sinatra.application.layouts
204 include ResponseHelpers
205 include RenderingHelpers
207 attr_accessor :request, :response
209 dslify_writter :status, :body
211 def initialize(request, response, route_params)
214 @route_params = route_params
219 @params ||= @route_params.merge(@request.params).symbolize_keys
226 def complete(returned)
227 @response.body || returned
232 def method_missing(name, *args, &b)
233 @response.send(name, *args, &b)
243 def to_result(cx, *args)
245 cx.header.merge!('Location' => @path)
251 def initialize(filename)
255 def to_result(cx, *args)
256 cx.body = File.read(@filename)
262 attr_reader :events, :layouts, :default_options
264 def self.default_options
265 @@default_options ||= {
268 :env => :development,
270 :public => Dir.pwd + '/public'
275 self.class.default_options
279 @events = Hash.new { |hash, key| hash[key] = [] }
283 def define_event(method, path, options = {}, &b)
284 events[method] << event = Event.new(path, options, &b)
288 def define_layout(name=:layout, &b)
292 def define_error(code, options = {}, &b)
293 events[:errors][code] = Error.new(code, &b)
297 @static ||= Static.new
301 method = env['REQUEST_METHOD'].downcase.to_sym
302 e = static.invoke(env)
303 e ||= events[method].eject(&[:invoke, env])
304 e ||= (events[:errors][404] || basic_not_found).invoke(env)
316 '<h1>Internal Server Error</h1>'
321 @options ||= OpenStruct.new(default_options)
326 context = EventContext.new(
327 Rack::Request.new(env),
332 context.status(result.status)
333 returned = catch(:halt) do
334 [:complete, context.instance_eval(&result.block)]
336 body = returned.to_result(context)
337 context.body = String === body ? [*body] : body
340 raise e if options.raise_errors
341 env['sinatra.error'] = e
342 result = (events[:errors][500] || basic_error).invoke(env)
343 returned = catch(:halt) do
344 [:complete, context.instance_eval(&result.block)]
346 body = returned.to_result(context)
348 context.body = String === body ? [*body] : body
357 def get(path, options ={}, &b)
358 Sinatra.application.define_event(:get, path, options, &b)
361 def post(path, options ={}, &b)
362 Sinatra.application.define_event(:post, path, options, &b)
365 def put(path, options ={}, &b)
366 Sinatra.application.define_event(:put, path, options, &b)
369 def delete(path, options ={}, &b)
370 Sinatra.application.define_event(:delete, path, options, &b)
374 Sinatra::EventContext.class_eval(&b)
377 def error(code, options = {}, &b)
378 Sinatra.application.define_error(code, options, &b)
381 def layout(name = :layout, &b)
382 Sinatra.application.define_layout(name, &b)
385 def configures(*envs, &b)
386 yield if envs.include?(Sinatra.application.options.env) ||
389 alias :configure :configures
391 ### Misc Core Extensions
396 old_verbose, $VERBOSE = $VERBOSE, nil
399 $VERBOSE = old_verbose
406 # Converts +self+ to an escaped URI parameter value
407 # 'Foo Bar'.to_param # => 'Foo%20Bar'
412 # Converts +self+ from an escaped URI parameter value
413 # 'Foo%20Bar'.from_param # => 'Foo Bar'
423 map { |k,v| "#{k}=#{URI.escape(v)}" }.join('&')
427 self.inject({}) { |h,(k,v)| h[k.to_sym] = v; h }
431 reject { |k,v| !keys.include?(k) }
439 Proc.new { |*args| args.shift.__send__(self, *args) }
447 self.inject({}) { |h, (k, v)| h[k] = v; h }
451 Proc.new { |*args| args.shift.__send__(self[0], *(args + self[1..-1])) }
459 find { |e| result = block[e] and break result }
464 ### Core Extension results for throw :halt
467 def to_result(cx, *args)
468 cx.instance_eval(&self)
473 def to_result(cx, *args)
479 def to_result(cx, *args)
480 self.shift.to_result(cx, *self)
485 def to_result(cx, *args)
491 def to_result(cx, *args)
498 def to_result(cx, *args)
506 Sinatra.run if Sinatra.application.options.run
509 ENV['SINATRA_ENV'] = 'test' if $0 =~ /_test\.rb$/
510 Sinatra::Application.default_options.merge!(
511 :env => (ENV['SINATRA_ENV'] || 'development').to_sym
514 configures :development do
516 get '/sinatra_custom_images/:image.png' do
517 File.read(File.dirname(__FILE__) + "/../images/#{params[:image]}.png")
523 <body style='text-align: center; color: #888; font-family: Arial; font-size: 22px; margin: 20px'>
524 <h2>Sinatra doesn't know this diddy.</h2>
525 <img src='/sinatra_custom_images/404.png'></img>
532 @error = request.env['sinatra.error']
536 <style type="text/css" media="screen">
538 font-family: Verdana;
559 border-left: 2px solid #ddd;
568 <img src="/sinatra_custom_images/500.png" />
569 <div id="stacktrace">
570 <h1>#{@error.message}</h1>
571 <pre><code>#{@error.backtrace.join("\n")}</code></pre>