Prerequisites

If you haven't already installed Tailor, you should will need to install it, as well as its dependencies:

  1. Download the latest version of Xcode from the App Store.
  2. Download and install the latest version of Tailor from this website.

Create the App

  1. Go to New -> Project in Xcode
  2. Go to "Application" under "OS X"
  3. Select "Tailor Application" and click "Next"
  4. Put in "Haberdashery" as the Product Name, and click "Next"
  5. Select a folder for your project, and click "Create".

Xcode will create a project with the basic structure of a Tailor app. There are still some things that we have to add before it's ready to build. Click on "Haberdashery" at the top of the file browser, then go to "File" -> "Add files to Haberdashery...". Go to the folder "/Library/Frameworks", and add "Tailor.framework" and "TailorSqlite.framework". As a sanity check, hit ⌘R to run your server. The console in Xcode will prompt you for a command. Type "server" and hit enter, and your server should start. Open your web browser and navigate to localhost:8080; you should see a page that says "File Not Found".

There are a few changes that you'll need to make before you can add any real pages. The application will print out some instructions when it starts with these steps:

First, you need to set up an encryption key for your session. All of the session data is stored in a single cookie, which is encrypted with AES to prevent snooping or forgeries. The encryption key is provided in the sessionEncryptionKey value in HaberdasheryConfiguration.swift. The logs should include a suggested value for this key.

Next, you'll need to remove some placeholder files that the template adds to create folders. The logs will tell you which files to remove.

Finally, you should remove the log statements in main.swift that gave you these instructions.

Building Your First Page

Now it's time to get your site to show something more interesting than an error page. Let's start off by adding a controller. You can find out more about controllers in the controller guide. The controller will be responsible for taking in requests, fetching information out of the database, running logic, and dispatching information to a template to render.

  1. Click on "controllers" in the file sidebar, then go to the menu and select File -> New -> "File..."
  2. Go to "Tailor" under "OS X", and select "Controller"
  3. Use "HatsController" as the name, and click "Create".

Each controller defines its own routes, inside of its defineRoutes method. You'll also need to update the loadRoutes method in HaberdasheryConfiguration.swift to add your controller methods en masse:

public func loadRoutes(inout routes: RouteSet) {
  routes.addControllerRoutes(HatsController.self)
}

Your controller will have different methods for handling different kinds of requests. These methods are called "actions". Create a method called indexAction as follows:

func indexAction() {
  var response = self.state.response
  var template = IndexTemplate(controller: self)
  response.responseCode = .Ok
  response.appendString(template.generate())
  respondWith(response)
}

Then, update the defineRoutes method to declare this new action:

static func defineRoutes(routes: RouteSet) {
  routes.withScope(path: "hats") {
    routes.route(.Get(""), to: indexAction, name: "index")
  }
}

This tells the routes that you want all of your controller routes to start with hats, and that a GET request to /hats/ should get routed to the indexAction method. The name parameter is used to refer to this route when you create a link to it from another page.

This will throw a compiler error, because we haven't bothered to define the IndexTemplate yet. Let's do that!

Go to File -> New -> Group and create a group called "views". Click the folder icon in the right-hand sidebar next to location, and create a new folder called "views" to hold the files for this group. Then create another group and folder inside of "views" called "hats".

Inside that "hats" group, create a new Swift file called "HatsIndexTemplate". Replace the contents of that file with the following:

import Tailor
extension HatsController {
  struct IndexTemplate: TemplateType {
    var state: TemplateState

    init(controller: ControllerType) {
      self.state = TemplateState(controller)
    }

    mutating func body() {
      tag("html") {
        tag("body") {
          tag("h1") { text("Hats") }
          tag("p") { text("A listing of hats will appear here soon")}
        }
      }
    }
  }
}

The TemplateType protocol has lots of shorthands for defining tags, which you can learn more about in the view guide. It requires that you have a "state" variable, which contains several fields that are used in generating your page's body. The two most important ones are contents, which holds the contents of the page's body, and controller, which you use to access the request parameters and other controller information. Though these are stored in the TemplateState, there are methods on the TemplateType protocol that allows you to access them directly.

Now you can run your server again and go to http://localhost:8080/hats in your browser. You should see a page with the message from the template.

Adding a Layout

Adding a layout will reduce the amount of boilerplate you have to add to your individual templates. Create a new Swift file in the views directory called "HaberdasheryLayout.swift", with the following contents:

import Tailor

struct HaberdasheryLayout: LayoutType {
  var state: TemplateState
  let template: TemplateType

  init(controller: ControllerType, template: TemplateType) {
    self.state = TemplateState(controller)
    self.template = template
  }
  mutating func body() {
    raw("<!DOCTYPE html>")
    tag("html") {
      tag("head") {
        tag("link", ["rel": "stylesheet", "href": "//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"])
        tag("link", ["rel": "stylesheet", "href": "//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css"])
        tag("meta", ["charset": "UTF-8"])
      }
      tag("body") {
        tag("div", ["class": "navbar navbar-default"]) {
          tag("div", ["class": "navbar-header"]) {
            tag("a", text: "Haberdashery", attributes: ["class": "navbar-brand"])
          }
        }
        tag("div", ["class": "container"]) {
          for key in ["success", "error"] {
            if let message = controller.request.session.flash(key) {
              tag("div", ["class": "alert alert-\(key)"]) {
                tag("button", ["type": "button", "class": "close", "data-dismiss": "alert"]) { self.raw("&times;") }
                tag("div", text: message)
              }
            }
          }
          renderTemplate(template)
        }
        tag("script", ["type": "text/javascript", "src": "//code.jquery.com/jquery-1.11.0.min.js"])
        tag("script", ["type": "text/javascript", "src": "//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"])
      }
    }
  }
}

extension ControllerType {
  static var layout: LayoutType.Type { return HaberdasheryLayout.self }
}

This layout adds Bootstrap and jQuery, and then adds a basic Bootstrap template and renders your individual templates within it.

Next, go into the HatsIndexTemplate and remove the html and body tags, leaving just the h1 and p tags.

There's still a little more work to make the index action use this new layout. The implementation earlier shows how you would make a custom response, but there are shorthands for most of the common response types. For an action that's rendering a page from a template, you can pass the template right to respondWith:

func indexAction() {
    respondWith(IndexTemplate(controller: self))
}

That also wraps the HTML from the template in the layout that you've set on the controller.

Now restart your server and navigate to http://localhost:8080/hats. You should see the page with the new layout.

Building Your First Model

Lets build a model for representing hats. The first step is the create a table in the database for them. You can do this with an alteration, which is a one-time script that changes your configuration or database structure. Create a folder called "alterations", and create a new file in that folder. Use the "Alteration" file template, which will be in the Tailor section. Call the file "CreateHatsAlteration". Update the run method for your alteration with this:

static func run() {
    query(
      "create table hats(",
      "id integer primary key,",
      "color varchar(255),",
      "brim_size int(11)",
      ")"
    )
  }

You should also replace the identifier to return the current date and time in a strictly numeric format. For instance, 201508181536, for 18 August 2015 at 15:36. Alterations will be run in order based on this identifier, so you'll want it to be strictly ordered by the date to make sure that they run in a consistent order.

Start your app again, and at the command prompt, type "run_alterations". It should run without complaint and create your new table.

Finally, create a new file in the models directory called "Hat", using the Model template. Give it these contents:

import Tailor

struct Hat : Persistable {
  /** The id for the record. */
  let id: Int?

  /** The hat's color. */
  var color: String

  /** The width of the hat's brim. */
  var brimSize: Int

  /**
    This method initializes a new hat.

    :param: color     The color of the hat
    :param: brimSize  The width of the hat's brim.
    */
  init(color: String, brimSize: Int) {
    self.color = color
    self.brimSize = brimSize
    self.id = nil
  }

  /**
    This method initializes a record from a row in the database.

    You should extract the fields that your record needs, and return nil if any
    required fields are missing.

    :param: databaseRow   The row in the database.
    */
  required init(databaseRow: DatabaseRow) throws {
    color = try databaseRow.read("color")
    brimSize = try databaseRow.read("brim_size")
    id = try databaseRow.read("id")
  }

  /**
    This method gets the columns that we will save in the database for a record.

    You must add the mapping for your fields here.
    */
  func valuesToPersist() -> [String : DatabaseValueConvertible?] {
    return ["color": color, "brim_size": brimSize]
  }

  /** The name of the table that holds posts. */
  static var tableName: String { return "hats" }

  /** A query for fetching hats. */
  static let query = Query<Hat>()
}

There are four parts that every model will have, if it is persisted to the database. The first part defines the fields on the model. The second part is an initializer that sets the fields based on a row in the database. The third part gets a dictionary of values that we save on the record. The fourth part provides the name of the table that holds these records.

The initializer should throw an error if there is a missing field or a field that is of the wrong type. In most of these methods that fetch records from the database, these errors are caught and logged so that you do not have to litter your code with exception handling for the rare case of a database that doesn't match the structure of your models. The DatabaseRow type provides a read method to help you with this. All you need to provide to this method is the name of the column that you want to read. It infers the type from the field you are storing the result in. It throws an error if the field is missing or of the wrong type. If you try to store the results in an optional field, it will not throw any errors for null values.

The query method is optional, since there's a default one provided by the Persistable protocol, but providing your own can make it cleaner to fetch records in a type-safe way. The Query type will be discussed more when we're ready to fetch records.

Building Pages That Use Your Models

Now let's build some pages that work with these models. We'll start with a form for adding a new hat. First, replace the defineRoutes method in HatsController with this:

static func defineRoutes(routes: RouteSet) {
  routes.withScope(path: "hats") {
    routes.route(.Get(""), to: indexAction, name: "index")
    routes.route(.Get("new"), to: newAction, name: "new")
    routes.route(.Post(""), to: createAction, name: "create")
  }
}

and add a couple of new methods as placeholders for the new actions:

func newAction() {
  render404()
}

func createAction() {
  render404()
}

Check that everything compiles. Once all of that is working, we're ready to build the form itself. Create a file in the views/hats folder called HatsFormTemplate with these contents:

import Tailor

extension HatsController {
  struct FormTemplate: TemplateType {
    var state: TemplateState
    let hat: Hat

    init(controller: ControllerType, hat: Hat) {
      self.hat = hat
      self.state = TemplateState(controller)
    }

    mutating func body() {
      tag("h1", text: "New Hat")
      let path = controller.pathFor(actionName: "create") ?? ""
      tag("div", ["class": "row"]) {
        tag("div", ["class": "col-md-6"]) {
          form(path, type: Hat.self) { form in
            form.input("color", hat.color)
            form.input("brimSize", String(hat.brimSize))
            form.input("submit", "Save", attributes: ["type": "submit", "class": "btn btn-primary"])
          }
        }
      }
    }
  }
}

Like we did before, we're creating the new template under the HatsController to signify that it's meant to be used for pages in that controller, and to make it easier to invoke within that controller. This template shows a common pattern: the template has an initializer that provides records that the template will show. The controller is responsible for building or fetching the records and giving them to the template. The template has access to the controller, so it can access the request parameters and fetch records itself, but putting them in an initializer provides better separation of concerns and makes the templates easier to test in isolation.

This template also shows the TemplateForm type, with provides some shorthands for building a form and adding inputs.

Now that you have the template, you can hook it up with the controller actions. The newAction method in HatsController should have these contents:

respondWith(FormTemplate(controller: self, hat: Hat(color: "", brimSize: 0)))

The createAction method is a little more complicated:

let hat = Hat(
  color: request.requestParameters["hat[color]"] ?? "",
  brimSize: Int(request.requestParameters["hat[brimSize]"] ?? "") ?? 0
)
hat.save()
redirectTo(actionName: "index")

That initializer creates a hat record that hasn't been saved to the database. The save method saves a record, and returns a new copy of it that has information that was changed in the process. For instance, that new copy will have an id set on it. A record can also have createdAt and updatedAt timestamps that will be set when the record is created or saved. If the record cannot be saved because of a database error, the method will return nil. In this case, we're not making any use of that information, so we're just discarding the result.

You can now run the app and use the form to create a hat, but you won't be able to see it anywhere. Let's fix that by updating the index page. Replace the contents of the IndexTemplate with the following:

import Tailor
extension HatsController {
  struct IndexTemplate: TemplateType {
    var state: TemplateState
    let hats: [Hat]

    init(controller: ControllerType, hats: [Hat]) {
      self.hats = hats
      state = TemplateState(controller)
    }

    mutating func body() {
      tag("h1") { text("Hats") }
      link(actionName: "new",attributes: ["class": "btn btn-success"]) { text("New Hat") }
      tag("table", ["class": "table"]) {
        tag("tr") {
          tag("th", text: "Color")
          tag("th", text: "Brim Size")
        }
        for hat in hats {
          tag("tr") {
            tag("td", text: hat.color)
            tag("td", text: String(hat.brimSize))
          }
        }
      }
    }
  }
}

Now we need to update the HatsController to provide the new information for this template. Replace the contents of the indexAction with the following:

respondWith(IndexTemplate(controller: self, hats: Hat.query.order("id", .OrderedDescending).all()))

This shows the Query type, which allows you to build queries and fetch records. The query method from earlier builds an empty query, and then you can chain together the parts of the query in a fluent interface. In this case, we're just putting an order on it, but you can see more in the model guide. You can now build and run the app, and add hats to your heart's desire.

Polishing Up the Layout

The default form styles don't look good in our template, so let's apply the Bootstrap form styles. We can do this with a custom input builder. Create a file called "TemplateHelpers.swift" in your "views" folder, and add this extension to the TemplateType protocol:

import Tailor
extension TemplateType {
  func buildBootstrapInput(form: TemplateForm, key: String, value: String, attributes: [String:String], errors: [ValidationError]) -> TemplateType {
    var t = form.template
    t.tag("div", ["class": "form-group"]) {
      let attributes = merge(attributes, [
        "class": "form-control",
        "name": "\(form.name)[\(key)]",
        "value": value
      ])
      let label: String

      if let type = form.modelType {
        label = self.attributeName(type, key)
      }
      else {
        label = ""
      }
      t.tag("label", text: label, attributes: ["class": "control-label"])
      t.tag("input", attributes)
    }
    return t
  }
}

Then we need to tell our form to use this method when adding inputs. In the HatsFromTemplate file, replace the call form(path, type: Hat.self) with form(path, type: Hat.self, inputBuilder: buildBootstrapInput). With that change, every time we call form.input it will call this method to generate the HTML for the input. We don't want to do this for the button, though, so change the line form.input("submit", "Save", attributes: ["type": "submit", "class": "btn btn-primary"]) to form.template.tag("input", ["type": "submit", "class": "btn btn-primary", "value": "Save"]).

Now you can build and re-run your app and check out the new styles.

Adding Form Validations

We don't want people adding all kind of garbage data, so let's add some checks on the data coming from our form. Replace the createAction method with the following:

func createAction() {
  let hat = Hat(
    color: request.requestParameters["hat[color]"] ?? "",
    brimSize: request.requestParameters["hat[brimSize]"]?.toInt() ?? 0
  )

  let validation = Validation("hat")
    .validate("brimSize", hat.brimSize, inBounds:1...100)
    .validate(presenceOf: "color", hat.color)

  if validation.valid {
    hat.save()
    redirectTo(actionName: "index")
  }
  else {
    respondWith(FormTemplate(controller: self, hat: hat))
  }
}

The Validation class allows us to chain together different checks we want to run on a record, and then collect the final results. In this case, it will check that the hat's brim size is between 1 and 100, and that the hat's color is not blank. If these conditions hold, it will save the hat and take us back to the index page. If one of these checks fails, it will render the form again. You can compile this and check the behavior.

It's not very user-friendly, though, because it doesn't show any errors to the user. Let's add the errors to the form. First, we'll need a way to pass them in to the form. Change the beginning of the FormTemplate to read like this:

var state: TemplateState
let hat: Hat
let errors: [ValidationError]

init(controller: ControllerType, hat: Hat, errors: [ValidationError] = []) {
  self.hat = hat
  self.errors = errors
  self.state = TemplateState(controller)
}

Next, update the line to render the form in createAction to read

respondWith(FormTemplate(controller: self, hat: hat, errors: validation.errors))

Next, we need to make sure that the form builder knows about the errors. Change the call to initialize the form builder to read

form(path, type: Hat.self, inputBuilder: buildBootstrapInput, validationErrors: errors)

Now that the form knows the full list of errors, it will check them when we ask it to build an input, and then give the input builder just the errors for the field that we are adding. To show the errors, replace the buildBootstrapInput method with this version:

func buildBootstrapInput(form: TemplateForm, key: String, value: String, attributes: [String:String], errors: [ValidationError]) -> TemplateType {
  var t = form.template
  var inputClass = "form-group"
  if !errors.isEmpty { inputClass += " has-error" }
  t.tag("div", ["class": inputClass]) {
    let attributes = merge(attributes, [
      "class": "form-control",
      "name": "\(form.name)[\(key)]",
      "value": value
    ])
    let label: String

    if let type = form.modelType {
      label = self.attributeName(type, key)
    }
    else {
      label = ""
    }
    t.tag("label", text: label, attributes: ["class": "control-label"])
    t.tag("input", attributes)


    for error in errors {
      t.tag("p", text: error.localize(self.controller.localization), attributes: ["class": "help-block"])
    }
  }
  return t
}

You can now compile and run the app. When you try to add a hat with no color, you should now get an error message on the form.

Editing Records

Now let's add some pages for editing hats. Start out by adding these lines to your controller, beneath the routes for the new and create actions:

routes.withScope(path: ":id") {
  routes.route(.Get("edit"), to: editAction, name: "edit")
  routes.route(.Post(""), to: updateAction, name: "update")
}

The :id part of the route captures a part of the request path and stores it in the id request parameter.

Next, update the IndexTemplate to add links to the edit page for each hat. The body should now look like this:

tag("h1") { text("Hats") }
link(actionName: "new",attributes: ["class": "btn btn-success"]) { text("New Hat") }
tag("table", ["class": "table"]) {
  tag("tr") {
    tag("th", text: "Color")
    tag("th", text: "Brim Size")
    tag("td", text: "Actions")
  }
  for hat in hats {
    tag("tr") {
      tag("td", text: hat.color)
      tag("td", text: String(hat.brimSize))
      tag("td") {
        link(actionName: "edit", parameters: ["id": String(hat.id ?? 0)]) { text("Edit") }
      }
    }
  }
}

When generating the links, we have to deal with the scenario of a hat with no id. We can either add a fallback for it or forcibly unwrap it by calling hat.id!. The id column is a primary key, and the hats in this table are being retrieved from the database, so there is no way for it to be nil. Forcibly unwrapping it should be safe. However, the IndexTemplate cannot guarantee this, and technically could be called with an unsaved hat in its hat list. If we failed to unwrap it, it would crash the whole server, so it's best to avoid forcible unwraps if at all possible. In this case, falling back to an id of zero will just cause a 404 error later on, so it's the safer way to handle that failure case.

The logic for updating a hat is going to be very similar to creating one, so let's create a method that can handle either. Rename the createAction method to createOrUpdateAction, like this:

func createOrUpdateAction(var hat: Hat) {
  hat.color = request.requestParameters["hat[color]"] ?? ""
  hat.brimSize = Int(request.requestParameters["hat[brimSize]"] ?? "") ?? 0

  let validation = Validation("hat")
    .validate("brimSize", hat.brimSize, inBounds:1...100)
    .validate(presenceOf: "color", hat.color)

  if validation.valid {
    hat.save()
    redirectTo(actionName: "index")
  }
  else {
    respondWith(FormTemplate(controller: self, hat: hat, errors: validation.errors))
  }
}

Add this method for safely fetching a hat from the request parameters:

var hat: Hat? {
  if let id = Int(request.requestParameters["id"] ?? "") {
    return Hat.query.find(id)
  }
  else {
    return nil
  }
}

And add these new action methods:

func createAction() {
  self.createOrUpdateAction(Hat(color: "", brimSize: 0))
}

func editAction() {
  guard let hat = self.hat else { render404(); return }
  respondWith(FormTemplate(controller: self, hat: hat))
}

func updateAction() {
  guard let hat = self.hat else { render404(); return }
  self.createOrUpdateAction(hat)
}

To tie it together, we'll need to make the form connect to the update action when editing an existing hat, or to connect to the create action when creating a new hat. Open the HatsController.FormTemplate and replace the line for setting the path variable with this:

let path: String
if hat.id == nil {
  path = controller.pathFor(actionName: "create") ?? ""
}
else {
  path = controller.pathFor(actionName: "update") ?? ""
}

Now you can recompile the app and go through the edit flows.

Authentication

We don't want random people coming in and adding hats, so let's add some basic authentication. Tailor provides a UserType protocol to describe a model that stores user accounts in the database and verifies passwords when someone wants to log in. The ControllerType protocol has some helper methods for signing in and getting the signed in user.

The first step is adding a users table. Create a new alteration with these contents:

import Tailor

class CreateUsersAlteration : AlterationScript {
  static let identifier: String = ""
  static func run() {
    query(
      "CREATE TABLE `users` (",
      "id integer null primary key,",
      "`email_address` varchar(255),",
      "`encrypted_password` varchar(255)",
      ")"
    )
  }
}

Make sure to fill in the identifier.

Build your app and run the run_alterations task to create the table. This table will have the basic fields that the UserType protocol requires, but you can add extra fields to your user type to add more functionality. For our purposes here, the stock fields will do just fine.

Next, you'll need a User type that supports this protocol. Create a new file in the models folder called "User.swift" with these contents:

import Tailor
struct User: UserType{
  let id: Int?
  var emailAddress: String
  var encryptedPassword: String

  init(emailAddress: String, password: String) {
    self.id = nil
    self.emailAddress = emailAddress
    self.encryptedPassword = PasswordHasher().encrypt(password)
  }

  init(databaseRow: DatabaseRow) throws {
    self.id = try databaseRow.read("id")
    self.emailAddress = try databaseRow.read("email_address")
    self.encryptedPassword = try databaseRow.read("encrypted_password")
  }

  func valuesToPersist() -> [String:DatabaseValueConvertible?] {
    return [
      "email_address": emailAddress,
      "encrypted_password": encryptedPassword
    ]
  }

  static let tableName = "users"
  static let query = Query<User>()
}

You'll need to tell your app to use that type when fetching users by adding the following to the configure method in HaberdasheryConfiguration.swift:

userType = User.self

Next create a new file called SessionsController with these contents:

import Tailor

struct SessionsController : ControllerType {
  var state: ControllerState

  static func defineRoutes(routes: RouteSet) {
    routes.withScope(path: "sessions") {
      routes.route(.Get("new"), to: newAction, name: "new")
      routes.route(.Post(""), to: createAction, name: "create")
      routes.route(.Get("destroy"), to: destroyAction, name: "destroy")
    }
  }

  func newAction() {

  }

  func createAction() {

  }

  func destroyAction() {

  }
}

Replace the line in the routes in HaberdasheryConfiguration.swift that calls addControllerRoutes with this:

routes.addControllerRoutes(SessionsController.self, HatsController.self)

We'll fill in the action methods one by one. Create a new folder in the views folder called "sessions", and add a file to it called SessionsNewTemplate. Give the file these contents:

import Tailor

extension SessionsController {
  struct NewTemplate: TemplateType {
    var state: TemplateState

    init(controller: SessionsController) {
      state = TemplateState(controller)
    }

    mutating func body() {
      tag("h1", text: "Sign In")
      let path = controller.pathFor(actionName: "create") ?? ""
      tag("div", ["class": "row"]) {
        tag("div", ["class": "col-md-6"]) {
          form(path, type: User.self, inputBuilder: buildBootstrapInput) { form in
            form.input("emailAddress", "")
            form.input("password", "", attributes: ["type": "password"])
            form.template.tag("input", ["type": "submit", "class": "btn btn-primary", "value": "Save"])
          }
        }
      }
    }
  }
}

Then add this to the newAction in SessionsController:

respondWith(NewTemplate(controller: self))

Let's check if we can pull up the form. Add this to the HaberdasheryLayout in the navbar section:

tag("ul", ["class": "nav navbar-nav"]) {
  tag("li") {
    link(SessionsController.self, actionName: "new") { text("Sign In") }
  }
}

You should now be able to build and run the app. There should be a link in the navbar to sign in, which should take you to the sign-in form. Now let's add the logic for the createAction method:

let emailAddress = request.requestParameters["user[emailAddress]"] ?? ""
let password = request.requestParameters["user[password]"] ?? ""
    do {
            let newSession = try signIn(emailAddress, password: password)
            redirectTo(HatsController.self, actionName: "index", session: newSession)
    }
    catch {
            respondWith(NewTemplate(controller: self))
    }

To test this, we'll need to create a user. We can do this with a task. A task is a script or job that can be run from the command line. Tailor provides a few tasks, like starting the server and running alterations, but it can also be helpful to define your own. Create a file in your project called ScratchTask with these contents:

import Tailor
class ScratchTask: TaskType {
  static func runTask() {
    let user = User(emailAddress: "test@tailorframe.work", password: "monkey")
    user.save()
  }
}

Now build and run your app, and type "scratch_task" at the prompt. This will run the script and create your user. Once you run this script, you should be able to sign in, but there won't be much of a visible effect. Let's add a link for signing out. Replace the sign-in link by replacing the sign-in link in HaberdasheryLayout with this:

if controller.currentUser == nil {
  link(controllerName: SessionsController.self, actionName: "new") { text("Sign In") }
}
else {
  link(controllerName: SessionsController.self, actionName: "destroy") { text("Sign Out") }
}

Now after you sign in, you'll see a sign out link. Let's make that link work. Add this to the destroyAction in SessionsController:

let newSession = signOut()
    redirectTo(HatsController.self, actionName: "index", session: newSession)

Now when you click the sign out link, it will sign you out and you'll see the sign in link again.

It would be nice to have more feedback for the users when they sign in and out. We can do this with flash messages, which are one-time messages that are stored in the session between requests. Change the redirect response in createAction to this:

var newSession = try signIn(emailAddress, password: password)
        newSession.setFlash("success", "You are now signed in")
        redirectTo(HatsController.self, actionName: "index", session: newSession)

And change the redirect response in destroyAction to this:

var newSession = signOut()
  newSession.setFlash("success", "You are now signed out")
        redirectTo(HatsController.self, actionName: "index", session: newSession)

Now you'll get nice little pop-up messages when you sign in and out.

Now that we've got the authentication working, let's enforce some access rules. We'll start by restricting access to the "New Hat" and "Edit" links to signed-in users. Replace the body in HatsController.IndexTemplate with:

tag("h1") { text("Hats") }
if controller.currentUser != nil {
  link(actionName: "new",attributes: ["class": "btn btn-success"]) { text("New Hat") }
}
tag("table", ["class": "table"]) {
  tag("tr") {
    tag("th", text: "Color")
    tag("th", text: "Brim Size")
    tag("td", text: "Actions")
  }
  for hat in hats {
    tag("tr") {
      tag("td", text: hat.color)
      tag("td", text: String(hat.brimSize))
      tag("td") {
        if controller.currentUser != nil {
          link(actionName: "edit", parameters: ["id": String(hat.id ?? 0)]) { text("Edit") }
        }
      }
    }
  }
}

That's not enough, though, because they can still go directly to the URL for editing hats. To prevent that, we'll need to add a check before the controller actions to make sure that we have a user signed in. Rather than repeating this code in every method that needs a user signed in, you can use a filter. A filter is a block of code that runs before and after a controller action. It can check the request data and offer an alternative response without even invoking the controller action, or provide some modification to the response data after it comes out of the controller. One of the filters provided by the framework is called AuthenticationFilter. To add it to your routes, change the logic in the defineRoutes method in HatsController to this:

static func defineRoutes(routes: RouteSet) {
  routes.withScope(path: "hats") {
    routes.route(.Get(""), to: indexAction, name: "index")
    let path = routes.pathFor(SessionsController.self, actionName: "new") ?? ""
    routes.withScope(filter: AuthenticationFilter(path)) {
      routes.route(.Get("new"), to: newAction, name: "new")
      routes.route(.Post(""), to: createAction, name: "create")
      routes.withScope(path: ":id") {
        routes.route(.Get("edit"), to: editAction, name: "edit")
        routes.route(.Post(""), to: updateAction, name: "update")
      }
    }
  }
}

Now if you try to access anything but the index page when not signed in, you will be redirected to the sign-in page.

Localizing your Site

You generally don't want to embed text directly into your site; instead, you should run it through a localization mechanism that will allow you to translate it for other languages and regions. Tailor assumes that text is localized by default. You can add a config file called "localization.plist" that holds mappings between localization keys and localized text. Tailor's convention is to have translation keys in lower case, with underscores to separate words and dots to separate segments of the key. In the plist file, you can have nested dictionaries for the segments, to give a more logical clustering of related keys.

Let's start by localizing some of the layout. Open up the layout template and replace the title "Haberdashery" with "haberdashery.haberdasherylayout.title". Then open up the localization plist file and go to the "en" dictionary. It should have been created with a default mapping when you created the project. Add a dictionary for "haberdashery", then for "haberdasherylayout", and create an entry in their mapping "title" to "Haberdashery". You can build and run the app now, and confirm that the title is being populated as you expect. You can do the same with the sign-in and sign-out labels, changing them to "haberdashery.haberdasherylayout.signin" and "haberdashery.haberdasherylayout.signout".

There's a convention for a shorthand for localization keys in views and controllers. If you give a key starting with a dot, Tailor will fill in a prefix based on the class name. For the HatsController, it would be "haberdashery.hatscontroller". For the IndexTemplate in HatsController, it would be "haberdashery.hatscontroller.indextemplate". We're already using this convention in the layout, so you can just shorten the keys in the template to ".title", ".signin", and ".sign_out".

You can now go in and make similar changes to the rest of the application. The controllers will default to using English for their localization, but you can change this by setting the localization field on the controller.

Conclusion

There's plenty more things that Tailor can do, but that's all we're going to cover in this tutorial. If you want to learn more, you can check out the guides.