mirror of
				https://github.com/KevinMidboe/linguist.git
				synced 2025-10-29 17:50:22 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1776 lines
		
	
	
		
			57 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			1776 lines
		
	
	
		
			57 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
# external dependencies
 | 
						|
require 'rack'
 | 
						|
require 'tilt'
 | 
						|
require "rack/protection"
 | 
						|
 | 
						|
# stdlib dependencies
 | 
						|
require 'thread'
 | 
						|
require 'time'
 | 
						|
require 'uri'
 | 
						|
 | 
						|
# other files we need
 | 
						|
require 'sinatra/showexceptions'
 | 
						|
require 'sinatra/version'
 | 
						|
 | 
						|
module Sinatra
 | 
						|
  # The request object. See Rack::Request for more info:
 | 
						|
  # http://rack.rubyforge.org/doc/classes/Rack/Request.html
 | 
						|
  class Request < Rack::Request
 | 
						|
    # Returns an array of acceptable media types for the response
 | 
						|
    def accept
 | 
						|
      @env['sinatra.accept'] ||= begin
 | 
						|
        entries = @env['HTTP_ACCEPT'].to_s.split(',')
 | 
						|
        entries.map { |e| accept_entry(e) }.sort_by(&:last).map(&:first)
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    def preferred_type(*types)
 | 
						|
      return accept.first if types.empty?
 | 
						|
      types.flatten!
 | 
						|
      accept.detect do |pattern|
 | 
						|
        type = types.detect { |t| File.fnmatch(pattern, t) }
 | 
						|
        return type if type
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    alias accept? preferred_type
 | 
						|
    alias secure? ssl?
 | 
						|
 | 
						|
    def forwarded?
 | 
						|
      @env.include? "HTTP_X_FORWARDED_HOST"
 | 
						|
    end
 | 
						|
 | 
						|
    def safe?
 | 
						|
      get? or head? or options? or trace?
 | 
						|
    end
 | 
						|
 | 
						|
    def idempotent?
 | 
						|
      safe? or put? or delete?
 | 
						|
    end
 | 
						|
 | 
						|
    private
 | 
						|
 | 
						|
    def accept_entry(entry)
 | 
						|
      type, *options = entry.delete(' ').split(';')
 | 
						|
      quality = 0 # we sort smallest first
 | 
						|
      options.delete_if { |e| quality = 1 - e[2..-1].to_f if e.start_with? 'q=' }
 | 
						|
      [type, [quality, type.count('*'), 1 - options.size]]
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  # The response object. See Rack::Response and Rack::ResponseHelpers for
 | 
						|
  # more info:
 | 
						|
  # http://rack.rubyforge.org/doc/classes/Rack/Response.html
 | 
						|
  # http://rack.rubyforge.org/doc/classes/Rack/Response/Helpers.html
 | 
						|
  class Response < Rack::Response
 | 
						|
    def body=(value)
 | 
						|
      value = value.body while Rack::Response === value
 | 
						|
      @body = String === value ? [value.to_str] : value
 | 
						|
    end
 | 
						|
 | 
						|
    def each
 | 
						|
      block_given? ? super : enum_for(:each)
 | 
						|
    end
 | 
						|
 | 
						|
    def finish
 | 
						|
      if status.to_i / 100 == 1
 | 
						|
        headers.delete "Content-Length"
 | 
						|
        headers.delete "Content-Type"
 | 
						|
      elsif Array === body and not [204, 304].include?(status.to_i)
 | 
						|
        # if some other code has already set Content-Length, don't muck with it
 | 
						|
        # currently, this would be the static file-handler
 | 
						|
        headers["Content-Length"] ||= body.inject(0) { |l, p| l + Rack::Utils.bytesize(p) }.to_s
 | 
						|
      end
 | 
						|
 | 
						|
      # Rack::Response#finish sometimes returns self as response body. We don't want that.
 | 
						|
      status, headers, result = super
 | 
						|
      result = body if result == self
 | 
						|
      [status, headers, result]
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  # Some Rack handlers (Thin, Rainbows!) implement an extended body object protocol, however,
 | 
						|
  # some middleware (namely Rack::Lint) will break it by not mirroring the methods in question.
 | 
						|
  # This middleware will detect an extended body object and will make sure it reaches the
 | 
						|
  # handler directly. We do this here, so our middleware and middleware set up by the app will
 | 
						|
  # still be able to run.
 | 
						|
  class ExtendedRack < Struct.new(:app)
 | 
						|
    def call(env)
 | 
						|
      result, callback = app.call(env), env['async.callback']
 | 
						|
      return result unless callback and async?(*result)
 | 
						|
      after_response { callback.call result }
 | 
						|
      setup_close(env, *result)
 | 
						|
      throw :async
 | 
						|
    end
 | 
						|
 | 
						|
    private
 | 
						|
 | 
						|
    def setup_close(env, status, header, body)
 | 
						|
      return unless body.respond_to? :close and env.include? 'async.close'
 | 
						|
      env['async.close'].callback { body.close }
 | 
						|
      env['async.close'].errback { body.close }
 | 
						|
    end
 | 
						|
 | 
						|
    def after_response(&block)
 | 
						|
      raise NotImplementedError, "only supports EventMachine at the moment" unless defined? EventMachine
 | 
						|
      EventMachine.next_tick(&block)
 | 
						|
    end
 | 
						|
 | 
						|
    def async?(status, headers, body)
 | 
						|
      return true if status == -1
 | 
						|
      body.respond_to? :callback and body.respond_to? :errback
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  # Behaves exactly like Rack::CommonLogger with the notable exception that it does nothing,
 | 
						|
  # if another CommonLogger is already in the middleware chane.
 | 
						|
  class CommonLogger < Rack::CommonLogger
 | 
						|
    def call(env)
 | 
						|
      env['sinatra.commonlogger'] ? @app.call(env) : super
 | 
						|
    end
 | 
						|
 | 
						|
    superclass.class_eval do
 | 
						|
      alias call_without_check call unless method_defined? :call_without_check
 | 
						|
      def call(env)
 | 
						|
        env['sinatra.commonlogger'] = true
 | 
						|
        call_without_check(env)
 | 
						|
      end
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  class NotFound < NameError #:nodoc:
 | 
						|
    def http_status; 404 end
 | 
						|
  end
 | 
						|
 | 
						|
  # Methods available to routes, before/after filters, and views.
 | 
						|
  module Helpers
 | 
						|
    # Set or retrieve the response status code.
 | 
						|
    def status(value=nil)
 | 
						|
      response.status = value if value
 | 
						|
      response.status
 | 
						|
    end
 | 
						|
 | 
						|
    # Set or retrieve the response body. When a block is given,
 | 
						|
    # evaluation is deferred until the body is read with #each.
 | 
						|
    def body(value=nil, &block)
 | 
						|
      if block_given?
 | 
						|
        def block.each; yield(call) end
 | 
						|
        response.body = block
 | 
						|
      elsif value
 | 
						|
        response.body = value
 | 
						|
      else
 | 
						|
        response.body
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    # Halt processing and redirect to the URI provided.
 | 
						|
    def redirect(uri, *args)
 | 
						|
      if env['HTTP_VERSION'] == 'HTTP/1.1' and env["REQUEST_METHOD"] != 'GET'
 | 
						|
        status 303
 | 
						|
      else
 | 
						|
        status 302
 | 
						|
      end
 | 
						|
 | 
						|
      # According to RFC 2616 section 14.30, "the field value consists of a
 | 
						|
      # single absolute URI"
 | 
						|
      response['Location'] = uri(uri, settings.absolute_redirects?, settings.prefixed_redirects?)
 | 
						|
      halt(*args)
 | 
						|
    end
 | 
						|
 | 
						|
    # Generates the absolute URI for a given path in the app.
 | 
						|
    # Takes Rack routers and reverse proxies into account.
 | 
						|
    def uri(addr = nil, absolute = true, add_script_name = true)
 | 
						|
      return addr if addr =~ /\A[A-z][A-z0-9\+\.\-]*:/
 | 
						|
      uri = [host = ""]
 | 
						|
      if absolute
 | 
						|
        host << "http#{'s' if request.secure?}://"
 | 
						|
        if request.forwarded? or request.port != (request.secure? ? 443 : 80)
 | 
						|
          host << request.host_with_port
 | 
						|
        else
 | 
						|
          host << request.host
 | 
						|
        end
 | 
						|
      end
 | 
						|
      uri << request.script_name.to_s if add_script_name
 | 
						|
      uri << (addr ? addr : request.path_info).to_s
 | 
						|
      File.join uri
 | 
						|
    end
 | 
						|
 | 
						|
    alias url uri
 | 
						|
    alias to uri
 | 
						|
 | 
						|
    # Halt processing and return the error status provided.
 | 
						|
    def error(code, body=nil)
 | 
						|
      code, body    = 500, code.to_str if code.respond_to? :to_str
 | 
						|
      response.body = body unless body.nil?
 | 
						|
      halt code
 | 
						|
    end
 | 
						|
 | 
						|
    # Halt processing and return a 404 Not Found.
 | 
						|
    def not_found(body=nil)
 | 
						|
      error 404, body
 | 
						|
    end
 | 
						|
 | 
						|
    # Set multiple response headers with Hash.
 | 
						|
    def headers(hash=nil)
 | 
						|
      response.headers.merge! hash if hash
 | 
						|
      response.headers
 | 
						|
    end
 | 
						|
 | 
						|
    # Access the underlying Rack session.
 | 
						|
    def session
 | 
						|
      request.session
 | 
						|
    end
 | 
						|
 | 
						|
    # Access shared logger object.
 | 
						|
    def logger
 | 
						|
      request.logger
 | 
						|
    end
 | 
						|
 | 
						|
    # Look up a media type by file extension in Rack's mime registry.
 | 
						|
    def mime_type(type)
 | 
						|
      Base.mime_type(type)
 | 
						|
    end
 | 
						|
 | 
						|
    # Set the Content-Type of the response body given a media type or file
 | 
						|
    # extension.
 | 
						|
    def content_type(type = nil, params={})
 | 
						|
      return response['Content-Type'] unless type
 | 
						|
      default = params.delete :default
 | 
						|
      mime_type = mime_type(type) || default
 | 
						|
      fail "Unknown media type: %p" % type if mime_type.nil?
 | 
						|
      mime_type = mime_type.dup
 | 
						|
      unless params.include? :charset or settings.add_charset.all? { |p| not p === mime_type }
 | 
						|
        params[:charset] = params.delete('charset') || settings.default_encoding
 | 
						|
      end
 | 
						|
      params.delete :charset if mime_type.include? 'charset'
 | 
						|
      unless params.empty?
 | 
						|
        mime_type << (mime_type.include?(';') ? ', ' : ';')
 | 
						|
        mime_type << params.map { |kv| kv.join('=') }.join(', ')
 | 
						|
      end
 | 
						|
      response['Content-Type'] = mime_type
 | 
						|
    end
 | 
						|
 | 
						|
    # Set the Content-Disposition to "attachment" with the specified filename,
 | 
						|
    # instructing the user agents to prompt to save.
 | 
						|
    def attachment(filename=nil)
 | 
						|
      response['Content-Disposition'] = 'attachment'
 | 
						|
      if filename
 | 
						|
        params = '; filename="%s"' % File.basename(filename)
 | 
						|
        response['Content-Disposition'] << params
 | 
						|
        ext = File.extname(filename)
 | 
						|
        content_type(ext) unless response['Content-Type'] or ext.empty?
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    # Use the contents of the file at +path+ as the response body.
 | 
						|
    def send_file(path, opts={})
 | 
						|
      if opts[:type] or not response['Content-Type']
 | 
						|
        content_type opts[:type] || File.extname(path), :default => 'application/octet-stream'
 | 
						|
      end
 | 
						|
 | 
						|
      if opts[:disposition] == 'attachment' || opts[:filename]
 | 
						|
        attachment opts[:filename] || path
 | 
						|
      elsif opts[:disposition] == 'inline'
 | 
						|
        response['Content-Disposition'] = 'inline'
 | 
						|
      end
 | 
						|
 | 
						|
      last_modified opts[:last_modified] if opts[:last_modified]
 | 
						|
 | 
						|
      file      = Rack::File.new nil
 | 
						|
      file.path = path
 | 
						|
      result    = file.serving env
 | 
						|
      result[1].each { |k,v| headers[k] ||= v }
 | 
						|
      headers['Content-Length'] = result[1]['Content-Length']
 | 
						|
      halt opts[:status] || result[0], result[2]
 | 
						|
    rescue Errno::ENOENT
 | 
						|
      not_found
 | 
						|
    end
 | 
						|
 | 
						|
    # Class of the response body in case you use #stream.
 | 
						|
    #
 | 
						|
    # Three things really matter: The front and back block (back being the
 | 
						|
    # blog generating content, front the one sending it to the client) and
 | 
						|
    # the scheduler, integrating with whatever concurrency feature the Rack
 | 
						|
    # handler is using.
 | 
						|
    #
 | 
						|
    # Scheduler has to respond to defer and schedule.
 | 
						|
    class Stream
 | 
						|
      def self.schedule(*) yield end
 | 
						|
      def self.defer(*)    yield end
 | 
						|
 | 
						|
      def initialize(scheduler = self.class, keep_open = false, &back)
 | 
						|
        @back, @scheduler, @keep_open = back.to_proc, scheduler, keep_open
 | 
						|
        @callbacks, @closed = [], false
 | 
						|
      end
 | 
						|
 | 
						|
      def close
 | 
						|
        return if @closed
 | 
						|
        @closed = true
 | 
						|
        @scheduler.schedule { @callbacks.each { |c| c.call }}
 | 
						|
      end
 | 
						|
 | 
						|
      def each(&front)
 | 
						|
        @front = front
 | 
						|
        @scheduler.defer do
 | 
						|
          begin
 | 
						|
            @back.call(self)
 | 
						|
          rescue Exception => e
 | 
						|
            @scheduler.schedule { raise e }
 | 
						|
          end
 | 
						|
          close unless @keep_open
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      def <<(data)
 | 
						|
        @scheduler.schedule { @front.call(data.to_s) }
 | 
						|
        self
 | 
						|
      end
 | 
						|
 | 
						|
      def callback(&block)
 | 
						|
        return yield if @closed
 | 
						|
        @callbacks << block
 | 
						|
      end
 | 
						|
 | 
						|
      alias errback callback
 | 
						|
    end
 | 
						|
 | 
						|
    # Allows to start sending data to the client even though later parts of
 | 
						|
    # the response body have not yet been generated.
 | 
						|
    #
 | 
						|
    # The close parameter specifies whether Stream#close should be called
 | 
						|
    # after the block has been executed. This is only relevant for evented
 | 
						|
    # servers like Thin or Rainbows.
 | 
						|
    def stream(keep_open = false)
 | 
						|
      scheduler = env['async.callback'] ? EventMachine : Stream
 | 
						|
      current   = @params.dup
 | 
						|
      body Stream.new(scheduler, keep_open) { |out| with_params(current) { yield(out) } }
 | 
						|
    end
 | 
						|
 | 
						|
    # Specify response freshness policy for HTTP caches (Cache-Control header).
 | 
						|
    # Any number of non-value directives (:public, :private, :no_cache,
 | 
						|
    # :no_store, :must_revalidate, :proxy_revalidate) may be passed along with
 | 
						|
    # a Hash of value directives (:max_age, :min_stale, :s_max_age).
 | 
						|
    #
 | 
						|
    #   cache_control :public, :must_revalidate, :max_age => 60
 | 
						|
    #   => Cache-Control: public, must-revalidate, max-age=60
 | 
						|
    #
 | 
						|
    # See RFC 2616 / 14.9 for more on standard cache control directives:
 | 
						|
    # http://tools.ietf.org/html/rfc2616#section-14.9.1
 | 
						|
    def cache_control(*values)
 | 
						|
      if values.last.kind_of?(Hash)
 | 
						|
        hash = values.pop
 | 
						|
        hash.reject! { |k,v| v == false }
 | 
						|
        hash.reject! { |k,v| values << k if v == true }
 | 
						|
      else
 | 
						|
        hash = {}
 | 
						|
      end
 | 
						|
 | 
						|
      values.map! { |value| value.to_s.tr('_','-') }
 | 
						|
      hash.each do |key, value|
 | 
						|
        key = key.to_s.tr('_', '-')
 | 
						|
        value = value.to_i if key == "max-age"
 | 
						|
        values << [key, value].join('=')
 | 
						|
      end
 | 
						|
 | 
						|
      response['Cache-Control'] = values.join(', ') if values.any?
 | 
						|
    end
 | 
						|
 | 
						|
    # Set the Expires header and Cache-Control/max-age directive. Amount
 | 
						|
    # can be an integer number of seconds in the future or a Time object
 | 
						|
    # indicating when the response should be considered "stale". The remaining
 | 
						|
    # "values" arguments are passed to the #cache_control helper:
 | 
						|
    #
 | 
						|
    #   expires 500, :public, :must_revalidate
 | 
						|
    #   => Cache-Control: public, must-revalidate, max-age=60
 | 
						|
    #   => Expires: Mon, 08 Jun 2009 08:50:17 GMT
 | 
						|
    #
 | 
						|
    def expires(amount, *values)
 | 
						|
      values << {} unless values.last.kind_of?(Hash)
 | 
						|
 | 
						|
      if amount.is_a? Integer
 | 
						|
        time    = Time.now + amount.to_i
 | 
						|
        max_age = amount
 | 
						|
      else
 | 
						|
        time    = time_for amount
 | 
						|
        max_age = time - Time.now
 | 
						|
      end
 | 
						|
 | 
						|
      values.last.merge!(:max_age => max_age)
 | 
						|
      cache_control(*values)
 | 
						|
 | 
						|
      response['Expires'] = time.httpdate
 | 
						|
    end
 | 
						|
 | 
						|
    # Set the last modified time of the resource (HTTP 'Last-Modified' header)
 | 
						|
    # and halt if conditional GET matches. The +time+ argument is a Time,
 | 
						|
    # DateTime, or other object that responds to +to_time+.
 | 
						|
    #
 | 
						|
    # When the current request includes an 'If-Modified-Since' header that is
 | 
						|
    # equal or later than the time specified, execution is immediately halted
 | 
						|
    # with a '304 Not Modified' response.
 | 
						|
    def last_modified(time)
 | 
						|
      return unless time
 | 
						|
      time = time_for time
 | 
						|
      response['Last-Modified'] = time.httpdate
 | 
						|
      return if env['HTTP_IF_NONE_MATCH']
 | 
						|
 | 
						|
      if status == 200 and env['HTTP_IF_MODIFIED_SINCE']
 | 
						|
        # compare based on seconds since epoch
 | 
						|
        since = Time.httpdate(env['HTTP_IF_MODIFIED_SINCE']).to_i
 | 
						|
        halt 304 if since >= time.to_i
 | 
						|
      end
 | 
						|
 | 
						|
      if (success? or status == 412) and env['HTTP_IF_UNMODIFIED_SINCE']
 | 
						|
        # compare based on seconds since epoch
 | 
						|
        since = Time.httpdate(env['HTTP_IF_UNMODIFIED_SINCE']).to_i
 | 
						|
        halt 412 if since < time.to_i
 | 
						|
      end
 | 
						|
    rescue ArgumentError
 | 
						|
    end
 | 
						|
 | 
						|
    # Set the response entity tag (HTTP 'ETag' header) and halt if conditional
 | 
						|
    # GET matches. The +value+ argument is an identifier that uniquely
 | 
						|
    # identifies the current version of the resource. The +kind+ argument
 | 
						|
    # indicates whether the etag should be used as a :strong (default) or :weak
 | 
						|
    # cache validator.
 | 
						|
    #
 | 
						|
    # When the current request includes an 'If-None-Match' header with a
 | 
						|
    # matching etag, execution is immediately halted. If the request method is
 | 
						|
    # GET or HEAD, a '304 Not Modified' response is sent.
 | 
						|
    def etag(value, options = {})
 | 
						|
      # Before touching this code, please double check RFC 2616 14.24 and 14.26.
 | 
						|
      options      = {:kind => options} unless Hash === options
 | 
						|
      kind         = options[:kind] || :strong
 | 
						|
      new_resource = options.fetch(:new_resource) { request.post? }
 | 
						|
 | 
						|
      unless [:strong, :weak].include?(kind)
 | 
						|
        raise ArgumentError, ":strong or :weak expected"
 | 
						|
      end
 | 
						|
 | 
						|
      value = '"%s"' % value
 | 
						|
      value = 'W/' + value if kind == :weak
 | 
						|
      response['ETag'] = value
 | 
						|
 | 
						|
      if success? or status == 304
 | 
						|
        if etag_matches? env['HTTP_IF_NONE_MATCH'], new_resource
 | 
						|
          halt(request.safe? ? 304 : 412)
 | 
						|
        end
 | 
						|
 | 
						|
        if env['HTTP_IF_MATCH']
 | 
						|
          halt 412 unless etag_matches? env['HTTP_IF_MATCH'], new_resource
 | 
						|
        end
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    # Sugar for redirect (example:  redirect back)
 | 
						|
    def back
 | 
						|
      request.referer
 | 
						|
    end
 | 
						|
 | 
						|
    # whether or not the status is set to 1xx
 | 
						|
    def informational?
 | 
						|
      status.between? 100, 199
 | 
						|
    end
 | 
						|
 | 
						|
    # whether or not the status is set to 2xx
 | 
						|
    def success?
 | 
						|
      status.between? 200, 299
 | 
						|
    end
 | 
						|
 | 
						|
    # whether or not the status is set to 3xx
 | 
						|
    def redirect?
 | 
						|
      status.between? 300, 399
 | 
						|
    end
 | 
						|
 | 
						|
    # whether or not the status is set to 4xx
 | 
						|
    def client_error?
 | 
						|
      status.between? 400, 499
 | 
						|
    end
 | 
						|
 | 
						|
    # whether or not the status is set to 5xx
 | 
						|
    def server_error?
 | 
						|
      status.between? 500, 599
 | 
						|
    end
 | 
						|
 | 
						|
    # whether or not the status is set to 404
 | 
						|
    def not_found?
 | 
						|
      status == 404
 | 
						|
    end
 | 
						|
 | 
						|
    # Generates a Time object from the given value.
 | 
						|
    # Used by #expires and #last_modified.
 | 
						|
    def time_for(value)
 | 
						|
      if value.respond_to? :to_time
 | 
						|
        value.to_time
 | 
						|
      elsif value.is_a? Time
 | 
						|
        value
 | 
						|
      elsif value.respond_to? :new_offset
 | 
						|
        # DateTime#to_time does the same on 1.9
 | 
						|
        d = value.new_offset 0
 | 
						|
        t = Time.utc d.year, d.mon, d.mday, d.hour, d.min, d.sec + d.sec_fraction
 | 
						|
        t.getlocal
 | 
						|
      elsif value.respond_to? :mday
 | 
						|
        # Date#to_time does the same on 1.9
 | 
						|
        Time.local(value.year, value.mon, value.mday)
 | 
						|
      elsif value.is_a? Numeric
 | 
						|
        Time.at value
 | 
						|
      else
 | 
						|
        Time.parse value.to_s
 | 
						|
      end
 | 
						|
    rescue ArgumentError => boom
 | 
						|
      raise boom
 | 
						|
    rescue Exception
 | 
						|
      raise ArgumentError, "unable to convert #{value.inspect} to a Time object"
 | 
						|
    end
 | 
						|
 | 
						|
    private
 | 
						|
 | 
						|
    # Helper method checking if a ETag value list includes the current ETag.
 | 
						|
    def etag_matches?(list, new_resource = request.post?)
 | 
						|
      return !new_resource if list == '*'
 | 
						|
      list.to_s.split(/\s*,\s*/).include? response['ETag']
 | 
						|
    end
 | 
						|
 | 
						|
    def with_params(temp_params)
 | 
						|
      original, @params = @params, temp_params
 | 
						|
      yield
 | 
						|
    ensure
 | 
						|
      @params = original if original
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  private
 | 
						|
 | 
						|
  # Template rendering methods. Each method takes the name of a template
 | 
						|
  # to render as a Symbol and returns a String with the rendered output,
 | 
						|
  # as well as an optional hash with additional options.
 | 
						|
  #
 | 
						|
  # `template` is either the name or path of the template as symbol
 | 
						|
  # (Use `:'subdir/myview'` for views in subdirectories), or a string
 | 
						|
  # that will be rendered.
 | 
						|
  #
 | 
						|
  # Possible options are:
 | 
						|
  #   :content_type   The content type to use, same arguments as content_type.
 | 
						|
  #   :layout         If set to false, no layout is rendered, otherwise
 | 
						|
  #                   the specified layout is used (Ignored for `sass` and `less`)
 | 
						|
  #   :layout_engine  Engine to use for rendering the layout.
 | 
						|
  #   :locals         A hash with local variables that should be available
 | 
						|
  #                   in the template
 | 
						|
  #   :scope          If set, template is evaluate with the binding of the given
 | 
						|
  #                   object rather than the application instance.
 | 
						|
  #   :views          Views directory to use.
 | 
						|
  module Templates
 | 
						|
    module ContentTyped
 | 
						|
      attr_accessor :content_type
 | 
						|
    end
 | 
						|
 | 
						|
    def initialize
 | 
						|
      super
 | 
						|
      @default_layout = :layout
 | 
						|
    end
 | 
						|
 | 
						|
    def erb(template, options={}, locals={})
 | 
						|
      render :erb, template, options, locals
 | 
						|
    end
 | 
						|
 | 
						|
    def erubis(template, options={}, locals={})
 | 
						|
      warn "Sinatra::Templates#erubis is deprecated and will be removed, use #erb instead.\n" \
 | 
						|
        "If you have Erubis installed, it will be used automatically."
 | 
						|
      render :erubis, template, options, locals
 | 
						|
    end
 | 
						|
 | 
						|
    def haml(template, options={}, locals={})
 | 
						|
      render :haml, template, options, locals
 | 
						|
    end
 | 
						|
 | 
						|
    def sass(template, options={}, locals={})
 | 
						|
      options.merge! :layout => false, :default_content_type => :css
 | 
						|
      render :sass, template, options, locals
 | 
						|
    end
 | 
						|
 | 
						|
    def scss(template, options={}, locals={})
 | 
						|
      options.merge! :layout => false, :default_content_type => :css
 | 
						|
      render :scss, template, options, locals
 | 
						|
    end
 | 
						|
 | 
						|
    def less(template, options={}, locals={})
 | 
						|
      options.merge! :layout => false, :default_content_type => :css
 | 
						|
      render :less, template, options, locals
 | 
						|
    end
 | 
						|
 | 
						|
    def builder(template=nil, options={}, locals={}, &block)
 | 
						|
      options[:default_content_type] = :xml
 | 
						|
      render_ruby(:builder, template, options, locals, &block)
 | 
						|
    end
 | 
						|
 | 
						|
    def liquid(template, options={}, locals={})
 | 
						|
      render :liquid, template, options, locals
 | 
						|
    end
 | 
						|
 | 
						|
    def markdown(template, options={}, locals={})
 | 
						|
      render :markdown, template, options, locals
 | 
						|
    end
 | 
						|
 | 
						|
    def textile(template, options={}, locals={})
 | 
						|
      render :textile, template, options, locals
 | 
						|
    end
 | 
						|
 | 
						|
    def rdoc(template, options={}, locals={})
 | 
						|
      render :rdoc, template, options, locals
 | 
						|
    end
 | 
						|
 | 
						|
    def radius(template, options={}, locals={})
 | 
						|
      render :radius, template, options, locals
 | 
						|
    end
 | 
						|
 | 
						|
    def markaby(template=nil, options={}, locals={}, &block)
 | 
						|
      render_ruby(:mab, template, options, locals, &block)
 | 
						|
    end
 | 
						|
 | 
						|
    def coffee(template, options={}, locals={})
 | 
						|
      options.merge! :layout => false, :default_content_type => :js
 | 
						|
      render :coffee, template, options, locals
 | 
						|
    end
 | 
						|
 | 
						|
    def nokogiri(template=nil, options={}, locals={}, &block)
 | 
						|
      options[:default_content_type] = :xml
 | 
						|
      render_ruby(:nokogiri, template, options, locals, &block)
 | 
						|
    end
 | 
						|
 | 
						|
    def slim(template, options={}, locals={})
 | 
						|
      render :slim, template, options, locals
 | 
						|
    end
 | 
						|
 | 
						|
    def creole(template, options={}, locals={})
 | 
						|
      render :creole, template, options, locals
 | 
						|
    end
 | 
						|
 | 
						|
    def yajl(template, options={}, locals={})
 | 
						|
      options[:default_content_type] = :json
 | 
						|
      render :yajl, template, options, locals
 | 
						|
    end
 | 
						|
 | 
						|
    # Calls the given block for every possible template file in views,
 | 
						|
    # named name.ext, where ext is registered on engine.
 | 
						|
    def find_template(views, name, engine)
 | 
						|
      yield ::File.join(views, "#{name}.#{@preferred_extension}")
 | 
						|
      Tilt.mappings.each do |ext, engines|
 | 
						|
        next unless ext != @preferred_extension and engines.include? engine
 | 
						|
        yield ::File.join(views, "#{name}.#{ext}")
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
  private
 | 
						|
    # logic shared between builder and nokogiri
 | 
						|
    def render_ruby(engine, template, options={}, locals={}, &block)
 | 
						|
      options, template = template, nil if template.is_a?(Hash)
 | 
						|
      template = Proc.new { block } if template.nil?
 | 
						|
      render engine, template, options, locals
 | 
						|
    end
 | 
						|
 | 
						|
    def render(engine, data, options={}, locals={}, &block)
 | 
						|
      # merge app-level options
 | 
						|
      options = settings.send(engine).merge(options) if settings.respond_to?(engine)
 | 
						|
      options[:outvar]           ||= '@_out_buf'
 | 
						|
      options[:default_encoding] ||= settings.default_encoding
 | 
						|
 | 
						|
      # extract generic options
 | 
						|
      locals          = options.delete(:locals) || locals         || {}
 | 
						|
      views           = options.delete(:views)  || settings.views || "./views"
 | 
						|
      layout          = options.delete(:layout)
 | 
						|
      eat_errors      = layout.nil?
 | 
						|
      layout          = @default_layout if layout.nil? or layout == true
 | 
						|
      content_type    = options.delete(:content_type)  || options.delete(:default_content_type)
 | 
						|
      layout_engine   = options.delete(:layout_engine) || engine
 | 
						|
      scope           = options.delete(:scope)         || self
 | 
						|
 | 
						|
      # compile and render template
 | 
						|
      begin
 | 
						|
        layout_was      = @default_layout
 | 
						|
        @default_layout = false
 | 
						|
        template        = compile_template(engine, data, options, views)
 | 
						|
        output          = template.render(scope, locals, &block)
 | 
						|
      ensure
 | 
						|
        @default_layout = layout_was
 | 
						|
      end
 | 
						|
 | 
						|
      # render layout
 | 
						|
      if layout
 | 
						|
        options = options.merge(:views => views, :layout => false, :eat_errors => eat_errors, :scope => scope)
 | 
						|
        catch(:layout_missing) { return render(layout_engine, layout, options, locals) { output } }
 | 
						|
      end
 | 
						|
 | 
						|
      output.extend(ContentTyped).content_type = content_type if content_type
 | 
						|
      output
 | 
						|
    end
 | 
						|
 | 
						|
    def compile_template(engine, data, options, views)
 | 
						|
      eat_errors = options.delete :eat_errors
 | 
						|
      template_cache.fetch engine, data, options do
 | 
						|
        template = Tilt[engine]
 | 
						|
        raise "Template engine not found: #{engine}" if template.nil?
 | 
						|
 | 
						|
        case data
 | 
						|
        when Symbol
 | 
						|
          body, path, line = settings.templates[data]
 | 
						|
          if body
 | 
						|
            body = body.call if body.respond_to?(:call)
 | 
						|
            template.new(path, line.to_i, options) { body }
 | 
						|
          else
 | 
						|
            found = false
 | 
						|
            @preferred_extension = engine.to_s
 | 
						|
            find_template(views, data, template) do |file|
 | 
						|
              path ||= file # keep the initial path rather than the last one
 | 
						|
              if found = File.exists?(file)
 | 
						|
                path = file
 | 
						|
                break
 | 
						|
              end
 | 
						|
            end
 | 
						|
            throw :layout_missing if eat_errors and not found
 | 
						|
            template.new(path, 1, options)
 | 
						|
          end
 | 
						|
        when Proc, String
 | 
						|
          body = data.is_a?(String) ? Proc.new { data } : data
 | 
						|
          path, line = settings.caller_locations.first
 | 
						|
          template.new(path, line.to_i, options, &body)
 | 
						|
        else
 | 
						|
          raise ArgumentError, "Sorry, don't know how to render #{data.inspect}."
 | 
						|
        end
 | 
						|
      end
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  # Base class for all Sinatra applications and middleware.
 | 
						|
  class Base
 | 
						|
    include Rack::Utils
 | 
						|
    include Helpers
 | 
						|
    include Templates
 | 
						|
 | 
						|
    attr_accessor :app
 | 
						|
    attr_reader   :template_cache
 | 
						|
 | 
						|
    def initialize(app=nil)
 | 
						|
      super()
 | 
						|
      @app = app
 | 
						|
      @template_cache = Tilt::Cache.new
 | 
						|
      yield self if block_given?
 | 
						|
    end
 | 
						|
 | 
						|
    # Rack call interface.
 | 
						|
    def call(env)
 | 
						|
      dup.call!(env)
 | 
						|
    end
 | 
						|
 | 
						|
    attr_accessor :env, :request, :response, :params
 | 
						|
 | 
						|
    def call!(env) # :nodoc:
 | 
						|
      @env      = env
 | 
						|
      @request  = Request.new(env)
 | 
						|
      @response = Response.new
 | 
						|
      @params   = indifferent_params(@request.params)
 | 
						|
      template_cache.clear if settings.reload_templates
 | 
						|
      force_encoding(@params)
 | 
						|
 | 
						|
      @response['Content-Type'] = nil
 | 
						|
      invoke { dispatch! }
 | 
						|
      invoke { error_block!(response.status) }
 | 
						|
 | 
						|
      unless @response['Content-Type']
 | 
						|
        if Array === body and body[0].respond_to? :content_type
 | 
						|
          content_type body[0].content_type
 | 
						|
        else
 | 
						|
          content_type :html
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      @response.finish
 | 
						|
    end
 | 
						|
 | 
						|
    # Access settings defined with Base.set.
 | 
						|
    def self.settings
 | 
						|
      self
 | 
						|
    end
 | 
						|
 | 
						|
    # Access settings defined with Base.set.
 | 
						|
    def settings
 | 
						|
      self.class.settings
 | 
						|
    end
 | 
						|
 | 
						|
    def options
 | 
						|
      warn "Sinatra::Base#options is deprecated and will be removed, " \
 | 
						|
        "use #settings instead."
 | 
						|
      settings
 | 
						|
    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 env
 | 
						|
      @response.status = status
 | 
						|
      @response.body = body
 | 
						|
      @response.headers.merge! headers
 | 
						|
      nil
 | 
						|
    end
 | 
						|
 | 
						|
  private
 | 
						|
    # Run filters defined on the class and all superclasses.
 | 
						|
    def filter!(type, base = settings)
 | 
						|
      filter! type, base.superclass if base.superclass.respond_to?(:filters)
 | 
						|
      base.filters[type].each { |args| process_route(*args) }
 | 
						|
    end
 | 
						|
 | 
						|
    # Run routes defined on the class and all superclasses.
 | 
						|
    def route!(base = settings, pass_block=nil)
 | 
						|
      if routes = base.routes[@request.request_method]
 | 
						|
        routes.each do |pattern, keys, conditions, block|
 | 
						|
          pass_block = process_route(pattern, keys, conditions) do |*args|
 | 
						|
            route_eval { block[*args] }
 | 
						|
          end
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      # Run routes defined in superclass.
 | 
						|
      if base.superclass.respond_to?(:routes)
 | 
						|
        return route!(base.superclass, pass_block)
 | 
						|
      end
 | 
						|
 | 
						|
      route_eval(&pass_block) if pass_block
 | 
						|
      route_missing
 | 
						|
    end
 | 
						|
 | 
						|
    # Run a route block and throw :halt with the result.
 | 
						|
    def route_eval
 | 
						|
      throw :halt, yield
 | 
						|
    end
 | 
						|
 | 
						|
    # If the current request matches pattern and conditions, fill params
 | 
						|
    # with keys and call the given block.
 | 
						|
    # Revert params afterwards.
 | 
						|
    #
 | 
						|
    # Returns pass block.
 | 
						|
    def process_route(pattern, keys, conditions, block = nil, values = [])
 | 
						|
      route = @request.path_info
 | 
						|
      route = '/' if route.empty? and not settings.empty_path_info?
 | 
						|
      return unless match = pattern.match(route)
 | 
						|
      values += match.captures.to_a.map { |v| force_encoding URI.decode_www_form_component(v) if v }
 | 
						|
 | 
						|
      if values.any?
 | 
						|
        original, @params = params, params.merge('splat' => [], 'captures' => values)
 | 
						|
        keys.zip(values) { |k,v| Array === @params[k] ? @params[k] << v : @params[k] = v if v }
 | 
						|
      end
 | 
						|
 | 
						|
      catch(:pass) do
 | 
						|
        conditions.each { |c| throw :pass if c.bind(self).call == false }
 | 
						|
        block ? block[self, values] : yield(self, values)
 | 
						|
      end
 | 
						|
    ensure
 | 
						|
      @params = original if original
 | 
						|
    end
 | 
						|
 | 
						|
    # No matching route was found or all routes passed. The default
 | 
						|
    # implementation is to forward the request downstream when running
 | 
						|
    # as middleware (@app is non-nil); when no downstream app is set, raise
 | 
						|
    # a NotFound exception. Subclasses can override this method to perform
 | 
						|
    # custom route miss logic.
 | 
						|
    def route_missing
 | 
						|
      if @app
 | 
						|
        forward
 | 
						|
      else
 | 
						|
        raise NotFound
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    # Attempt to serve static files from public directory. Throws :halt when
 | 
						|
    # a matching file is found, returns nil otherwise.
 | 
						|
    def static!
 | 
						|
      return if (public_dir = settings.public_folder).nil?
 | 
						|
      public_dir = File.expand_path(public_dir)
 | 
						|
 | 
						|
      path = File.expand_path(public_dir + unescape(request.path_info))
 | 
						|
      return unless path.start_with?(public_dir) and File.file?(path)
 | 
						|
 | 
						|
      env['sinatra.static_file'] = path
 | 
						|
      cache_control(*settings.static_cache_control) if settings.static_cache_control?
 | 
						|
      send_file path, :disposition => nil
 | 
						|
    end
 | 
						|
 | 
						|
    # Enable string or symbol key access to the nested params hash.
 | 
						|
    def indifferent_params(object)
 | 
						|
      case object
 | 
						|
      when Hash
 | 
						|
        new_hash = indifferent_hash
 | 
						|
        object.each { |key, value| new_hash[key] = indifferent_params(value) }
 | 
						|
        new_hash
 | 
						|
      when Array
 | 
						|
        object.map { |item| indifferent_params(item) }
 | 
						|
      else
 | 
						|
        object
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    # Creates a Hash with indifferent access.
 | 
						|
    def indifferent_hash
 | 
						|
      Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
 | 
						|
    end
 | 
						|
 | 
						|
    # Run the block with 'throw :halt' support and apply result to the response.
 | 
						|
    def invoke
 | 
						|
      res = catch(:halt) { yield }
 | 
						|
      res = [res] if Fixnum === res or String === res
 | 
						|
      if Array === res and Fixnum === res.first
 | 
						|
        status(res.shift)
 | 
						|
        body(res.pop)
 | 
						|
        headers(*res)
 | 
						|
      elsif res.respond_to? :each
 | 
						|
        body res
 | 
						|
      end
 | 
						|
      nil # avoid double setting the same response tuple twice
 | 
						|
    end
 | 
						|
 | 
						|
    # Dispatch a request with error handling.
 | 
						|
    def dispatch!
 | 
						|
      invoke do
 | 
						|
        static! if settings.static? && (request.get? || request.head?)
 | 
						|
        filter! :before
 | 
						|
        route!
 | 
						|
      end
 | 
						|
    rescue ::Exception => boom
 | 
						|
      invoke { handle_exception!(boom) }
 | 
						|
    ensure
 | 
						|
      filter! :after unless env['sinatra.static_file']
 | 
						|
    end
 | 
						|
 | 
						|
    # Error handling during requests.
 | 
						|
    def handle_exception!(boom)
 | 
						|
      @env['sinatra.error'] = boom
 | 
						|
 | 
						|
      if boom.respond_to? :http_status
 | 
						|
        status(boom.http_status)
 | 
						|
      elsif settings.use_code? and boom.respond_to? :code and boom.code.between? 400, 599
 | 
						|
        status(boom.code)
 | 
						|
      else
 | 
						|
        status(500)
 | 
						|
      end
 | 
						|
 | 
						|
      status(500) unless status.between? 400, 599
 | 
						|
 | 
						|
      if server_error?
 | 
						|
        dump_errors! boom if settings.dump_errors?
 | 
						|
        raise boom if settings.show_exceptions? and settings.show_exceptions != :after_handler
 | 
						|
      end
 | 
						|
 | 
						|
      if not_found?
 | 
						|
        headers['X-Cascade'] = 'pass'
 | 
						|
        body '<h1>Not Found</h1>'
 | 
						|
      end
 | 
						|
 | 
						|
      res = error_block!(boom.class, boom) || error_block!(status, boom)
 | 
						|
      return res if res or not server_error?
 | 
						|
      raise boom if settings.raise_errors? or settings.show_exceptions?
 | 
						|
      error_block! Exception, boom
 | 
						|
    end
 | 
						|
 | 
						|
    # Find an custom error block for the key(s) specified.
 | 
						|
    def error_block!(key, *block_params)
 | 
						|
      base = settings
 | 
						|
      while base.respond_to?(:errors)
 | 
						|
        next base = base.superclass unless args_array = base.errors[key]
 | 
						|
        args_array.reverse_each do |args|
 | 
						|
          first = args == args_array.first
 | 
						|
          args += [block_params]
 | 
						|
          resp = process_route(*args)
 | 
						|
          return resp unless resp.nil? && !first
 | 
						|
        end
 | 
						|
      end
 | 
						|
      return false unless key.respond_to? :superclass and key.superclass < Exception
 | 
						|
      error_block!(key.superclass, *block_params)
 | 
						|
    end
 | 
						|
 | 
						|
    def dump_errors!(boom)
 | 
						|
      msg = ["#{boom.class} - #{boom.message}:", *boom.backtrace].join("\n\t")
 | 
						|
      @env['rack.errors'].puts(msg)
 | 
						|
    end
 | 
						|
 | 
						|
    class << self
 | 
						|
      attr_reader :routes, :filters, :templates, :errors
 | 
						|
 | 
						|
      # Removes all routes, filters, middleware and extension hooks from the
 | 
						|
      # current class (not routes/filters/... defined by its superclass).
 | 
						|
      def reset!
 | 
						|
        @conditions     = []
 | 
						|
        @routes         = {}
 | 
						|
        @filters        = {:before => [], :after => []}
 | 
						|
        @errors         = {}
 | 
						|
        @middleware     = []
 | 
						|
        @prototype      = nil
 | 
						|
        @extensions     = []
 | 
						|
 | 
						|
        if superclass.respond_to?(:templates)
 | 
						|
          @templates = Hash.new { |hash,key| superclass.templates[key] }
 | 
						|
        else
 | 
						|
          @templates = {}
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      # Extension modules registered on this class and all superclasses.
 | 
						|
      def extensions
 | 
						|
        if superclass.respond_to?(:extensions)
 | 
						|
          (@extensions + superclass.extensions).uniq
 | 
						|
        else
 | 
						|
          @extensions
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      # Middleware used in this class and all superclasses.
 | 
						|
      def middleware
 | 
						|
        if superclass.respond_to?(:middleware)
 | 
						|
          superclass.middleware + @middleware
 | 
						|
        else
 | 
						|
          @middleware
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      # Sets an option to the given value.  If the value is a proc,
 | 
						|
      # the proc will be called every time the option is accessed.
 | 
						|
      def set(option, value = (not_set = true), ignore_setter = false, &block)
 | 
						|
        raise ArgumentError if block and !not_set
 | 
						|
        value, not_set = block, false if block
 | 
						|
 | 
						|
        if not_set
 | 
						|
          raise ArgumentError unless option.respond_to?(:each)
 | 
						|
          option.each { |k,v| set(k, v) }
 | 
						|
          return self
 | 
						|
        end
 | 
						|
 | 
						|
        if respond_to?("#{option}=") and not ignore_setter
 | 
						|
          return __send__("#{option}=", value)
 | 
						|
        end
 | 
						|
 | 
						|
        setter = proc { |val| set option, val, true }
 | 
						|
        getter = proc { value }
 | 
						|
 | 
						|
        case value
 | 
						|
        when Proc
 | 
						|
          getter = value
 | 
						|
        when Symbol, Fixnum, FalseClass, TrueClass, NilClass
 | 
						|
          getter = value.inspect
 | 
						|
        when Hash
 | 
						|
          setter = proc do |val|
 | 
						|
            val = value.merge val if Hash === val
 | 
						|
            set option, val, true
 | 
						|
          end
 | 
						|
        end
 | 
						|
 | 
						|
        define_singleton_method("#{option}=", setter) if setter
 | 
						|
        define_singleton_method(option, getter) if getter
 | 
						|
        define_singleton_method("#{option}?", "!!#{option}") unless method_defined? "#{option}?"
 | 
						|
        self
 | 
						|
      end
 | 
						|
 | 
						|
      # Same as calling `set :option, true` for each of the given options.
 | 
						|
      def enable(*opts)
 | 
						|
        opts.each { |key| set(key, true) }
 | 
						|
      end
 | 
						|
 | 
						|
      # Same as calling `set :option, false` for each of the given options.
 | 
						|
      def disable(*opts)
 | 
						|
        opts.each { |key| set(key, false) }
 | 
						|
      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, &block)
 | 
						|
        args  = compile! "ERROR", //, block
 | 
						|
        codes = codes.map { |c| Array(c) }.flatten
 | 
						|
        codes << Exception if codes.empty?
 | 
						|
        codes.each { |c| (@errors[c] ||= []) << args }
 | 
						|
      end
 | 
						|
 | 
						|
      # Sugar for `error(404) { ... }`
 | 
						|
      def not_found(&block)
 | 
						|
        error 404, &block
 | 
						|
      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 the layout template. The block must return the template source.
 | 
						|
      def layout(name=:layout, &block)
 | 
						|
        template name, &block
 | 
						|
      end
 | 
						|
 | 
						|
      # Load embeded templates from the file; uses the caller's __FILE__
 | 
						|
      # when no file is specified.
 | 
						|
      def inline_templates=(file=nil)
 | 
						|
        file = (file.nil? || file == true) ? (caller_files.first || File.expand_path($0)) : file
 | 
						|
 | 
						|
        begin
 | 
						|
          io = ::IO.respond_to?(:binread) ? ::IO.binread(file) : ::IO.read(file)
 | 
						|
          app, data = io.gsub("\r\n", "\n").split(/^__END__$/, 2)
 | 
						|
        rescue Errno::ENOENT
 | 
						|
          app, data = nil
 | 
						|
        end
 | 
						|
 | 
						|
        if data
 | 
						|
          if app and app =~ /([^\n]*\n)?#[^\n]*coding: *(\S+)/m
 | 
						|
            encoding = $2
 | 
						|
          else
 | 
						|
            encoding = settings.default_encoding
 | 
						|
          end
 | 
						|
          lines = app.count("\n") + 1
 | 
						|
          template = nil
 | 
						|
          force_encoding data, encoding
 | 
						|
          data.each_line do |line|
 | 
						|
            lines += 1
 | 
						|
            if line =~ /^@@\s*(.*\S)\s*$/
 | 
						|
              template = force_encoding('', encoding)
 | 
						|
              templates[$1.to_sym] = [template, file, lines]
 | 
						|
            elsif template
 | 
						|
              template << line
 | 
						|
            end
 | 
						|
          end
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      # Lookup or register a mime type in Rack's mime registry.
 | 
						|
      def mime_type(type, value=nil)
 | 
						|
        return type if type.nil? || type.to_s.include?('/')
 | 
						|
        type = ".#{type}" unless type.to_s[0] == ?.
 | 
						|
        return Rack::Mime.mime_type(type, nil) unless value
 | 
						|
        Rack::Mime::MIME_TYPES[type] = value
 | 
						|
      end
 | 
						|
 | 
						|
      # provides all mime types matching type, including deprecated types:
 | 
						|
      #   mime_types :html # => ['text/html']
 | 
						|
      #   mime_types :js   # => ['application/javascript', 'text/javascript']
 | 
						|
      def mime_types(type)
 | 
						|
        type = mime_type type
 | 
						|
        type =~ /^application\/(xml|javascript)$/ ? [type, "text/#$1"] : [type]
 | 
						|
      end
 | 
						|
 | 
						|
      # Define a before filter; runs before all requests within the same
 | 
						|
      # context as route handlers and may access/modify the request and
 | 
						|
      # response.
 | 
						|
      def before(path = nil, options = {}, &block)
 | 
						|
        add_filter(:before, path, options, &block)
 | 
						|
      end
 | 
						|
 | 
						|
      # Define an after filter; runs after all requests within the same
 | 
						|
      # context as route handlers and may access/modify the request and
 | 
						|
      # response.
 | 
						|
      def after(path = nil, options = {}, &block)
 | 
						|
        add_filter(:after, path, options, &block)
 | 
						|
      end
 | 
						|
 | 
						|
      # add a filter
 | 
						|
      def add_filter(type, path = nil, options = {}, &block)
 | 
						|
        path, options = //, path if path.respond_to?(:each_pair)
 | 
						|
        filters[type] << compile!(type, path || //, block, options)
 | 
						|
      end
 | 
						|
 | 
						|
      # Add a route condition. The route is considered non-matching when the
 | 
						|
      # block returns false.
 | 
						|
      def condition(name = "#{caller.first[/`.*'/]} condition", &block)
 | 
						|
        @conditions << generate_method(name, &block)
 | 
						|
      end
 | 
						|
 | 
						|
      def public=(value)
 | 
						|
        warn ":public is no longer used to avoid overloading Module#public, use :public_dir instead"
 | 
						|
        set(:public_folder, value)
 | 
						|
      end
 | 
						|
 | 
						|
      def public_dir=(value)
 | 
						|
        self.public_folder = value
 | 
						|
      end
 | 
						|
 | 
						|
      def public_dir
 | 
						|
        public_folder
 | 
						|
      end
 | 
						|
 | 
						|
    private
 | 
						|
      # Dynamically defines a method on settings.
 | 
						|
      def define_singleton_method(name, content = Proc.new)
 | 
						|
        # replace with call to singleton_class once we're 1.9 only
 | 
						|
        (class << self; self; end).class_eval do
 | 
						|
          undef_method(name) if method_defined? name
 | 
						|
          String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content)
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      # Condition for matching host name. Parameter might be String or Regexp.
 | 
						|
      def host_name(pattern)
 | 
						|
        condition { pattern === request.host }
 | 
						|
      end
 | 
						|
 | 
						|
      # Condition for matching user agent. Parameter should be Regexp.
 | 
						|
      # Will set params[:agent].
 | 
						|
      def user_agent(pattern)
 | 
						|
        condition do
 | 
						|
          if request.user_agent.to_s =~ pattern
 | 
						|
            @params[:agent] = $~[1..-1]
 | 
						|
            true
 | 
						|
          else
 | 
						|
            false
 | 
						|
          end
 | 
						|
        end
 | 
						|
      end
 | 
						|
      alias_method :agent, :user_agent
 | 
						|
 | 
						|
      # Condition for matching mimetypes. Accepts file extensions.
 | 
						|
      def provides(*types)
 | 
						|
        types.map! { |t| mime_types(t) }
 | 
						|
        types.flatten!
 | 
						|
        condition do
 | 
						|
          if type = response['Content-Type']
 | 
						|
            types.include? type or types.include? type[/^[^;]+/]
 | 
						|
          elsif type = request.preferred_type(types)
 | 
						|
            content_type(type)
 | 
						|
            true
 | 
						|
          else
 | 
						|
            false
 | 
						|
          end
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
    public
 | 
						|
      # Defining a `GET` handler also automatically defines
 | 
						|
      # a `HEAD` handler.
 | 
						|
      def get(path, opts={}, &block)
 | 
						|
        conditions = @conditions.dup
 | 
						|
        route('GET', path, opts, &block)
 | 
						|
 | 
						|
        @conditions = conditions
 | 
						|
        route('HEAD', path, opts, &block)
 | 
						|
      end
 | 
						|
 | 
						|
      def put(path, opts={}, &bk)     route 'PUT',     path, opts, &bk end
 | 
						|
      def post(path, opts={}, &bk)    route 'POST',    path, opts, &bk end
 | 
						|
      def delete(path, opts={}, &bk)  route 'DELETE',  path, opts, &bk end
 | 
						|
      def head(path, opts={}, &bk)    route 'HEAD',    path, opts, &bk end
 | 
						|
      def options(path, opts={}, &bk) route 'OPTIONS', path, opts, &bk end
 | 
						|
      def patch(path, opts={}, &bk)   route 'PATCH',   path, opts, &bk end
 | 
						|
 | 
						|
    private
 | 
						|
      def route(verb, path, options={}, &block)
 | 
						|
        # Because of self.options.host
 | 
						|
        host_name(options.delete(:host)) if options.key?(:host)
 | 
						|
        enable :empty_path_info if path == "" and empty_path_info.nil?
 | 
						|
        signature = compile!(verb, path, block, options)
 | 
						|
        (@routes[verb] ||= []) << signature
 | 
						|
        invoke_hook(:route_added, verb, path, block)
 | 
						|
        signature
 | 
						|
      end
 | 
						|
 | 
						|
      def invoke_hook(name, *args)
 | 
						|
        extensions.each { |e| e.send(name, *args) if e.respond_to?(name) }
 | 
						|
      end
 | 
						|
 | 
						|
      def generate_method(method_name, &block)
 | 
						|
        define_method(method_name, &block)
 | 
						|
        method = instance_method method_name
 | 
						|
        remove_method method_name
 | 
						|
        method
 | 
						|
      end
 | 
						|
 | 
						|
      def compile!(verb, path, block, options = {})
 | 
						|
        options.each_pair { |option, args| send(option, *args) }
 | 
						|
        method_name             = "#{verb} #{path}"
 | 
						|
        unbound_method          = generate_method(method_name, &block)
 | 
						|
        pattern, keys           = compile path
 | 
						|
        conditions, @conditions = @conditions, []
 | 
						|
 | 
						|
        [ pattern, keys, conditions, block.arity != 0 ?
 | 
						|
            proc { |a,p| unbound_method.bind(a).call(*p) } :
 | 
						|
            proc { |a,p| unbound_method.bind(a).call } ]
 | 
						|
      end
 | 
						|
 | 
						|
      def compile(path)
 | 
						|
        keys = []
 | 
						|
        if path.respond_to? :to_str
 | 
						|
          pattern = path.to_str.gsub(/[^\?\%\\\/\:\*\w]/) { |c| encoded(c) }
 | 
						|
          pattern.gsub!(/((:\w+)|\*)/) do |match|
 | 
						|
            if match == "*"
 | 
						|
              keys << 'splat'
 | 
						|
              "(.*?)"
 | 
						|
            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?(:names) && path.respond_to?(:match)
 | 
						|
          [path, path.names]
 | 
						|
        elsif path.respond_to? :match
 | 
						|
          [path, keys]
 | 
						|
        else
 | 
						|
          raise TypeError, path
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      URI = ::URI.const_defined?(:Parser) ? ::URI::Parser.new : ::URI
 | 
						|
 | 
						|
      def encoded(char)
 | 
						|
        enc = URI.escape(char)
 | 
						|
        enc = "(?:#{Regexp.escape enc}|#{URI.escape char, /./})" if enc == char
 | 
						|
        enc = "(?:#{enc}|#{encoded('+')})" if char == " "
 | 
						|
        enc
 | 
						|
      end
 | 
						|
 | 
						|
    public
 | 
						|
      # Makes the methods defined in the block and in the Modules given
 | 
						|
      # in `extensions` available to the handlers and templates
 | 
						|
      def helpers(*extensions, &block)
 | 
						|
        class_eval(&block)   if block_given?
 | 
						|
        include(*extensions) if extensions.any?
 | 
						|
      end
 | 
						|
 | 
						|
      # Register an extension. Alternatively take a block from which an
 | 
						|
      # extension will be created and registered on the fly.
 | 
						|
      def register(*extensions, &block)
 | 
						|
        extensions << Module.new(&block) if block_given?
 | 
						|
        @extensions += extensions
 | 
						|
        extensions.each do |extension|
 | 
						|
          extend extension
 | 
						|
          extension.registered(self) if extension.respond_to?(:registered)
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      def development?; environment == :development end
 | 
						|
      def production?;  environment == :production  end
 | 
						|
      def test?;        environment == :test        end
 | 
						|
 | 
						|
      # Set configuration options for Sinatra and/or the app.
 | 
						|
      # Allows scoping of settings for certain environments.
 | 
						|
      def configure(*envs, &block)
 | 
						|
        yield self if envs.empty? || envs.include?(environment.to_sym)
 | 
						|
      end
 | 
						|
 | 
						|
      # Use the specified Rack middleware
 | 
						|
      def use(middleware, *args, &block)
 | 
						|
        @prototype = nil
 | 
						|
        @middleware << [middleware, args, block]
 | 
						|
      end
 | 
						|
 | 
						|
      def quit!(server, handler_name)
 | 
						|
        # Use Thin's hard #stop! if available, otherwise just #stop.
 | 
						|
        server.respond_to?(:stop!) ? server.stop! : server.stop
 | 
						|
        $stderr.puts "\n== Sinatra has ended his set (crowd applauds)" unless handler_name =~/cgi/i
 | 
						|
      end
 | 
						|
 | 
						|
      # Run the Sinatra app as a self-hosted server using
 | 
						|
      # Thin, Puma, Mongrel, or WEBrick (in that order). If given a block, will call
 | 
						|
      # with the constructed handler once we have taken the stage.
 | 
						|
      def run!(options={})
 | 
						|
        set options
 | 
						|
        handler         = detect_rack_handler
 | 
						|
        handler_name    = handler.name.gsub(/.*::/, '')
 | 
						|
        server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {}
 | 
						|
        handler.run self, server_settings.merge(:Port => port, :Host => bind) do |server|
 | 
						|
          unless handler_name =~ /cgi/i
 | 
						|
            $stderr.puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " +
 | 
						|
            "on #{port} for #{environment} with backup from #{handler_name}"
 | 
						|
          end
 | 
						|
          [:INT, :TERM].each { |sig| trap(sig) { quit!(server, handler_name) } }
 | 
						|
          server.threaded = settings.threaded if server.respond_to? :threaded=
 | 
						|
          set :running, true
 | 
						|
          yield server if block_given?
 | 
						|
        end
 | 
						|
      rescue Errno::EADDRINUSE
 | 
						|
        $stderr.puts "== Someone is already performing on port #{port}!"
 | 
						|
      end
 | 
						|
 | 
						|
      # The prototype instance used to process requests.
 | 
						|
      def prototype
 | 
						|
        @prototype ||= new
 | 
						|
      end
 | 
						|
 | 
						|
      # Create a new instance without middleware in front of it.
 | 
						|
      alias new! new unless method_defined? :new!
 | 
						|
 | 
						|
      # Create a new instance of the class fronted by its middleware
 | 
						|
      # pipeline. The object is guaranteed to respond to #call but may not be
 | 
						|
      # an instance of the class new was called on.
 | 
						|
      def new(*args, &bk)
 | 
						|
        instance = new!(*args, &bk)
 | 
						|
        Wrapper.new(build(instance).to_app, instance)
 | 
						|
      end
 | 
						|
 | 
						|
      # Creates a Rack::Builder instance with all the middleware set up and
 | 
						|
      # the given +app+ as end point.
 | 
						|
      def build(app)
 | 
						|
        builder = Rack::Builder.new
 | 
						|
        setup_default_middleware builder
 | 
						|
        setup_middleware builder
 | 
						|
        builder.run app
 | 
						|
        builder
 | 
						|
      end
 | 
						|
 | 
						|
      def call(env)
 | 
						|
        synchronize { prototype.call(env) }
 | 
						|
      end
 | 
						|
 | 
						|
    private
 | 
						|
      def setup_default_middleware(builder)
 | 
						|
        builder.use ExtendedRack
 | 
						|
        builder.use ShowExceptions       if show_exceptions?
 | 
						|
        builder.use Rack::MethodOverride if method_override?
 | 
						|
        builder.use Rack::Head
 | 
						|
        setup_logging    builder
 | 
						|
        setup_sessions   builder
 | 
						|
        setup_protection builder
 | 
						|
      end
 | 
						|
 | 
						|
      def setup_middleware(builder)
 | 
						|
        middleware.each { |c,a,b| builder.use(c, *a, &b) }
 | 
						|
      end
 | 
						|
 | 
						|
      def setup_logging(builder)
 | 
						|
        if logging?
 | 
						|
          setup_common_logger(builder)
 | 
						|
          setup_custom_logger(builder)
 | 
						|
        elsif logging == false
 | 
						|
          setup_null_logger(builder)
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      def setup_null_logger(builder)
 | 
						|
        builder.use Rack::NullLogger
 | 
						|
      end
 | 
						|
 | 
						|
      def setup_common_logger(builder)
 | 
						|
        builder.use Sinatra::CommonLogger
 | 
						|
      end
 | 
						|
 | 
						|
      def setup_custom_logger(builder)
 | 
						|
        if logging.respond_to? :to_int
 | 
						|
          builder.use Rack::Logger, logging
 | 
						|
        else
 | 
						|
          builder.use Rack::Logger
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      def setup_protection(builder)
 | 
						|
        return unless protection?
 | 
						|
        options = Hash === protection ? protection.dup : {}
 | 
						|
        options[:except] = Array options[:except]
 | 
						|
        options[:except] += [:session_hijacking, :remote_token] unless sessions?
 | 
						|
        options[:reaction] ||= :drop_session
 | 
						|
        builder.use Rack::Protection, options
 | 
						|
      end
 | 
						|
 | 
						|
      def setup_sessions(builder)
 | 
						|
        return unless sessions?
 | 
						|
        options = {}
 | 
						|
        options[:secret] = session_secret if session_secret?
 | 
						|
        options.merge! sessions.to_hash if sessions.respond_to? :to_hash
 | 
						|
        builder.use Rack::Session::Cookie, options
 | 
						|
      end
 | 
						|
 | 
						|
      def detect_rack_handler
 | 
						|
        servers = Array(server)
 | 
						|
        servers.each do |server_name|
 | 
						|
          begin
 | 
						|
            return Rack::Handler.get(server_name.to_s)
 | 
						|
          rescue LoadError, NameError
 | 
						|
          end
 | 
						|
        end
 | 
						|
        fail "Server handler (#{servers.join(',')}) not found."
 | 
						|
      end
 | 
						|
 | 
						|
      def inherited(subclass)
 | 
						|
        subclass.reset!
 | 
						|
        subclass.set :app_file, caller_files.first unless subclass.app_file?
 | 
						|
        super
 | 
						|
      end
 | 
						|
 | 
						|
      @@mutex = Mutex.new
 | 
						|
      def synchronize(&block)
 | 
						|
        if lock?
 | 
						|
          @@mutex.synchronize(&block)
 | 
						|
        else
 | 
						|
          yield
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
    public
 | 
						|
      CALLERS_TO_IGNORE = [ # :nodoc:
 | 
						|
        /\/sinatra(\/(base|main|showexceptions))?\.rb$/, # all sinatra code
 | 
						|
        /lib\/tilt.*\.rb$/,                              # all tilt code
 | 
						|
        /^\(.*\)$/,                                      # generated code
 | 
						|
        /rubygems\/custom_require\.rb$/,                 # rubygems require hacks
 | 
						|
        /active_support/,                                # active_support require hacks
 | 
						|
        /bundler(\/runtime)?\.rb/,                       # bundler require hacks
 | 
						|
        /<internal:/,                                    # internal in ruby >= 1.9.2
 | 
						|
        /src\/kernel\/bootstrap\/[A-Z]/                  # maglev kernel files
 | 
						|
      ]
 | 
						|
 | 
						|
      # contrary to what the comment said previously, rubinius never supported this
 | 
						|
      if defined?(RUBY_IGNORE_CALLERS)
 | 
						|
        warn "RUBY_IGNORE_CALLERS is deprecated and will no longer be supported by Sinatra 2.0"
 | 
						|
        CALLERS_TO_IGNORE.concat(RUBY_IGNORE_CALLERS)
 | 
						|
      end
 | 
						|
 | 
						|
      # Like Kernel#caller but excluding certain magic entries and without
 | 
						|
      # line / method information; the resulting array contains filenames only.
 | 
						|
      def caller_files
 | 
						|
        cleaned_caller(1).flatten
 | 
						|
      end
 | 
						|
 | 
						|
      # Like caller_files, but containing Arrays rather than strings with the
 | 
						|
      # first element being the file, and the second being the line.
 | 
						|
      def caller_locations
 | 
						|
        cleaned_caller 2
 | 
						|
      end
 | 
						|
 | 
						|
    private
 | 
						|
      # used for deprecation warnings
 | 
						|
      def warn(message)
 | 
						|
        super message + "\n\tfrom #{cleaned_caller.first.join(':')}"
 | 
						|
      end
 | 
						|
 | 
						|
      # Like Kernel#caller but excluding certain magic entries
 | 
						|
      def cleaned_caller(keep = 3)
 | 
						|
        caller(1).
 | 
						|
          map    { |line| line.split(/:(?=\d|in )/, 3)[0,keep] }.
 | 
						|
          reject { |file, *_| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } }
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    # Fixes encoding issues by
 | 
						|
    # * defaulting to UTF-8
 | 
						|
    # * casting params to Encoding.default_external
 | 
						|
    #
 | 
						|
    # The latter might not be necessary if Rack handles it one day.
 | 
						|
    # Keep an eye on Rack's LH #100.
 | 
						|
    def force_encoding(*args) settings.force_encoding(*args) end
 | 
						|
    if defined? Encoding
 | 
						|
      def self.force_encoding(data, encoding = default_encoding)
 | 
						|
        return if data == settings || data.is_a?(Tempfile)
 | 
						|
        if data.respond_to? :force_encoding
 | 
						|
          data.force_encoding(encoding).encode!
 | 
						|
        elsif data.respond_to? :each_value
 | 
						|
          data.each_value { |v| force_encoding(v, encoding) }
 | 
						|
        elsif data.respond_to? :each
 | 
						|
          data.each { |v| force_encoding(v, encoding) }
 | 
						|
        end
 | 
						|
        data
 | 
						|
      end
 | 
						|
    else
 | 
						|
      def self.force_encoding(data, *) data end
 | 
						|
    end
 | 
						|
 | 
						|
    reset!
 | 
						|
 | 
						|
    set :environment, (ENV['RACK_ENV'] || :development).to_sym
 | 
						|
    set :raise_errors, Proc.new { test? }
 | 
						|
    set :dump_errors, Proc.new { !test? }
 | 
						|
    set :show_exceptions, Proc.new { development? }
 | 
						|
    set :sessions, false
 | 
						|
    set :logging, false
 | 
						|
    set :protection, true
 | 
						|
    set :method_override, false
 | 
						|
    set :use_code, false
 | 
						|
    set :default_encoding, "utf-8"
 | 
						|
    set :add_charset, %w[javascript xml xhtml+xml json].map { |t| "application/#{t}" }
 | 
						|
    settings.add_charset << /^text\//
 | 
						|
 | 
						|
    # explicitly generating a session secret eagerly to play nice with preforking
 | 
						|
    begin
 | 
						|
      require 'securerandom'
 | 
						|
      set :session_secret, SecureRandom.hex(64)
 | 
						|
    rescue LoadError, NotImplementedError
 | 
						|
      # SecureRandom raises a NotImplementedError if no random device is available
 | 
						|
      set :session_secret, "%064x" % Kernel.rand(2**256-1)
 | 
						|
    end
 | 
						|
 | 
						|
    class << self
 | 
						|
      alias_method :methodoverride?, :method_override?
 | 
						|
      alias_method :methodoverride=, :method_override=
 | 
						|
    end
 | 
						|
 | 
						|
    set :run, false                       # start server via at-exit hook?
 | 
						|
    set :running, false                   # is the built-in server running now?
 | 
						|
    set :server, %w[http webrick]
 | 
						|
    set :bind, '0.0.0.0'
 | 
						|
    set :port, 4567
 | 
						|
 | 
						|
    ruby_engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
 | 
						|
 | 
						|
    if ruby_engine == 'macruby'
 | 
						|
      server.unshift 'controll_tower'
 | 
						|
    else
 | 
						|
      server.unshift 'mongrel'  if ruby_engine.nil?
 | 
						|
      server.unshift 'puma'     if ruby_engine != 'rbx'
 | 
						|
      server.unshift 'thin'     if ruby_engine != 'jruby'
 | 
						|
      server.unshift 'puma'     if ruby_engine == 'rbx'
 | 
						|
      server.unshift 'trinidad' if ruby_engine =='jruby'
 | 
						|
    end
 | 
						|
 | 
						|
    set :absolute_redirects, true
 | 
						|
    set :prefixed_redirects, false
 | 
						|
    set :empty_path_info, nil
 | 
						|
 | 
						|
    set :app_file, nil
 | 
						|
    set :root, Proc.new { app_file && File.expand_path(File.dirname(app_file)) }
 | 
						|
    set :views, Proc.new { root && File.join(root, 'views') }
 | 
						|
    set :reload_templates, Proc.new { development? }
 | 
						|
    set :lock, false
 | 
						|
    set :threaded, true
 | 
						|
 | 
						|
    set :public_folder, Proc.new { root && File.join(root, 'public') }
 | 
						|
    set :static, Proc.new { public_folder && File.exist?(public_folder) }
 | 
						|
    set :static_cache_control, false
 | 
						|
 | 
						|
    error ::Exception do
 | 
						|
      response.status = 500
 | 
						|
      content_type 'text/html'
 | 
						|
      '<h1>Internal Server Error</h1>'
 | 
						|
    end
 | 
						|
 | 
						|
    configure :development do
 | 
						|
      get '/__sinatra__/:image.png' do
 | 
						|
        filename = File.dirname(__FILE__) + "/images/#{params[:image]}.png"
 | 
						|
        content_type :png
 | 
						|
        send_file filename
 | 
						|
      end
 | 
						|
 | 
						|
      error NotFound do
 | 
						|
        content_type 'text/html'
 | 
						|
 | 
						|
        (<<-HTML).gsub(/^ {8}/, '')
 | 
						|
        <!DOCTYPE html>
 | 
						|
        <html>
 | 
						|
        <head>
 | 
						|
          <style type="text/css">
 | 
						|
          body { text-align:center;font-family:helvetica,arial;font-size:22px;
 | 
						|
            color:#888;margin:20px}
 | 
						|
          #c {margin:0 auto;width:500px;text-align:left}
 | 
						|
          </style>
 | 
						|
        </head>
 | 
						|
        <body>
 | 
						|
          <h2>Sinatra doesn’t know this ditty.</h2>
 | 
						|
          <img src='#{uri "/__sinatra__/404.png"}'>
 | 
						|
          <div id="c">
 | 
						|
            Try this:
 | 
						|
            <pre>#{request.request_method.downcase} '#{request.path_info}' do\n  "Hello World"\nend</pre>
 | 
						|
          </div>
 | 
						|
        </body>
 | 
						|
        </html>
 | 
						|
        HTML
 | 
						|
      end
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  # Execution context for classic style (top-level) applications. All
 | 
						|
  # DSL methods executed on main are delegated to this class.
 | 
						|
  #
 | 
						|
  # The Application class should not be subclassed, unless you want to
 | 
						|
  # inherit all settings, routes, handlers, and error pages from the
 | 
						|
  # top-level. Subclassing Sinatra::Base is highly recommended for
 | 
						|
  # modular applications.
 | 
						|
  class Application < Base
 | 
						|
    set :logging, Proc.new { ! test? }
 | 
						|
    set :method_override, true
 | 
						|
    set :run, Proc.new { ! test? }
 | 
						|
    set :session_secret, Proc.new { super() unless development? }
 | 
						|
    set :app_file, nil
 | 
						|
 | 
						|
    def self.register(*extensions, &block) #:nodoc:
 | 
						|
      added_methods = extensions.map {|m| m.public_instance_methods }.flatten
 | 
						|
      Delegator.delegate(*added_methods)
 | 
						|
      super(*extensions, &block)
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  # Sinatra delegation mixin. Mixing this module into an object causes all
 | 
						|
  # methods to be delegated to the Sinatra::Application class. Used primarily
 | 
						|
  # at the top-level.
 | 
						|
  module Delegator #:nodoc:
 | 
						|
    def self.delegate(*methods)
 | 
						|
      methods.each do |method_name|
 | 
						|
        define_method(method_name) do |*args, &block|
 | 
						|
          return super(*args, &block) if respond_to? method_name
 | 
						|
          Delegator.target.send(method_name, *args, &block)
 | 
						|
        end
 | 
						|
        private method_name
 | 
						|
      end
 | 
						|
    end
 | 
						|
 | 
						|
    delegate :get, :patch, :put, :post, :delete, :head, :options, :template, :layout,
 | 
						|
             :before, :after, :error, :not_found, :configure, :set, :mime_type,
 | 
						|
             :enable, :disable, :use, :development?, :test?, :production?,
 | 
						|
             :helpers, :settings
 | 
						|
 | 
						|
    class << self
 | 
						|
      attr_accessor :target
 | 
						|
    end
 | 
						|
 | 
						|
    self.target = Application
 | 
						|
  end
 | 
						|
 | 
						|
  class Wrapper
 | 
						|
    def initialize(stack, instance)
 | 
						|
      @stack, @instance = stack, instance
 | 
						|
    end
 | 
						|
 | 
						|
    def settings
 | 
						|
      @instance.settings
 | 
						|
    end
 | 
						|
 | 
						|
    def helpers
 | 
						|
      @instance
 | 
						|
    end
 | 
						|
 | 
						|
    def call(env)
 | 
						|
      @stack.call(env)
 | 
						|
    end
 | 
						|
 | 
						|
    def inspect
 | 
						|
      "#<#{@instance.class} app_file=#{settings.app_file.inspect}>"
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  # Create a new Sinatra application. The block is evaluated in the new app's
 | 
						|
  # class scope.
 | 
						|
  def self.new(base=Base, options={}, &block)
 | 
						|
    base = Class.new(base)
 | 
						|
    base.class_eval(&block) if block_given?
 | 
						|
    base
 | 
						|
  end
 | 
						|
 | 
						|
  # Extend the top-level DSL with the modules provided.
 | 
						|
  def self.register(*extensions, &block)
 | 
						|
    Delegator.target.register(*extensions, &block)
 | 
						|
  end
 | 
						|
 | 
						|
  # Include the helper modules provided in Sinatra's request context.
 | 
						|
  def self.helpers(*extensions, &block)
 | 
						|
    Delegator.target.helpers(*extensions, &block)
 | 
						|
  end
 | 
						|
 | 
						|
  # Use the middleware for classic applications.
 | 
						|
  def self.use(*args, &block)
 | 
						|
    Delegator.target.use(*args, &block)
 | 
						|
  end
 | 
						|
end
 |