# Use the specified Rack middleware def use(middleware, *args, &block) @prototype = nil @middleware << [middleware, args, block] end def compile(path) keys = [] if path.respond_to? :to_str special_chars = %w{. + ( )} pattern = path.to_str.gsub(/((:\w+)|[\*#{special_chars.join}])/) do |match| case match when "*" keys << 'splat' "(.*?)" when *special_chars Regexp.escape(match) else keys << $2[1..-1] "([^/?&#]+)" end end [/^#{pattern}$/, keys] elsif path.respond_to?(:keys) && path.respond_to?(:match) [path, path.keys] elsif path.respond_to? :match [path, keys] else raise TypeError, path end end def compile!(verb, path, block) method_name = "#{verb} #{path}" define_method(method_name, &block) unbound_method = instance_method method_name remove_method method_name [block.arity != 0 ? proc { unbound_method.bind(self).call(*@block_params) } : proc { unbound_method.bind(self).call }, *compile(path)] end # Define a named template. The block must return the template source. def template(name, &block) filename, line = caller_locations.first templates[name] = [block, filename, line.to_i] end # Define a custom error handler. Optionally takes either an Exception # class, or an HTTP status code to specify which errors should be # handled. def error(codes=Exception, &block) Array(codes).each { |code| @errors[code] = block } end # Sugar for `error(404) { ... }` def not_found(&block) error 404, &block end # Find an custom error block for the key(s) specified. def error_block!(*keys) keys.each do |key| base = self.class while base.respond_to?(:errors) if block = base.errors[key] # found a handler, eval and return result return instance_eval(&block) else base = base.superclass end end end nil end def dump_errors!(boom) msg = ["#{boom.class} - #{boom.message}:", *boom.backtrace].join("\n ") @env['rack.errors'].puts(msg) end # Exit the current block, halts any further processing # of the request, and returns the specified response. def halt(*response) response = response.first if response.length == 1 throw :halt, response end # Pass control to the next matching route. # If there are no more matching routes, Sinatra will # return a 404 response. def pass(&block) throw :pass, block end # Forward the request to the downstream app -- middleware only. def forward fail "downstream app not set" unless @app.respond_to? :call status, headers, body = @app.call(@request.env) @response.status = status @response.body = body @response.headers.merge! headers nil end