Controllers in Caboose are inspired by rails and will look very similar.
Controllers are the glue that connect the views with the routing system. They are primarily responsible for dealing with models, collecting and arranging data, backend routing, and view determination. You should aim to have short controllers and embed most of your logic in other functional units like models or helpers and just coordinate and translate in the controller.
Caboose applications start out with an ApplicationController which serves to be the root controller that all others in
your application inherit from. This is not necessary, but is a good pattern to follow. The ApplicationController is a place to
put all of the filters and helpers that will be common to all controllers within your application.
The ApplicationController inherits from Controller which contains the necessary methods to respond to requests. You can
either extend Controller directly or extend another controller.
To create a new controller, you can either use the command line
$ caboose generate controller Posts
or just create a new file in the app/controllers directory.
import 'ApplicationController'
class PostsController extends ApplicationController
index: ->
@render()
Please note that naming conventions are very important here. If you are creating controllers by hand, be sure to put them in the app/controllers directory and name the file in all lowercase letters as [controller name]_controller.coffee. This should correspond to the controller class, which should always have Controller at the end of it and be camel-case. So PostsController would correspond to posts_controller.coffee, and UniqueUsersController would correspond to unique_users_controller.coffee.
Methods on a controller correspond to a route action. When caboose finds an appropriate route for a request, it will instantiate the controller and call the action named in the route. For instance, if you have a route to the posts controller's index action:
modules.exports = ->
@route '/posts', 'posts#index'
and you make a request to http://your-server/posts, caboose will first create the PostsController and then execute the index
method. If the index method looks like above, then it will simply render the page without taking any further actions.
There are a few ways that you can respond to a request from within a controller. The most common will be @render
and @redirect_to.
The main response method. All other methods are just sugar.
Responds with a 302 redirection to the path of your choosing.
The optional flash object will be available on the next rendered page.
@redirect_to '/authorized_path', {success: 'Welcome to my website!'}
Responds with a 404, optionally taking an error object.
Passes the error to the next middleware in the stack. The default stack will respond with a 500 and print out the error.
Responds with a 401, optionally taking an error object.
The request object as it comes back from express.
The response object as it comes back from express.
An object with the current request's headers.
An object with the current request's cookies.
An object containing the current session.
The body of a request when the encoding is multipart.
An object that contains the parameters of the current request. This is populated by the routing system.
An object with the URL query parameters broken out. For example, http://www.caboosejs.com?foo=bar&page=1 will result in a @query object that looks like
{
foo: 'bar',
page: '1'
}
Make an object's methods available to the view. Check out the helper docs.
Execute a method before an action is executed.
import 'ApplicationController'
class UsersController extends ApplicationController
before_action (next) ->
console.log "Request to #{@request.url}"
next()
before_action 'something_in_application_controller'
before_action 'filter_something', {only: ['index']}
before_action ((next) ->
console.log 'This is not the index action'
next()
), {except: ['index']}
filter_something: (next) ->
return next(new Error()) unless @params.foobar?
next()
index: -> @render()
Here we will create a resources route for our blog posts and another route that will map the root route to the PostsController index route.
module.exports = ->
@resources 'posts'
@route '/', 'posts#index'
We have an ApplicationController with a filter method that can be shared by all controllers who extend ApplicationController.
We're using the caboose-model plugin to query for the post specified by the id
parameter, setting the successful response to an instance variable to be used by the actions later.
import 'Post'
class ApplicationController extends Controller
posts_filter: (next) ->
Post.where(_id: @params.id).first (err, post) =>
return next(err) if err?
return next(new Error("Could not find post with id #{@params.id}")) unless post?
@post = post
next()
Finally, we create a PostsController to handle the resources actions. We're telling the controller to filter the show, edit, update
and destroy methods with the posts_filter from the ApplicationController to find the current post. We've also implemented
the index and show actions here. The show action will already have @post set from the posts_filter, so all it does is
render the view (which will be located at app/views/posts/show.html.[view-engine]).
The index action is a bit more complex, and implements a rudimentary form of paging. It is pulling the page and
page_size from the @query and then using that to skip and limit the post records. Finally, we render
the index view (which will be located at app/views/posts/index.html.[view-engine]).
import 'Post'
import 'ApplicationController'
class PostsController extends ApplicationController
before_action 'posts_filter', {only: ['show', 'edit', 'update', 'destroy']}
index: ->
page_size = @query.page_size || 10
page = @query.page || 1
Post.skip(page_size * (page - 1)).limit(page_size).array (err, posts) =>
return @error(err) if err?
@posts = posts
@render()
show: -> @render()
# handle other actions...