The Controller

Scorched consists almost entirely of the Scorched::Controller. The Controller is the class from which your application class inherits. All the code examples provided in the documentation are assumed to be wrapped within a controller class.

class MyApp < Scorched::Controller
  # We are now within the controller class.
  # Most examples are assumed to be within this context.
end

Your application's root controller (named MyApp in the example above), should be configured as the run target in your rackup file:

# config.ru
require './myapp.rb'
run MyApp

Sub-Controllers

One of the core features of Scorched is the fact that Controller's are intended to be inheritable and nestable as sub-controllers. This is perhaps the main advantage of Scorched over the likes of Sinatra and Padrino, and is one of the key innovations of the framework.

For the most part, you will find yourself breaking up your application into many discrete controllers. This will allow you to scope your configuration, filters, error handlers, and conditions, to logical groups of routes. This can save a lot of code repetition, keeping your code DRY, hence the name Scorched.

A sub-controller is any controller that's mapped by another controller. ControllerB in the following example, is one example of a sub-controller.

class ControllerA < Scorched::Controller
  get '/' do
    "Hello there"
  end

  after do
    response.body[0] << '.' # Always forgetting to add my periods
  end
end

class ControllerB < Scorched::Controller
  get '/' do
    "I'm apparently a sub-controller"
  end
end

ControllerA << {pattern: '/sub', target: ControllerB}

The previous example is awfully crude, but it hopefully demonstrates that there's absolutely no magic happening here. In that previous example, a request for /sub will pass through ControllerA and onto ControllerB, before heading back out the way it came. Hence, a request for /sub will yield I'm apparently a sub-controller.; note the period added by ControllerA's after filter (more on after filters later).

While in the previous example, ControllerB counts as a sub-controller in that it's nested within ControllerA, at least from a routing perspective, ControllerA and ControllerB are completely unrelated; they share nothing.

A lot can be gained by not only nesting ControllerB within ControllerA, but by also inheriting from it.

class ControllerA < Scorched::Controller
  render_defaults[:dir] = 'views'
  render_defaults[:layout] = :main

  conditions[:user] = proc { |usernames|
    [*usernames].include? session['username']
  }


  get '/' do
    bold "Hello there"
  end

  def bold(str)
    "<strong>#{str}</strong>"
  end
end

class ControllerB < ControllerA
  render_defaults[:layout] = :controller_b

  get '/', user: 'bob' do
    bold "I'm apparently a sub-controller"
  end
end

ControllerA << {pattern: '/sub', target: ControllerB}

Now that ControllerB is inheriting from ControllerA, it not only gets access to all its methods, such as the very helpful bold, but it also inherits anything configured on ControllerA, such as rendering defaults, filters, middleware, conditions, etc. Very handy.

You can use nesting and inheritance exclusively, or together, depending on your needs.

Controller Helper

There is a more succinct way of mapping and defining sub-controllers, and that's with the controller helper. Here's the previous example cut-down and re-written using the controller helper.

class MyApp < Scorched::Controller
  get '/' do
    bold "Hello there"
  end

  controller '/sub' do
    render_defaults[:layout] = :controller_b

    get '/', user: ['bob', 'jeff'] do
      bold "I'm apparently a sub-controller"
    end
  end

  def bold(str)
    "<strong>#{str}</strong>"
  end
end

The controller helper takes an optional URL pattern as it's first argument, an optional parent class as its second, and finally a mapping hash as its third optional argument, where you can define a priority, conditions, or override the URL pattern. Of course, the controller helper takes a block as well, which defines the body of the new controller class.

The optional URL pattern defaults to '/' which means it's essentially a match-all mapping. In addition, the generated controller has :auto_pass set to true by default (refer to configuration documentation for more information). This is a handy combination for grouping a set of routes in their own scope, with their own methods, filters, configuration, etc.

class MyApp < Scorched::Controller
  get '/' do
    format "Hello there"
  end

  controller do 
    before { response['Content-Type'] = 'text/plain' }

    get '/hello' do
      'Hello'
    end

    def emphasise(str)
      "**#{str}**"
    end
  end

  get '/goodbye' do
   'Goodbye'
  end

  after { response.body = emphasise(response.body.join) }

  def emphasise(str)
    "<strong>#{str}</strong>"
  end
end

That example, while serving no practical purpose, hopefully demonstrates how you can combine various constructs with sub-controllers, to come up with DRY creative solutions.

Finally, the controller helper can also be used a shortcut to map one controller to another, where the given class is mapped directly, instead of being the parent of a new controller class. These two examples are essentially equivalent:

ControllerA << {pattern: '/sub', target: ControllerB}

# Or

class ControllerA
  controller '/', ControllerB # Note that no block was given
end

The Root Controller

Although you will likely have a main controller to serve as the target for Rack, Scorched does not have the concept of a root controller. It makes no differentiation between a sub-controller and any other controller. All Controllers are made equal.

You can arrange and nest your controllers in any way, shape or form. Scorched has been designed to not make any assumptions about how you structure your controllers, which again, can accommodate creative solutions.