5 def dslify_writter(*syms)
9 self.send "#{sym}=", v if v
20 Result = Struct.new(:block, :params)
23 @app ||= Application.new
32 URI_CHAR = '[^/?:,&#]'.freeze unless defined?(URI_CHAR)
33 PARAM = /:(#{URI_CHAR}+)/.freeze unless defined?(PARAM)
35 attr_reader :path, :block, :param_keys, :pattern
37 def initialize(path, &b)
41 regex = @path.to_s.gsub(PARAM) do
42 @param_keys << $1.intern
45 @pattern = /^#{regex}$/
49 return unless pattern =~ env['PATH_INFO'].squeeze('/')
50 params = param_keys.zip($~.captures.map(&:from_param)).to_hash
51 Result.new(block, params)
58 module ResponseHelpers
61 throw :halt, Redirect.new(path)
65 include ResponseHelpers
67 module RenderingHelpers
69 def render(content, options={})
70 template = resolve_template(content, options)
71 @content = _evaluate_render(template)
72 layout = resolve_layout(options[:layout], options)
73 @content = _evaluate_render(layout) if layout
79 def _evaluate_render(content, options={})
82 instance_eval(%Q{"#{content}"})
84 instance_eval(&content)
86 instance_eval(%Q{"#{content.read}"})
90 def resolve_template(content, options={})
95 File.new(filename_for(content, options))
99 def resolve_layout(name, options={})
100 return if name == false
101 if layout = layouts[name || :layout]
104 if File.file?(filename = filename_for(name, options))
109 def filename_for(name, options={})
110 (options[:views_directory] || 'views') + "/#{name}.#{ext}"
118 Sinatra.application.layouts
122 include RenderingHelpers
125 attr_accessor :request, :response
127 dslify_writter :status, :body
129 def initialize(request, response, route_params)
132 @route_params = route_params
137 @params ||= @route_params.merge(@request.params).symbolize_keys
144 def complete(returned)
145 @response.body || returned
150 def method_missing(name, *args, &b)
151 @response.send(name, *args, &b)
161 def to_result(cx, *args)
163 cx.header.merge!('Location' => @path)
170 attr_reader :events, :layouts
173 @events = Hash.new { |hash, key| hash[key] = [] }
177 def define_event(method, path, &b)
178 events[method] << event = Event.new(path, &b)
182 def define_layout(name=:layout, &b)
187 events[env['REQUEST_METHOD'].downcase.to_sym].eject(&[:invoke, env])
191 return [404, {}, 'Not Found'] unless result = lookup(env)
192 context = EventContext.new(
193 Rack::Request.new(env),
197 returned = catch(:halt) do
198 [:complete, context.instance_eval(&result.block)]
200 result = returned.to_result(context)
201 context.body = String === result ? [*result] : result
210 Sinatra.application.define_event(:get, path, &b)
214 Sinatra.application.define_event(:post, path, &b)
218 Sinatra.application.define_event(:put, path, &b)
222 Sinatra.application.define_event(:delete, path, &b)
226 Sinatra::EventContext.class_eval(&b)
229 def layout(name = :layout, &b)
230 Sinatra.application.define_layout(name, &b)
233 ### Misc Core Extensions
238 old_verbose, $VERBOSE = $VERBOSE, nil
241 $VERBOSE = old_verbose
248 # Converts +self+ to an escaped URI parameter value
249 # 'Foo Bar'.to_param # => 'Foo%20Bar'
254 # Converts +self+ from an escaped URI parameter value
255 # 'Foo%20Bar'.from_param # => 'Foo Bar'
265 map { |k,v| "#{k}=#{URI.escape(v)}" }.join('&')
269 self.inject({}) { |h,(k,v)| h[k.to_sym] = v; h }
273 reject { |k,v| !keys.include?(k) }
281 Proc.new { |*args| args.shift.__send__(self, *args) }
289 self.inject({}) { |h, (k, v)| h[k] = v; h }
293 Proc.new { |*args| args.shift.__send__(self[0], *(args + self[1..-1])) }
301 find { |e| result = block[e] and break result }
306 ### Core Extension results for throw :halt
309 def to_result(cx, *args)
310 cx.instance_eval(&self)
315 def to_result(cx, *args)
321 def to_result(cx, *args)
322 self.shift.to_result(cx, *self)
327 def to_result(cx, *args)
333 def to_result(cx, *args)
340 def to_result(cx, *args)