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
76 def initialize(path, &b)
80 regex = @path.to_s.gsub(PARAM) do
81 @param_keys << $1.intern
84 @pattern = /^#{regex}$/
88 return unless pattern =~ env['PATH_INFO'].squeeze('/')
89 params = param_keys.zip($~.captures.map(&:from_param)).to_hash
90 Result.new(block, params, 200)
97 attr_reader :code, :block
99 def initialize(code, &b)
100 @code, @block = code, b
104 Result.new(block, {}, 404)
112 return unless File.file?(
113 Sinatra.application.options.public + env['PATH_INFO']
115 Result.new(block, {}, 200)
120 send_file Sinatra.application.options.public +
121 request.env['PATH_INFO']
127 module ResponseHelpers
130 throw :halt, Redirect.new(path)
133 def send_file(filename)
134 throw :halt, SendFile.new(filename)
139 module RenderingHelpers
141 def render(content, options={})
142 template = resolve_template(content, options)
143 @content = _evaluate_render(template)
144 layout = resolve_layout(options[:layout], options)
145 @content = _evaluate_render(layout) if layout
151 def _evaluate_render(content, options={})
154 instance_eval(%Q{"#{content}"})
156 instance_eval(&content)
158 instance_eval(%Q{"#{content.read}"})
162 def resolve_template(content, options={})
167 File.new(filename_for(content, options))
171 def resolve_layout(name, options={})
172 return if name == false
173 if layout = layouts[name || :layout]
176 if File.file?(filename = filename_for(name, options))
181 def filename_for(name, options={})
182 (options[:views_directory] || 'views') + "/#{name}.#{ext}"
190 Sinatra.application.layouts
197 include ResponseHelpers
198 include RenderingHelpers
200 attr_accessor :request, :response
202 dslify_writter :status, :body
204 def initialize(request, response, route_params)
207 @route_params = route_params
212 @params ||= @route_params.merge(@request.params).symbolize_keys
219 def complete(returned)
220 @response.body || returned
225 def method_missing(name, *args, &b)
226 @response.send(name, *args, &b)
236 def to_result(cx, *args)
238 cx.header.merge!('Location' => @path)
244 def initialize(filename)
248 def to_result(cx, *args)
249 cx.body = File.read(@filename)
255 attr_reader :events, :layouts, :default_options
257 def self.default_options
258 @@default_options ||= {
261 :env => :development,
263 :public => Dir.pwd + '/public'
268 self.class.default_options
272 @events = Hash.new { |hash, key| hash[key] = [] }
276 def define_event(method, path, &b)
277 events[method] << event = Event.new(path, &b)
281 def define_layout(name=:layout, &b)
285 def define_error(code, &b)
286 events[:errors][code] = Error.new(code, &b)
290 @static ||= Static.new
294 method = env['REQUEST_METHOD'].downcase.to_sym
295 e = static.invoke(env)
296 e ||= events[method].eject(&[:invoke, env])
297 e ||= (events[:errors][404] || basic_not_found).invoke(env)
309 '<h1>Internal Server Error</h1>'
314 @options ||= OpenStruct.new(default_options)
319 context = EventContext.new(
320 Rack::Request.new(env),
325 context.status(result.status)
326 returned = catch(:halt) do
327 [:complete, context.instance_eval(&result.block)]
329 body = returned.to_result(context)
331 raise e if options.env == :test
332 env['sinatra.error'] = e
333 result = (events[:errors][500] || basic_error).invoke(env)
334 returned = catch(:halt) do
335 [:complete, context.instance_eval(&result.block)]
337 body = returned.to_result(context)
340 context.body = String === body ? [*body] : body
349 Sinatra.application.define_event(:get, path, &b)
353 Sinatra.application.define_event(:post, path, &b)
357 Sinatra.application.define_event(:put, path, &b)
361 Sinatra.application.define_event(:delete, path, &b)
365 Sinatra::EventContext.class_eval(&b)
369 Sinatra.application.define_error(code, &b)
372 def layout(name = :layout, &b)
373 Sinatra.application.define_layout(name, &b)
376 def configures(*envs, &b)
377 yield if envs.include?(Sinatra.application.options.env) ||
380 alias :configure :configures
382 ### Misc Core Extensions
387 old_verbose, $VERBOSE = $VERBOSE, nil
390 $VERBOSE = old_verbose
397 # Converts +self+ to an escaped URI parameter value
398 # 'Foo Bar'.to_param # => 'Foo%20Bar'
403 # Converts +self+ from an escaped URI parameter value
404 # 'Foo%20Bar'.from_param # => 'Foo Bar'
414 map { |k,v| "#{k}=#{URI.escape(v)}" }.join('&')
418 self.inject({}) { |h,(k,v)| h[k.to_sym] = v; h }
422 reject { |k,v| !keys.include?(k) }
430 Proc.new { |*args| args.shift.__send__(self, *args) }
438 self.inject({}) { |h, (k, v)| h[k] = v; h }
442 Proc.new { |*args| args.shift.__send__(self[0], *(args + self[1..-1])) }
450 find { |e| result = block[e] and break result }
455 ### Core Extension results for throw :halt
458 def to_result(cx, *args)
459 cx.instance_eval(&self)
464 def to_result(cx, *args)
470 def to_result(cx, *args)
471 self.shift.to_result(cx, *self)
476 def to_result(cx, *args)
482 def to_result(cx, *args)
489 def to_result(cx, *args)
497 Sinatra.run if Sinatra.application.options.run
500 ENV['SINATRA_ENV'] = 'test' if $0 =~ /_test\.rb$/
501 Sinatra::Application.default_options.merge!(
502 :env => (ENV['SINATRA_ENV'] || 'development').to_sym
505 configures :development do
507 get '/sinatra_custom_images/:image.png' do
508 File.read(File.dirname(__FILE__) + "/../images/#{params[:image]}.png")
514 <body style='text-align: center; color: #888; font-family: Arial; font-size: 22px; margin: 20px'>
515 <h2>Sinatra doesn't know this diddy.</h2>
516 <img src='/sinatra_custom_images/404.png'></img>
523 @error = request.env['sinatra.error']
527 <style type="text/css" media="screen">
529 font-family: Verdana;
550 border-left: 2px solid #ddd;
559 <img src="/sinatra_custom_images/500.png" />
560 <div id="stacktrace">
561 <h1>#{@error.message}</h1>
562 <pre><code>#{@error.backtrace.join("\n")}</code></pre>