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
51 puts "== Sinatra has taken the stage on port #{port} for #{env}"
53 Rack::Handler::Mongrel.run(application, :Port => port) do |server|
56 puts "\n== Sinatra has ended his set (crowd applauds)"
59 rescue Errno::EADDRINUSE => e
60 puts "== Someone is already performing on port #{port}!"
67 URI_CHAR = '[^/?:,&#]'.freeze unless defined?(URI_CHAR)
68 PARAM = /:(#{URI_CHAR}+)/.freeze unless defined?(PARAM)
70 attr_reader :path, :block, :param_keys, :pattern
72 def initialize(path, &b)
76 regex = @path.to_s.gsub(PARAM) do
77 @param_keys << $1.intern
80 @pattern = /^#{regex}$/
84 return unless pattern =~ env['PATH_INFO'].squeeze('/')
85 params = param_keys.zip($~.captures.map(&:from_param)).to_hash
86 Result.new(block, params, 200)
93 attr_reader :code, :block
95 def initialize(code, &b)
96 @code, @block = code, b
100 Result.new(block, {}, 404)
105 module ResponseHelpers
108 throw :halt, Redirect.new(path)
113 module RenderingHelpers
115 def render(content, options={})
116 template = resolve_template(content, options)
117 @content = _evaluate_render(template)
118 layout = resolve_layout(options[:layout], options)
119 @content = _evaluate_render(layout) if layout
125 def _evaluate_render(content, options={})
128 instance_eval(%Q{"#{content}"})
130 instance_eval(&content)
132 instance_eval(%Q{"#{content.read}"})
136 def resolve_template(content, options={})
141 File.new(filename_for(content, options))
145 def resolve_layout(name, options={})
146 return if name == false
147 if layout = layouts[name || :layout]
150 if File.file?(filename = filename_for(name, options))
155 def filename_for(name, options={})
156 (options[:views_directory] || 'views') + "/#{name}.#{ext}"
164 Sinatra.application.layouts
171 include ResponseHelpers
172 include RenderingHelpers
174 attr_accessor :request, :response
176 dslify_writter :status, :body
178 def initialize(request, response, route_params)
181 @route_params = route_params
186 @params ||= @route_params.merge(@request.params).symbolize_keys
193 def complete(returned)
194 @response.body || returned
199 def method_missing(name, *args, &b)
200 @response.send(name, *args, &b)
210 def to_result(cx, *args)
212 cx.header.merge!('Location' => @path)
219 attr_reader :events, :layouts, :default_options
221 def self.default_options
222 @@default_options ||= {
230 self.class.default_options
234 @events = Hash.new { |hash, key| hash[key] = [] }
238 def define_event(method, path, &b)
239 events[method] << event = Event.new(path, &b)
243 def define_layout(name=:layout, &b)
247 def define_error(code, &b)
248 events[:errors][code] = Error.new(code, &b)
252 e = events[env['REQUEST_METHOD'].downcase.to_sym].eject(&[:invoke, env])
253 e ||= (events[:errors][404] || basic_not_found).invoke(env)
264 '<h1>Internal Server Error</h1>'
269 @options ||= OpenStruct.new(default_options)
276 context = EventContext.new(
277 Rack::Request.new(env),
281 context.status(result.status)
282 returned = catch(:halt) do
283 [:complete, context.instance_eval(&result.block)]
285 body = returned.to_result(context)
287 env['sinatra.error'] = e
288 result = (events[:errors][500] || basic_error).invoke(env)
289 returned = catch(:halt) do
290 [:complete, context.instance_eval(&result.block)]
292 body = returned.to_result(context)
295 context.body = String === body ? [*body] : body
304 Sinatra.application.define_event(:get, path, &b)
308 Sinatra.application.define_event(:post, path, &b)
312 Sinatra.application.define_event(:put, path, &b)
316 Sinatra.application.define_event(:delete, path, &b)
320 Sinatra::EventContext.class_eval(&b)
324 Sinatra.application.define_error(code, &b)
327 def layout(name = :layout, &b)
328 Sinatra.application.define_layout(name, &b)
331 ### Misc Core Extensions
336 old_verbose, $VERBOSE = $VERBOSE, nil
339 $VERBOSE = old_verbose
346 # Converts +self+ to an escaped URI parameter value
347 # 'Foo Bar'.to_param # => 'Foo%20Bar'
352 # Converts +self+ from an escaped URI parameter value
353 # 'Foo%20Bar'.from_param # => 'Foo Bar'
363 map { |k,v| "#{k}=#{URI.escape(v)}" }.join('&')
367 self.inject({}) { |h,(k,v)| h[k.to_sym] = v; h }
371 reject { |k,v| !keys.include?(k) }
379 Proc.new { |*args| args.shift.__send__(self, *args) }
387 self.inject({}) { |h, (k, v)| h[k] = v; h }
391 Proc.new { |*args| args.shift.__send__(self[0], *(args + self[1..-1])) }
399 find { |e| result = block[e] and break result }
404 ### Core Extension results for throw :halt
407 def to_result(cx, *args)
408 cx.instance_eval(&self)
413 def to_result(cx, *args)
419 def to_result(cx, *args)
420 self.shift.to_result(cx, *self)
425 def to_result(cx, *args)
431 def to_result(cx, *args)
438 def to_result(cx, *args)
446 Sinatra.run if Sinatra.application.options.run