123456789_123456789_123456789_123456789_123456789_

Class: Sinatra::Base

Relationships & Source Files
Extension / Inclusion / Inheritance Descendants
Subclasses:
Super Chains via Extension / Inclusion / Inheritance
Instance Chain:
self, Templates, Helpers, Rack::Utils
Inherits: Object
Defined in: lib/sinatra/base.rb

Overview

Base class for all ::Sinatra applications and middleware.

Constant Summary

  • CALLERS_TO_IGNORE = Internal use only
    # File 'lib/sinatra/base.rb', line 1286
    [ # :nodoc:
      %r{/sinatra(/(base|main|show_exceptions))?\.rb$},   # all sinatra code
      %r{lib/tilt.*\.rb$},                                # all tilt code
      /^\(.*\)$/,                                         # generated code
      /\/bundled_gems.rb$/,                               # ruby >= 3.3 with bundler >= 2.5
      %r{rubygems/(custom|core_ext/kernel)_require\.rb$}, # rubygems require hacks
      /active_support/,                                   # active_support require hacks
      %r{bundler(/(?:runtime|inline))?\.rb},              # bundler require hacks
      /<internal:/,                                       # internal in ruby >= 1.9.2
      %r{zeitwerk/kernel\.rb}                             # Zeitwerk kernel#require decorator
    ].freeze
  • URI_INSTANCE =
    # File 'lib/sinatra/base.rb', line 975
    URI::Parser.new

Helpers - Included

ETAG_KINDS, MULTIPART_FORM_DATA_REPLACEMENT_TABLE

Class Attribute Summary

Class Method Summary

Instance Attribute Summary

Helpers - Included

#bad_request?

whether or not the status is set to 400.

#client_error?

whether or not the status is set to 4xx.

#informational?

whether or not the status is set to 1xx.

#not_found?

whether or not the status is set to 404.

#redirect?

whether or not the status is set to 3xx.

#server_error?

whether or not the status is set to 5xx.

#success?

whether or not the status is set to 2xx.

Instance Method Summary

Templates - Included

#asciidoc, #builder, #erb,
#find_template

Calls the given block for every possible template file in views, named name.ext, where ext is registered on engine.

#haml, #initialize, #liquid, #markaby, #markdown, #nokogiri, #rabl, #rdoc, #sass, #scss, #slim, #yajl, #compile_block_template, #compile_template, #render,
#render_ruby

logic shared between builder and nokogiri.

Helpers - Included

#attachment

Set the Content-Disposition to "attachment" with the specified filename, instructing the user agents to prompt to save.

#back

Sugar for redirect (example: redirect back).

#body

Set or retrieve the response body.

#cache_control

Specify response freshness policy for HTTP caches (Cache-Control header).

#content_type

Set the content-type of the response body given a media type or file extension.

#error

Halt processing and return the error status provided.

#etag

Set the response entity tag (HTTP 'ETag' header) and halt if conditional GET matches.

#expires

Set the Expires header and Cache-Control/max-age directive.

#headers

Set multiple response headers with Hash.

#last_modified

Set the last modified time of the resource (HTTP 'Last-Modified' header) and halt if conditional GET matches.

#logger

Access shared logger object.

#mime_type

Look up a media type by file extension in Rack's mime registry.

#not_found

Halt processing and return a 404 Not Found.

#redirect

Halt processing and redirect to the URI provided.

#send_file

Use the contents of the file at path as the response body.

#session

Access the underlying ::Rack session.

#status

Set or retrieve the response status code.

#stream

Allows to start sending data to the client even though later parts of the response body have not yet been generated.

#time_for

Generates a Time object from the given value.

#to

Alias for Helpers#uri.

#uri

Generates the absolute URI for a given path in the app.

#url

Alias for Helpers#uri.

#etag_matches?

Helper method checking if a ETag value list includes the current ETag.

#with_params

Constructor Details

.new(*args, &block) ⇒ Base Also known as: .new!, .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.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1657

def initialize(app = nil, **_kwargs)
  super()
  @app = app
  @template_cache = TemplateCache.new
  @pinned_response = nil # whether a before! filter pinned the content-type
  yield self if block_given?
end

Class Attribute Details

.development?Boolean (readonly)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1568

def development?; environment == :development end

.errors (readonly)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1298

attr_reader :routes, :filters, :templates, :errors, :on_start_callback, :on_stop_callback

.filters (readonly)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1298

attr_reader :routes, :filters, :templates, :errors, :on_start_callback, :on_stop_callback

.on_start_callback (readonly)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1298

attr_reader :routes, :filters, :templates, :errors, :on_start_callback, :on_stop_callback

.on_stop_callback (readonly)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1298

attr_reader :routes, :filters, :templates, :errors, :on_start_callback, :on_stop_callback

.production?Boolean (readonly)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1569

def production?;  environment == :production  end

.public=(value) (writeonly)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1511

def public=(value)
  warn_for_deprecation ':public is no longer used to avoid overloading Module#public, use :public_folder or :public_dir instead'
  set(:public_folder, value)
end

.public_dir (rw)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1520

def public_dir
  public_folder
end

.public_dir=(value) (rw)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1516

def public_dir=(value)
  self.public_folder = value
end

.routes (readonly)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1298

attr_reader :routes, :filters, :templates, :errors, :on_start_callback, :on_stop_callback

.running?Boolean (readonly)

Check whether the self-hosted server is running or not.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1642

def running?
  running_server?
end

.suppress_messages?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1705

def suppress_messages?
  handler_name =~ /cgi/i || quiet
end

.templates (readonly)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1298

attr_reader :routes, :filters, :templates, :errors, :on_start_callback, :on_stop_callback

.test?Boolean (readonly)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1570

def test?;        environment == :test        end

Class Method Details

.add_filter(type, path = /.*/, **options, &block)

add a filter

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1493

def add_filter(type, path = /.*/, **options, &block)
  filters[type] << compile!(type, path, block, **options)
end

.after(path = /.*/, **options, &block)

Define an after filter; runs after all requests within the same context as route handlers and may access/modify the request and response.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1488

def after(path = /.*/, **options, &block)
  add_filter(:after, path, **options, &block)
end

.agent(pattern) (private)

Alias for .user_agent.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1749

alias agent user_agent

.before(path = /.*/, **options, &block)

Define a before filter; runs before all requests within the same context as route handlers and may access/modify the request and response.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1481

def before(path = /.*/, **options, &block)
  add_filter(:before, path, **options, &block)
end

.build(app)

Creates a ::Rack::Builder instance with all the middleware set up and the given #app as end point.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1665

def build(app)
  builder = Rack::Builder.new
  setup_default_middleware builder
  setup_middleware builder
  builder.run app
  builder
end

.call(env)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1673

def call(env)
  synchronize { prototype.call(env) }
end

.caller_files

Like Kernel#caller but excluding certain magic entries and without line / method information; the resulting array contains filenames only.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1679

def caller_files
  cleaned_caller(1).flatten
end

.callers_to_ignore

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1300

def callers_to_ignore
  CALLERS_TO_IGNORE
end

.cleaned_caller(keep = 3) (private)

Like Kernel#caller but excluding certain magic entries

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1900

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

.compile(path, route_mustermann_opts = {}) (private)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1810

def compile(path, route_mustermann_opts = {})
  Mustermann.new(path, **mustermann_opts.merge(route_mustermann_opts))
end

.compile!(verb, path, block, **options) (private)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1790

def compile!(verb, path, block, **options)
  # Because of self.options.host
  host_name(options.delete(:host)) if options.key?(:host)
  # Pass Mustermann opts to compile()
  route_mustermann_opts = options.key?(:mustermann_opts) ? options.delete(:mustermann_opts) : {}.freeze

  options.each_pair { |option, args| send(option, *args) }

  pattern                 = compile(path, route_mustermann_opts)
  method_name             = "#{verb} #{path}"
  unbound_method          = generate_method(method_name, &block)
  conditions = @conditions
  @conditions = []
  wrapper = block.arity.zero? ?
    proc { |a, _p| unbound_method.bind(a).call } :
    proc { |a, p| unbound_method.bind(a).call(*p) }

  [pattern, conditions, wrapper]
end

.condition(name = "#{caller.first[/`.*'/]} condition", &block)

Add a route condition. The route is considered non-matching when the block returns false.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1507

def condition(name = "#{caller.first[/`.*'/]} condition", &block)
  @conditions << generate_method(name, &block)
end

.configure(*envs) {|_self| ... }

Set configuration options for ::Sinatra and/or the app. Allows scoping of settings for certain environments.

Yields:

  • (_self)

Yield Parameters:

  • _self (Base)

    the object that the method was called on

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1574

def configure(*envs)
  yield self if envs.empty? || envs.include?(environment.to_sym)
end

.define_singleton(name, content = Proc.new) (private)

Dynamically defines a method on settings.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1725

def define_singleton(name, content = Proc.new)
  singleton_class.class_eval do
    undef_method(name) if method_defined? name
    String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content)
  end
end

.delete(path, opts = {}, &block)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1538

def delete(path, opts = {}, &block)  route 'DELETE',  path, opts, &block end

.disable(*opts)

Same as calling set <code>:option</code>, false for each of the given options.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1388

def disable(*opts)
  opts.each { |key| set(key, false) }
end

.enable(*opts)

Same as calling set <code>:option</code>, true for each of the given options.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1383

def enable(*opts)
  opts.each { |key| set(key, true) }
end

.error(*codes, &block)

Define a custom error handler. Optionally takes either an Exception class, or an HTTP status code to specify which errors should be handled.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1395

def error(*codes, &block)
  args  = compile! 'ERROR', /.*/, block
  codes = codes.flat_map(&method(:Array))
  codes << Exception if codes.empty?
  codes << Sinatra::NotFound if codes.include?(404)
  codes.each { |c| (@errors[c] ||= []) << args }
end

.extensions

Extension modules registered on this class and all superclasses.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1323

def extensions
  if superclass.respond_to?(:extensions)
    (@extensions + superclass.extensions).uniq
  else
    @extensions
  end
end

.force_encoding(data, encoding = default_encoding) (private)

Force data to specified encoding. It defaults to settings.default_encoding which is UTF-8 by default

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1909

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

.generate_method(method_name, &block) (private)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1783

def generate_method(method_name, &block)
  define_method(method_name, &block)
  method = instance_method method_name
  remove_method method_name
  method
end

.get(path, opts = {}, &block)

Defining a GET handler also automatically defines a HEAD handler.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1526

def get(path, opts = {}, &block)
  conditions = @conditions.dup
  route('GET', path, opts, &block)

  @conditions = conditions
  route('HEAD', path, opts, &block)
end

.head(path, opts = {}, &block)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1540

def head(path, opts = {}, &block)    route 'HEAD',    path, opts, &block end

.helpers(*extensions, &block)

Makes the methods defined in the block and in the Modules given in extensions available to the handlers and templates

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1552

def helpers(*extensions, &block)
  class_eval(&block)   if block_given?
  include(*extensions) if extensions.any?
end

.host_name(pattern) (private)

Condition for matching host name. Parameter might be String or Regexp.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1733

def host_name(pattern)
  condition { pattern === request.host }
end

.inherited(subclass) (private)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1879

def inherited(subclass)
  subclass.reset!
  subclass.set :app_file, caller_files.first unless subclass.app_file?
  super
end

.inline_templates=(file = nil)

Load embedded templates from the file; uses the caller's FILE when no file is specified.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1421

def inline_templates=(file = nil)
  file = (caller_files.first || File.expand_path($0)) if file.nil? || file == true

  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

  return unless data

  encoding = if app && app =~ /([^\n]*\n)?#[^\n]*coding: *(\S+)/m
               $2
             else
               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(String.new, encoding)
      templates[$1.to_sym] = [template, file, lines]
    elsif template
      template << line
    end
  end
end

.invoke_hook(name, *args) (private)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1779

def invoke_hook(name, *args)
  extensions.each { |e| e.send(name, *args) if e.respond_to?(name) }
end

.layout(name = :layout, &block)

Define the layout template. The block must return the template source.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1415

def layout(name = :layout, &block)
  template name, &block
end

.methodoverride=

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1957

alias methodoverride= method_override=

.methodoverride?

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1956

alias methodoverride? method_override?

.middleware

Middleware used in this class and all superclasses.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1332

def middleware
  if superclass.respond_to?(:middleware)
    superclass.middleware + @middleware
  else
    @middleware
  end
end

.mime_type(type, value = nil)

Lookup or register a mime type in Rack's mime registry.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1454

def mime_type(type, value = nil)
  return type      if type.nil?
  return type.to_s if 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

.mime_types(type)

provides all mime types matching type, including deprecated types: mime_types :html # => ['text/html'] mime_types :js # => ['application/javascript', 'text/javascript']

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1467

def mime_types(type)
  type = mime_type type
  if type =~ %r{^application/(xml|javascript)$}
    [type, "text/#{$1}"]
  elsif type =~ %r{^text/(xml|javascript)$}
    [type, "application/#{$1}"]
  else
    [type]
  end
end

.new!(app = nil, **_kwargs)

Alias for .new.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1652

alias new! new unless method_defined? :new!

.not_found(&block)

Sugar for error(404) { ... }

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1404

def not_found(&block)
  error(404, &block)
end

.on_start(&on_start_callback)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1497

def on_start(&on_start_callback)
  @on_start_callback = on_start_callback
end

.on_stop(&on_stop_callback)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1501

def on_stop(&on_stop_callback)
  @on_stop_callback = on_stop_callback
end

.options(path, opts = {}, &block)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1542

def options(path, opts = {}, &block) route 'OPTIONS', path, opts, &block end

.patch(path, opts = {}, &block)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1544

def patch(path, opts = {}, &block)   route 'PATCH',   path, opts, &block end

.post(path, opts = {}, &block)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1536

def post(path, opts = {}, &block)    route 'POST',    path, opts, &block end

.prototype

The prototype instance used to process requests.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1647

def prototype
  @prototype ||= new
end

.provides(*types) (private)

Condition for matching mimetypes. Accepts file extensions.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1752

def provides(*types)
  types.map! { |t| mime_types(t) }
  types.flatten!
  condition do
    response_content_type = response['content-type']
    preferred_type = request.preferred_type(types)

    if response_content_type
      types.include?(response_content_type) || types.include?(response_content_type[/^[^;]+/])
    elsif preferred_type
      params = (preferred_type.respond_to?(:params) ? preferred_type.params : {})
      content_type(preferred_type, params)
      true
    else
      false
    end
  end
end

.put(path, opts = {}, &block)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1534

def put(path, opts = {}, &block)     route 'PUT',     path, opts, &block end

.quit! Also known as: .stop!

Stop the self-hosted server if running.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1586

def quit!
  return unless running?

  # Use Thin's hard #stop! if available, otherwise just #stop.
  running_server.respond_to?(:stop!) ? running_server.stop! : running_server.stop
  warn '== Sinatra has ended his set (crowd applauds)' unless suppress_messages?
  set :running_server, nil
  set :handler_name, nil

  on_stop_callback.call unless on_stop_callback.nil?
end

.register(*extensions, &block)

Register an extension. Alternatively take a block from which an extension will be created and registered on the fly.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1559

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

.reset!

Removes all routes, filters, middleware and extension hooks from the current class (not routes/filters/... defined by its superclass).

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1306

def reset!
  @conditions     = []
  @routes         = {}
  @filters        = { before: [], after: [] }
  @errors         = {}
  @middleware     = []
  @prototype      = nil
  @extensions     = []

  @templates = if superclass.respond_to?(:templates)
                 Hash.new { |_hash, key| superclass.templates[key] }
               else
                 {}
               end
end

.route(verb, path, options = {}, &block) (private)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1771

def route(verb, path, options = {}, &block)
  enable :empty_path_info if path == '' && empty_path_info.nil?
  signature = compile!(verb, path, block, **options)
  (@routes[verb] ||= []) << signature
  invoke_hook(:route_added, verb, path, block)
  signature
end

.run!(options = {}, &block) Also known as: .start!

Run the ::Sinatra app as a self-hosted server using Puma, Falcon, or WEBrick (in that order). If given a block, will call with the constructed handler once we have taken the stage.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1603

def run!(options = {}, &block)
  unless defined?(Rackup::Handler)
    rackup_warning = <<~MISSING_RACKUP
      Sinatra could not start, the "rackup" gem was not found!

      Add it to your bundle with:

          bundle add rackup

      or install it with:

          gem install rackup

    MISSING_RACKUP
    warn rackup_warning
    exit 1
  end

  return if running?

  set options
  handler         = Rackup::Handler.pick(server)
  handler_name    = handler.name.gsub(/.*::/, '')
  server_settings = settings.respond_to?(:server_settings) ? settings.server_settings : {}
  server_settings.merge!(Port: port, Host: bind)

  begin
    start_server(handler, server_settings, handler_name, &block)
  rescue Errno::EADDRINUSE
    warn "== Someone is already performing on port #{port}!"
    raise
  ensure
    quit!
  end
end

.set(option, value = (not_set = true), ignore_setter = false, &block)

Sets an option to the given value. If the value is a proc, the proc will be called every time the option is accessed.

Raises:

  • (ArgumentError)
[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1342

def set(option, value = (not_set = true), ignore_setter = false, &block)
  raise ArgumentError if block && !not_set

  if block
    value = block
    not_set = false
  end

  if not_set
    raise ArgumentError unless option.respond_to?(:each)

    option.each { |k, v| set(k, v) }
    return self
  end

  if respond_to?("#{option}=") && !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, Integer, 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("#{option}=", setter)
  define_singleton(option, getter)
  define_singleton("#{option}?", "!!#{option}") unless method_defined? "#{option}?"
  self
end

.settings

Access settings defined with Base.set.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1016

def self.settings
  self
end

.setup_common_logger(builder) (private)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1841

def setup_common_logger(builder)
  builder.use Sinatra::CommonLogger
end

.setup_custom_logger(builder) (private)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1845

def setup_custom_logger(builder)
  if logging.respond_to? :to_int
    builder.use Rack::Logger, logging
  else
    builder.use Rack::Logger
  end
end

.setup_default_middleware(builder) (private)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1814

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

.setup_logging(builder) (private)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1828

def setup_logging(builder)
  if logging?
    setup_common_logger(builder)
    setup_custom_logger(builder)
  elsif logging == false
    setup_null_logger(builder)
  end
end

.setup_middleware(builder) (private)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1824

def setup_middleware(builder)
  middleware.each { |c, a, b| builder.use(c, *a, &b) }
end

.setup_null_logger(builder) (private)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1837

def setup_null_logger(builder)
  builder.use Rack::NullLogger
end

.setup_protection(builder) (private)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1853

def setup_protection(builder)
  return unless protection?

  options = Hash === protection ? protection.dup : {}
  options = {
    img_src: "'self' data:",
    font_src: "'self'"
  }.merge options

  protect_session = options.fetch(:session) { sessions? }
  options[:without_session] = !protect_session

  options[:reaction] ||= :drop_session

  builder.use Rack::Protection, options
end

.setup_sessions(builder) (private)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1870

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 session_store, options
end

.setup_traps (private)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1709

def setup_traps
  return unless traps?

  at_exit { quit! }

  %i[INT TERM].each do |signal|
    old_handler = trap(signal) do
      quit!
      old_handler.call if old_handler.respond_to?(:call)
    end
  end

  set :traps, false
end

.start!(options = {}, &block)

Alias for .run!.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1639

alias start! run!

.start_server(handler, server_settings, handler_name) (private)

Starts the server by running the ::Rack Handler.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1686

def start_server(handler, server_settings, handler_name)
  # Ensure we initialize middleware before startup, to match standard Rack
  # behavior, by ensuring an instance exists:
  prototype
  # Run the instance we created:
  handler.run(self, **server_settings) do |server|
    unless suppress_messages?
      warn "== Sinatra (v#{Sinatra::VERSION}) has taken the stage on #{port} for #{environment} with backup from #{handler_name}"
    end

    setup_traps
    set :running_server, server
    set :handler_name,   handler_name
    server.threaded = settings.threaded if server.respond_to? :threaded=
    on_start_callback.call unless on_start_callback.nil?
    yield server if block_given?
  end
end

.stop!

Alias for .quit!.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1598

alias stop! quit!

.synchronize(&block) (private)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1886

def synchronize(&block)
  if lock?
    @@mutex.synchronize(&block)
  else
    yield
  end
end

.template(name, &block)

Define a named template. The block must return the template source.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1409

def template(name, &block)
  filename, line = caller_locations.first
  templates[name] = [block, filename, line.to_i]
end

.use(middleware, *args, &block)

Use the specified ::Rack middleware

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1579

def use(middleware, *args, &block)
  @prototype = nil
  @middleware << [middleware, args, block]
end

.user_agent(pattern) (private) Also known as: .agent

Condition for matching user agent. Parameter should be Regexp. Will set params[:agent].

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1739

def user_agent(pattern)
  condition do
    if request.user_agent.to_s =~ pattern
      @params[:agent] = $~[1..-1]
      true
    else
      false
    end
  end
end

.warn_for_deprecation(message) (private)

used for deprecation warnings

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1895

def warn_for_deprecation(message)
  warn message + "\n\tfrom #{cleaned_caller.first.join(':')}"
end

Instance Attribute Details

#app (rw)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 977

attr_accessor :app, :env, :request, :response, :params

#env (rw)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 977

attr_accessor :app, :env, :request, :response, :params

#params (rw)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 977

attr_accessor :app, :env, :request, :response, :params

#request (rw)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 977

attr_accessor :app, :env, :request, :response, :params

#response (rw)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 977

attr_accessor :app, :env, :request, :response, :params

#template_cache (readonly)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 978

attr_reader   :template_cache

Instance Method Details

#call(env)

::Rack call interface.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 989

def call(env)
  dup.call!(env)
end

#call!(env)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 993

def call!(env) # :nodoc:
  @env      = env
  @params   = IndifferentHash.new
  @request  = Request.new(env)
  @response = Response.new
  @pinned_response = nil
  template_cache.clear if settings.reload_templates

  invoke { dispatch! }
  invoke { error_block!(response.status) } unless @env['sinatra.error']

  unless @response['content-type']
    if Array === body && body[0].respond_to?(:content_type)
      content_type body[0].content_type
    elsif (default = settings.default_content_type)
      content_type default
    end
  end

  @response.finish
end

#dispatch! (private)

Dispatch a request with error handling.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1176

def dispatch!
  # Avoid passing frozen string in force_encoding
  @params.merge!(@request.params).each do |key, val|
    next unless val.respond_to?(:force_encoding)

    val = val.dup if val.frozen?
    @params[key] = force_encoding(val)
  end

  invoke do
    static! if settings.static? && (request.get? || request.head?)
    filter! :before do
      @pinned_response = !response['content-type'].nil?
    end
    route!
  end
rescue ::Exception => e
  invoke { handle_exception!(e) }
ensure
  begin
    filter! :after unless env['sinatra.static_file']
  rescue ::Exception => e
    invoke { handle_exception!(e) } unless @env['sinatra.error']
  end
end

#dump_errors!(boom) (private)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1268

def dump_errors!(boom)
  if boom.respond_to?(:detailed_message)
    msg = boom.detailed_message(highlight: false)
    if msg =~ /\A(.*?)(?: \(#{ Regexp.quote(boom.class.to_s) }\))?\n/
      msg = $1
      additional_msg = $'.lines(chomp: true)
    else
      additional_msg = []
    end
  else
    msg = boom.message
    additional_msg = []
  end
  msg = ["#{Time.now.strftime('%Y-%m-%d %H:%M:%S')} - #{boom.class} - #{msg}:", *additional_msg, *boom.backtrace].join("\n\t")
  @env['rack.errors'].puts(msg)
end

#error_block!(key, *block_params) (private)

Find an custom error block for the key(s) specified.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1249

def error_block!(key, *block_params)
  base = settings
  while base.respond_to?(:errors)
    args_array = base.errors[key]

    next base = base.superclass unless args_array

    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) && (key.superclass < Exception)

  error_block!(key.superclass, *block_params)
end

#filter!(type, base = settings, &block) (private)

Run filters defined on the class and all superclasses. Accepts an optional block to call after each filter is applied.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1054

def filter!(type, base = settings, &block)
  filter!(type, base.superclass, &block) if base.superclass.respond_to?(:filters)
  base.filters[type].each do |args|
    result = process_route(*args)
    block.call(result) if block_given?
  end
end

#force_encoding(*args) (private)

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1922

def force_encoding(*args)
  settings.force_encoding(*args)
end

#forward

Forward the request to the downstream app -- middleware only.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1040

def forward
  raise '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

#halt(*response)

Exit the current block, halts any further processing of the request, and returns the specified response.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1027

def halt(*response)
  response = response.first if response.length == 1
  throw :halt, response
end

#handle_exception!(boom) (private)

Error handling during requests.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1203

def handle_exception!(boom)
  error_params = @env['sinatra.error.params']

  @params = @params.merge(error_params) if error_params

  @env['sinatra.error'] = boom

  http_status = if boom.is_a? Sinatra::Error
                  if boom.respond_to? :http_status
                    boom.http_status
                  elsif settings.use_code? && boom.respond_to?(:code)
                    boom.code
                  end
                end

  http_status = 500 unless http_status&.between?(400, 599)
  status(http_status)

  if server_error?
    dump_errors! boom if settings.dump_errors?
    raise boom if settings.show_exceptions? && (settings.show_exceptions != :after_handler)
  elsif not_found?
    headers['X-Cascade'] = 'pass' if settings.x_cascade?
  end

  if (res = error_block!(boom.class, boom) || error_block!(status, boom))
    return res
  end

  if not_found? || bad_request?
    if boom.message && boom.message != boom.class.name
      body Rack::Utils.escape_html(boom.message)
    else
      content_type 'text/html'
      body "<h1>#{not_found? ? 'Not Found' : 'Bad Request'}</h1>"
    end
  end

  return unless server_error?

  raise boom if settings.raise_errors? || settings.show_exceptions?

  error_block! Exception, boom
end

#invoke(&block) (private)

Run the block with 'throw :halt' support and apply result to the response.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1160

def invoke(&block)
  res = catch(:halt, &block)

  res = [res] if (Integer === res) || (String === res)
  if (Array === res) && (Integer === res.first)
    res = res.dup
    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

#pass(&block)

Pass control to the next matching route. If there are no more matching routes, ::Sinatra will return a 404 response.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1035

def pass(&block)
  throw :pass, block
end

#process_route(pattern, conditions, block = nil, values = []) (private)

If the current request matches pattern and conditions, fill params with keys and call the given block. Revert params afterwards.

Returns pass block.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1097

def process_route(pattern, conditions, block = nil, values = [])
  route = @request.path_info
  route = '/' if route.empty? && !settings.empty_path_info?
  route = route[0..-2] if !settings.strict_paths? && route != '/' && route.end_with?('/')

  params = pattern.params(route)
  return unless params

  params.delete('ignore') # TODO: better params handling, maybe turn it into "smart" object or detect changes
  force_encoding(params)
  @params = @params.merge(params) { |_k, v1, v2| v2 || v1 } if params.any?

  regexp_exists = pattern.is_a?(Mustermann::Regular) || (pattern.respond_to?(:patterns) && pattern.patterns.any? { |subpattern| subpattern.is_a?(Mustermann::Regular) })
  if regexp_exists
    captures           = pattern.match(route).captures.map { |c| URI_INSTANCE.unescape(c) if c }
    values            += captures
    @params[:captures] = force_encoding(captures) unless captures.nil? || captures.empty?
  else
    values += params.values.flatten
  end

  catch(:pass) do
    conditions.each { |c| throw :pass if c.bind(self).call == false }
    block ? block[self, values] : yield(self, values)
  end
rescue StandardError
  @env['sinatra.error.params'] = @params
  raise
ensure
  params ||= {}
  params.each { |k, _| @params.delete(k) } unless @env['sinatra.error.params']
end

#route!(base = settings, pass_block = nil) (private)

Run routes defined on the class and all superclasses.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1063

def route!(base = settings, pass_block = nil)
  routes = base.routes[@request.request_method]

  routes&.each do |pattern, conditions, block|
    response.delete_header('content-type') unless @pinned_response

    returned_pass_block = process_route(pattern, conditions) do |*args|
      env['sinatra.route'] = "#{@request.request_method} #{pattern}"
      route_eval { block[*args] }
    end

    # don't wipe out pass_block in superclass
    pass_block = returned_pass_block if returned_pass_block
  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

#route_eval (private)

Run a route block and throw :halt with the result.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1088

def route_eval
  throw :halt, yield
end

#route_missing (private)

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.

Raises:

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1135

def route_missing
  raise NotFound unless @app

  forward
end

#settings

Access settings defined with Base.set.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1021

def settings
  self.class.settings
end

#static!(options = {}) (private)

Attempt to serve static files from public directory. Throws :halt when a matching file is found, returns nil otherwise.

[ GitHub ]

  
# File 'lib/sinatra/base.rb', line 1143

def static!(options = {})
  return if (public_dir = settings.public_folder).nil?

  path = "#{public_dir}#{URI_INSTANCE.unescape(request.path_info)}"
  return unless valid_path?(path)

  path = File.expand_path(path)
  return unless path.start_with?("#{File.expand_path(public_dir)}/")

  return unless File.file?(path)

  env['sinatra.static_file'] = path
  cache_control(*settings.static_cache_control) if settings.static_cache_control?
  send_file path, options.merge(disposition: nil)
end