Class: Sinatra::Base

Inherits:
Object
  • Object
show all
Includes:
Rack::Utils, Helpers, Templates
Defined in:
lib/sinatra/base.rb

Overview

Base class for all Sinatra applications and middleware.

Direct Known Subclasses

Application

Constant Summary collapse

URI_INSTANCE =
defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::RFC2396_Parser.new
CALLERS_TO_IGNORE =

:nodoc:

[ # :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/(core_ext/)?kernel\.rb}                 # Zeitwerk kernel#require decorator
].freeze

Constants included from Helpers

Helpers::ETAG_KINDS, Helpers::MULTIPART_FORM_DATA_REPLACEMENT_TABLE

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Templates

#asciidoc, #builder, #erb, #find_template, #haml, #liquid, #markaby, #markdown, #nokogiri, #rabl, #rdoc, #sass, #scss, #slim, #yajl

Methods included from Helpers

#attachment, #back, #bad_request?, #body, #cache_control, #client_error?, #content_type, #error, #etag, #expires, #headers, #informational?, #last_modified, #logger, #mime_type, #not_found, #not_found?, #redirect, #redirect?, #send_file, #server_error?, #session, #status, #stream, #success?, #time_for, #uri

Constructor Details

#initialize(app = nil, **_kwargs) {|_self| ... } ⇒ Base

Returns a new instance of Base.

Yields:

  • (_self)

Yield Parameters:

  • _self (Sinatra::Base)

    the object that the method was called on



983
984
985
986
987
988
989
# File 'lib/sinatra/base.rb', line 983

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

.errorsObject (readonly)

Returns the value of attribute errors.



1301
1302
1303
# File 'lib/sinatra/base.rb', line 1301

def errors
  @errors
end

.filtersObject (readonly)

Returns the value of attribute filters.



1301
1302
1303
# File 'lib/sinatra/base.rb', line 1301

def filters
  @filters
end

.on_start_callbackObject (readonly)

Returns the value of attribute on_start_callback.



1301
1302
1303
# File 'lib/sinatra/base.rb', line 1301

def on_start_callback
  @on_start_callback
end

.on_stop_callbackObject (readonly)

Returns the value of attribute on_stop_callback.



1301
1302
1303
# File 'lib/sinatra/base.rb', line 1301

def on_stop_callback
  @on_stop_callback
end

.routesObject (readonly)

Returns the value of attribute routes.



1301
1302
1303
# File 'lib/sinatra/base.rb', line 1301

def routes
  @routes
end

.templatesObject (readonly)

Returns the value of attribute templates.



1301
1302
1303
# File 'lib/sinatra/base.rb', line 1301

def templates
  @templates
end

Instance Attribute Details

#appObject

Returns the value of attribute app.



980
981
982
# File 'lib/sinatra/base.rb', line 980

def app
  @app
end

#envObject

Returns the value of attribute env.



980
981
982
# File 'lib/sinatra/base.rb', line 980

def env
  @env
end

#paramsObject

Returns the value of attribute params.



980
981
982
# File 'lib/sinatra/base.rb', line 980

def params
  @params
end

#requestObject

Returns the value of attribute request.



980
981
982
# File 'lib/sinatra/base.rb', line 980

def request
  @request
end

#responseObject

Returns the value of attribute response.



980
981
982
# File 'lib/sinatra/base.rb', line 980

def response
  @response
end

#template_cacheObject (readonly)

Returns the value of attribute template_cache.



981
982
983
# File 'lib/sinatra/base.rb', line 981

def template_cache
  @template_cache
end

Class Method Details

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

add a filter



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

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

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

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



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

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

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

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



1484
1485
1486
# File 'lib/sinatra/base.rb', line 1484

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

.build(app) ⇒ Object

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



1668
1669
1670
1671
1672
1673
1674
# File 'lib/sinatra/base.rb', line 1668

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

.call(env) ⇒ Object



1676
1677
1678
# File 'lib/sinatra/base.rb', line 1676

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

.caller_filesObject

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



1682
1683
1684
# File 'lib/sinatra/base.rb', line 1682

def caller_files
  cleaned_caller(1).flatten
end

.callers_to_ignoreObject



1303
1304
1305
# File 'lib/sinatra/base.rb', line 1303

def callers_to_ignore
  CALLERS_TO_IGNORE
end

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

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



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

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

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

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

Yields:

  • (_self)

Yield Parameters:

  • _self (Sinatra::Base)

    the object that the method was called on



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

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

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



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

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

.development?Boolean

Returns:

  • (Boolean)


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

def development?; environment == :development end

.disable(*opts) ⇒ Object

Same as calling set :option, false for each of the given options.



1391
1392
1393
# File 'lib/sinatra/base.rb', line 1391

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

.enable(*opts) ⇒ Object

Same as calling set :option, true for each of the given options.



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

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

.error(*codes, &block) ⇒ Object

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



1398
1399
1400
1401
1402
1403
1404
# File 'lib/sinatra/base.rb', line 1398

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

.extensionsObject

Extension modules registered on this class and all superclasses.



1326
1327
1328
1329
1330
1331
1332
# File 'lib/sinatra/base.rb', line 1326

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

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

Defining a GET handler also automatically defines a HEAD handler.



1529
1530
1531
1532
1533
1534
1535
# File 'lib/sinatra/base.rb', line 1529

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) ⇒ Object



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

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

.helpers(*extensions, &block) ⇒ Object

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



1555
1556
1557
1558
# File 'lib/sinatra/base.rb', line 1555

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

.inline_templates=(file = nil) ⇒ Object

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



1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
# File 'lib/sinatra/base.rb', line 1424

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

.layout(name = :layout, &block) ⇒ Object

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



1418
1419
1420
# File 'lib/sinatra/base.rb', line 1418

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


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

def link(path, opts = {}, &block)    route 'LINK',    path, opts, &block end

.middlewareObject

Middleware used in this class and all superclasses.



1335
1336
1337
1338
1339
1340
1341
# File 'lib/sinatra/base.rb', line 1335

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

.mime_type(type, value = nil) ⇒ Object

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



1457
1458
1459
1460
1461
1462
1463
1464
1465
# File 'lib/sinatra/base.rb', line 1457

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) ⇒ Object

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



1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
# File 'lib/sinatra/base.rb', line 1470

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(*args, &block) ⇒ Object

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.



1660
1661
1662
1663
# File 'lib/sinatra/base.rb', line 1660

def new(*args, &block)
  instance = new!(*args, &block)
  Wrapper.new(build(instance).to_app, instance)
end

.not_found(&block) ⇒ Object

Sugar for error(404) { ... }



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

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

.on_start(&on_start_callback) ⇒ Object



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

def on_start(&on_start_callback)
  @on_start_callback = on_start_callback
end

.on_stop(&on_stop_callback) ⇒ Object



1504
1505
1506
# File 'lib/sinatra/base.rb', line 1504

def on_stop(&on_stop_callback)
  @on_stop_callback = on_stop_callback
end

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



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

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

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



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

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

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



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

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

.production?Boolean

Returns:

  • (Boolean)


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

def production?;  environment == :production  end

.prototypeObject

The prototype instance used to process requests.



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

def prototype
  @prototype ||= new
end

.public=(value) ⇒ Object



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

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_dirObject



1523
1524
1525
# File 'lib/sinatra/base.rb', line 1523

def public_dir
  public_folder
end

.public_dir=(value) ⇒ Object



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

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

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



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

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

.quit!Object Also known as: stop!

Stop the self-hosted server if running.



1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
# File 'lib/sinatra/base.rb', line 1589

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) ⇒ Object

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



1562
1563
1564
1565
1566
1567
1568
1569
# File 'lib/sinatra/base.rb', line 1562

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!Object

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



1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
# File 'lib/sinatra/base.rb', line 1309

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

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

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



1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
# File 'lib/sinatra/base.rb', line 1606

def run!(options = {}, &block)
  unless defined?(Rackup::Handler)
    rackup_warning = <<~MISSING_RACKUP
      Sinatra could not start, the required gems weren't found!

      Add them to your bundle with:

          bundle add rackup puma

      or install them with:

          gem install rackup puma

    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

.running?Boolean

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

Returns:

  • (Boolean)


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

def running?
  running_server?
end

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

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)


1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
# File 'lib/sinatra/base.rb', line 1345

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

.settingsObject

Access settings defined with Base.set.



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

def self.settings
  self
end

.template(name, &block) ⇒ Object

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



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

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

.test?Boolean

Returns:

  • (Boolean)


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

def test?;        environment == :test        end


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

def unlink(path, opts = {}, &block)  route 'UNLINK',  path, opts, &block end

.use(middleware, *args, &block) ⇒ Object

Use the specified Rack middleware



1582
1583
1584
1585
# File 'lib/sinatra/base.rb', line 1582

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

Instance Method Details

#call(env) ⇒ Object

Rack call interface.



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

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

#call!(env) ⇒ Object

:nodoc:



996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
# File 'lib/sinatra/base.rb', line 996

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

#forwardObject

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



1043
1044
1045
1046
1047
1048
1049
1050
1051
# File 'lib/sinatra/base.rb', line 1043

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) ⇒ Object

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



1030
1031
1032
1033
# File 'lib/sinatra/base.rb', line 1030

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

#new!Object

Create a new instance without middleware in front of it.



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

alias new! new

#pass(&block) ⇒ Object

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



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

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

#settingsObject

Access settings defined with Base.set.



1024
1025
1026
# File 'lib/sinatra/base.rb', line 1024

def settings
  self.class.settings
end