We already have a working environment – it’s the best possible moment to get started with teh codez!

Yesod, like many other web frameworks, has a Model-View-Controller architecture. It has a central controller instance which routes requests to proper handlers. In the scaffolded site these routes are conveniently stored in config/routes file. If you didn’t make any changes to it, it probably looks like this:

/static StaticR Static appStatic
/auth   AuthR   Auth   getAuth

/favicon.ico FaviconR GET
/robots.txt RobotsR GET

/ HomeR GET POST

/comments CommentR POST

it contains a lot of magic. It is a syntax which is later processed by Template Haskell to generate quite some code that guarantees type safety. For starters let’s skip first two routes and focus on the rest.

The syntax is fairly simple: route route-type handled-methods. So, for example, line /comments CommentR POST means: from all requests directed to route /comments accept only POST requests and define type CommentR which will be used to redirect to this route (in Yesod routes are type-safe, so you need types for them). Additional feature is that route handlers are invoked automatically – in out example it would be postCommentR function (you can find its implementation in scaffolded site in Handler/Comment.hs file. Handler names are created from lowercase method name concatenated with route type name. It’s a nice example of Convention over Configuration pattern, and it frees us from writing an awful amount of code which we don’t really want to write (at least I don’t). Adding R postfix (shortcut for Route) to route type is also a Yesod convention, which does not need to be kept, but may help others read your code.

Ok, enough chit-chat, let’s write some codez!

First, let’s edit config/routes. Just stuff in there /projects ProjectsR GET. Great, now your code doesn’t compile and complains about lack of getProjectsR function. Let’s fix it the way scaffolded site does it – create a Handler/Projects.hs file with the following content:

module Handler.Projects where
import Import

import Yesod.Form.Bootstrap3()

getProjectsR :: Handler Html
getProjectsR = defaultLayout $ do
    setTitle "Projects"

add the handler to <code>Application.hs</code> as well:

-- Import all relevant handler modules here.
-- Don't forget to add new modules to your cabal file!
import Handler.Common
import Handler.Home

-- new one
import Handler.Projects

as the comment says, you also need to add this file to your <code>.cabal</code> file to this block:

        exposed-modules: Application
                     Foundation
                     Import
                     Import.NoFoundation
                     Model
                     Settings
                     Settings.StaticFiles
                     Handler.Common
                     Handler.Home
                     Handler.Comment
                     Handler.Projects

that’s important, because if you won’t do that, you’ll get an unknown symbol error at startup. Now run stack exec -- yesod devel and go to localhost:3000/projects. The result might not be very impressive (an empty page), but you might guess that it’s already using our route – it has our title (not very impressive either).

That’s all nice and fine, but if you take a look at the page source, you’ll notice that there’s quite some stuff already, including Bootstrap and jQuery. Where did it code from?
If you recall the handler code, it was really simple: getProjectsR = defaultLayout $ do setTitle "Projects". All the magic lies in defaultLayout call. It picks up a default (for the project) layout file and fills it with data from the argument. This default template layout is split between two files: default-layout.hamlet and default-layout-wrapper.hamlet, both in templates directory. .hamlet is an extension for Hamlet files, which are Haskell form of HTML templates, serving roughly the same purpose as Jade, Jinja or Smarty. I’ll cover Hamlet templates in greater detail in the next post – for now let’s just treat a .hamlet file as a HTML without closing tags. Since pretty much everything seems to be in default-layout-wrapper.hamlet, you might wonder why are there two – this is generally related to the purposes – default-layout-wrapper.hamlet is roughly a static structure that will be filled with data, while default-layout.hamlet is more dynamic and can contain some conditional logic and similar stuff. Additionally, CSS and JavaScript files with the same name are automatically added to default-layout.hamlet (this is because default-layout.hamlet is treated as a usual Widget. We’ll talk about Widgets later in the series). Go ahead, change something – for instance, add a new div in default-layout.hamlet and some text in default-layout-wrapper.hamlet and refresh the page. See how default-layout.hamlet was inserted in place of ^{pageBody pc} ?

If you don’t, go ahead and call stack exec -- yesod devel. Remember that stack might be able to reload Hamlet templates, but is not really able to hot-swap actual Haskell code, so you might need to restart it, especially when adding new files.

That’s it for today – in the next episode we’re gonna create a template view for our projects.

Stay tuned!