1. Guide Assumptions

This guide is designed for beginners who want to get started with Crails from scratch. It does not assume any prior experience with Crails.

We assume you are already familiar with the C++ programming language and CMake building tools.

2. Creating a new Crails Application

The best way to read this guide is to follow it step by step. All steps are essential to run this example application and no additional code or steps are needed.

By following this guide, you'll create a very simple weblog.

2.1 Installing Crails

First, if you haven't already done it before, you need to install Crails Framework. You may do so by following the guide corresponding to your operating system:

2.2 Creating the Blog application

Crails comes with a application generator tool which will provide you the foundation of a fresh Crails application, so that you don't have to write it yourself.

To use this generator, open a terminal, navigate to the directory in which you wish to create the application, and type:

$ crails new --name blog

This will create a Crails application called Blog in a `blog` directory.

Take some time to check out the configuration options provided by running crails new -h.

After you create the blog application, switch to its folder:

$ cd blog

The blog directory has a number of auto-generated files and folders that make up the structure of a Crails application. Most of the work in this tutorial will happen in the app folder, but here's a basic rundown on the function of each file and folders that Crails created by default:

File/Folder Purpose
app/ Contains the controllers, models, views, helpers, mailers and assets for your application.
app/main.cpp This file contains the entry point for the server binary.
app/routes.cpp This is where you'll bind your routes with your controller methods.
build/ Output for your application binaires: server, tasks, anything from your application that can be executed will end up in this folder.
config/ Configure your application's server, database, request pipeline and several optional features of Crails.
lib/ This folder is used for application code that you haven't written yourself. Crails uses several code generating utilities: the lib folder is where this code will be generated and compiled from
public/ The only folder seen by the world as-is. Contains static files and compiled assets.
spec/ This is where you'll write tests for your application.
CMakeLists.txt Contains a set of directives describing the project's source files, dependencies, and build targets.
.crails This file is managed by the command line interface tool, and records several settings about your project used when building or generating code.

3. Hello, Crails!

To begin with, let's get some text up on screen quickly. Let's get your Crails application running:

3.1 Starting up the web server

$ crails build
$ build/server

The first command will compile your application. The second one will fire up your server binary. To see your application in action, open a browser window and navigate to http://localhost:3000. You should see the Crails default information page:

To stop the web server, hit Ctrl+C in the terminal window where it's running. To verify the server has stopped you should see your command prompt cursor again. Note that changes you make to your application's code won't get picked up by the server unless you run a new compilation using crails build and then restart the server.

The Crails default information page is the smoke test for a new Crails application: it makes sure that your application is configured correctly and can receive and respond to http queries.

3.2 Say "Hello", Crails

To get Crails to say "Hello", you need to create at minimum a route, a controller and a view. A route maps a request to a controller action. A controller action performs the necessary work to handle the request, and prepares any data for the view. A view displays data in the desired format.

To create a new controller, we'll run the controller generator:

$ crails scaffold controller -n Welcome

This will generate two files: app/controllers/welcome.hpp and app/controllers/welcome.cpp. Let's take a look at those:

app/controllers/welcome.hpp
#pragma once
#include "app/controllers/application.hpp"

class WelcomeController : public ApplicationController
{
public:
  WelcomeController(Crails::Context& context);

  void initialize() override;
  void finalize() override;
};
app/controllers/welcome.cpp
#include "welcome.hpp"

using namespace std;

WelcomeController::WelcomeController(Crails::Context& context)
  : ApplicationController(context)
{
}

void WelcomeController::initialize()
{
  ApplicationController::initialize();
}

void WelcomeController::finalize()
{
  ApplicationController::finalize();
}

This is a basic skeleton for a controller. We will now add an action to the controller by adding the index method:

app/controllers/welcome.hpp
#pragma once
#include "app/controllers/application.hpp"

class WelcomeController : public ApplicationController
{
public:
  WelcomeController(Crails::Context& context);

  void initialize() override;
  void finalize() override;

  void index()
  {
    render(TEXT, "Hello world!"); // will respond to the action
                                  // with a plain text answer
  }
};

Then, you need to register a new route in app/routes.cpp:

app/routes.cpp
#include "config/router.hpp"
#include "controllers/welcome.hpp"

ApplicationRouter::ApplicationRouter()
{
  match_action("GET", "/", WelcomeController, index);
}
For more information about routing, refer to the Crails Routing guide

Lastly, remove the default index.html file (otherwise, it will be served by default as the root of your application)

$ rm public/index.tml

We're good to go. Compile your code and restart the server to get it to pick up your changes:

$ crails build
$ build/server

Then, open http://localhost:3000 in a web browser. You'll see a simple text response saying "Hello world!".

3.3 Say "Hello" with HTML

In the last section, our server gave a response using the text/plain format. To provide an HTML response, you'll need to create a view.

A view's purpose is to display information in a human readable format. An important distinction to make is that it is the controller, not the view, where information is collected. The view should just display that information. By default, view templates are written in a language called ecpp (Embedded C++) which is used to generate the C++ code for your views.

First, you need to enable html views in your application. To do that, you need to add the html renderer to your application, by running the following command:

$ crails templates formats --add html

Now, we'll add a new folder at app/views/welcome to create an ecpp template named app/views/welcome/index.html

app/views/welcome/index.html
<html>
  <head>
    <title>Hello world!</title>
  </head>
  <body>
    This view has been generated from an ecpp template.
  </body>
</html>

We also need to update our controller so that it responds using our view:

app/controllers/welcome.hpp
#pragma once
#include "app/controllers/application.hpp"

class WelcomeController : public ApplicationController
{
public:
  WelcomeController(Crails::Context& context);

  void initialize() override;
  void finalize() override;

  void index()
  {
    render("welcome/index"); // <-- renders the html view
  }
};

Compile and restart your server, then go to http://localhost:3000 to see that your application now provides an HTML response.

4. Getting Up and Running

Now that you've seen how to create a controller, an action, and a view, let's create something with a bit more substance.

In the Blog application, you will now create a new resource. A resource is the term used for a collection of similar objects, such as articles, people, or animals. You can create, read, update, and destroy items for a resource and these operations are referred to as CRUD operations.

4.1 Setting up a database

Resources need to be persistent, and for that, we will be using an SQL database. The preferred method for that is to use the ODB ORM. We provide a plugin to integrate it with our controller and building system. Let's install it:

$ crails plugins odb install -b sqlite

This will update your CMakeLists.txt to link your application to the odb libraries, generate a default configuration for an sqlite database, and adds the odb_migrate task which we'll describe later on.

Check out the Crails Databases manual to learn more about the configuration options for databases with crails.

4.2 Creating an MVC resource

Now that our database is configured, we will create a set of model, views and controllers. To generate it all at once, we will use the resource generator:

$ crails scaffold resource -m Article -p std::string/title std::string/content

This will create the following files:

File Purpose
app/models/article.hpp odb-based model
app/models/article.cpp model implementation
app/controllers/article.hpp odb-based controller with CRUD actions
app/controllers/article.cpp controller implementation
app/views/article/index.html lists an index of article loaded by the controller
app/views/article/show.html shows a single article loaded by the controller
app/views/article/new.html displays a form to create a new article
app/views/article/edit.html displays a form to edit an existing article
app/views/article/form.html partial view containing the form used in the new and edit views.

The app/routes.cpp file will also be modified to connect our controller's actions:

app/routes.cpp
#include "app/controllers/article.hpp"
#include "app/controllers/welcome.hpp"
#include "config/router.hpp"

ApplicationRouter::ApplicationRouter()
{
  resource_actions(article, ArticleController);
  match_action("GET", "/", WelcomeController, index);
}

The resource_actions macro creates routes for all the actions in our resource controller. You can learn more about the resource_actions macro in the Routing guide.

4.3 Database Migrations

Migrations are used to alter the structure of an application's database. In Crails applications, migrations are written in XML and C++ so they can be database agnostic.

In the simplest scenarios, the odb compiler generates an application.xml file that tracks changes to the database schema. In most cases, this is enough to handle updates to your databases, though you can also run C++ code between migrations (for instance, when you need to change the content of a column during a migration). Check that out in the Migrations with ODB manual.

To create or update a database table schema, we will have to use the odb_migrate task:

$ crails build # don't forget to build your project first
$ build/tasks/odb_migrate/task -c odb

The -c option tells the migrating task to create the database if it does not already exist.

The odb parameter points to a specific database configured in our project, which you can find in the config/databases.cpp file which was generated when we installed the odb plugin.

4.4 Showing a list of Articles

Let's take a look at the controller we just generated, at app/controllers/article.cpp. The generator defined two methods used to show an index of article: the find_list and index methods:

app/controllers/article.cpp
void ArticleController::find_list()
{
  odb::result<Article> results;

  database.find(results);
  model_list = Crails::Odb::to_vector<Article>(results);
  vars["models"] = &model_list;
}

void ArticleController::index()
{
  find_list();
  render("article/index");
}

The find_list method fetches all the Article objects stored in database, and store the results in model_list, which is a container declared in our generated controller.

Notice the vars["models"] = &model_list assignation. The vars object is used to share variables with views. We'll now take a look at the index view and see how to read the contents of this variable.

Open the app/views/article/index.html file, and replace its contents as following:

app/views/welcome/index.html
#include "app/models/article.hpp"

std::vector<Article>& @models;
// END LINKING
<h1>Article index</h1>

<ul>
  <% for (const Article& article : models) do %>
    <li>
      <%= article.get_name() %>
    </li>
  <% end %>
</ul>

The above code is a mixture of HTML and ECPP. ECPP is a templating system that generates C++ from markup documents. Here, we can see two types of ECPP tags: <% %> and <%= %>. The <% %> tag means "evaluate the enclosed C++ code". The <%= %> means "evaluate the enclosed C++ code, and output the value it returns". Anything you could write in the body of a C++ function can go inside these ECPP tags, though it's usually best to keep the contents of ERB tags short, for readability.

ECPP is based on std::stringstream, which means the <%= %> tags can output anything supported by a std::stringstream.

We can see the final result by visiting http://localhost:3000/article. (Remember that build/server server must be running!) Here's what happens when we do that:

1. The browser makes a request GET http://localhost:3000/article
2. Our Crails application receive the request
3. The Crails router maps the article route to the index action of ArticleController
4. The find_list method uses the Article model and the database object to fetch all articles from the database.
5. The index action renders the app/views/article/index.html view.
6. The ECPP template is evaluated and outputs HTML.
7. The server sends a repsonse containing the HTML back to the browser.

We've connected all the MVC pieces together, and we have our first controller action! Next, we'll move on to the second action.

5. CRUDit where CRUDit is due

Almost all web applications involve CRUD (Create, Read, Update, and Delete) operations. You may even find that the majority of the work your application does is CRUD. Crails acknowledges this, and provides many features to help simplify code doing CRUD.

Let's begin exploring these features by adding more functionality to our application.

5.1 Showing a Single Article

We currently have a view that lists all articles in our database. Let's add a new view that shows the title and body of a single article.

The resource generator we used earlier already generated the route and controller methods needed. Let's take a look at the controller's code:

app/controllers/article.cpp
std::shared_ptr<Article> ArticleController::find_model(Crails::Odb::id_type id)
{
  database.find_one(model, odb::query<Article>::id == id);
  vars["model"] = model.get();
  return model;
}

void ArticleController::require_model(Crails::Odb::id_type id)
{
  if (!model && !find_model(id))
    respond_with(Crails::HttpStatus::not_found);
}

void ArticleController::show()
{
  render("article/show");
}

Three methods are involved in showing a single article:

You might've noticed that the require_model method isn't being called by the show action. In simple cases, you may want to fetch the model directly from the action. But since multiple actions will need to fetch a model, we'll use the initialize method instead.

5.1.1 Initialize and finalize

Before and after running each action on a controller, the initialize and finalize methods will be called. This can be useful when several actions need to be setup in similar ways.

Let's take a look at the initialize method generated for our ArticleController:

app/controllers/article.cpp
void ArticleController::initialize()
{
  Crails::Odb::Controller<ApplicationController>::initialize();
  string action_name = get_action_name();

  if (action_name == "show" || action_name == "edit" || action_name == "update" || action_name == "destroy")
    require_model(params["id"]);
}

In this initializer, we use get_action_name to figure out which action is about to be triggered. If said action requires a single article model to be loaded, we call require_model.

When a request's response is provided within an initializer, the action method won't be called at all. The finalize method, however, is always called.

As we've seen earlier, the require_model method already provides a response by calling respond_with when the resource was not found. In such cases, the show method will only be called when an Article object is loaded within the shared variable object (vars).

5.1.2 Setting up the show view

Let's create our show view by replacing the content of app/views/article/show.html with the following contents:

app/views/article/show.html
#include "app/models/article.hpp"
Article& @model;
// END LINKING
<h1><%= model.get_title() %></h1>

<p><%= model.get_content() %></p>

Now we can see the article when we visit http://localhost:3000/article/1!

To finish up, let's add a convenient way to get to an article's page. We'll link each article's title in the index view to its page:

app/views/article/index.html
#include "app/models/article.hpp"

std::vector<Article>& @models;
// END LINKING
<h1>Article index</h1>

<ul>
  <% for (const Article& article : models) do %>
    <li>
      <a href="/article/<%= article.get_id() %>">
        <%= article.get_name() %>
      </a>
    </li>
  <% end %>
</ul>

5.2 Creating a New Article

Now we move on to the "C" (Create) of CRUD. Typically, in web applications, creating a new resource is a multi-step process. First, the user requests a form to fill out. Then, the user submits the form. If there are no errors, then the resource is created and some kind of confirmation is displayed. Else, the form is redisplayed with error messages, and the process is repeated.

In a Rails application, these steps are conventionally handled by a controller's new_ and create actions. Let's check out the implementation of these actions in our generated controller:

app/controllers/articles.cpp
void ArticleController::new_()
{
  render("article/new");
}

void ArticleController::create()
{
  Article model;
  model.edit(params[Article::resource_name]);
  database.save(model);
  redirect_to("/article/" + boost::lexical_cast<std::string>(model.get_id()));
}

The new action is very simple, and merely renders the app/views/article/new.html view.

The create action instantiates a new article, and initialize its properties from parameters using model.edit(params[Article::resource_name]). It then persists the model to database by calling database.save(model). At this point, the model should have an ID assigned to it: we use this ID to respond with a 303 redirection, by calling redirect_to towards the show route for the article model.

5.2.1 New view

Let's take a look at the app/views/article/new.html view that was generated with the crails scaffold resource command:

app/views/article/new.html
#include "app/models/article.hpp"
Article model;
// END LINKING
<h1>New Article</h1>
<%= partial("article/form", {{"model", &model}} %>

The new.html view is very simple: it instantiate an empty Article, then renders a partial view that contains the form for our article model. Using a partial for the form allows us to use the same HTML for both the creation and updating forms. Let's head over to the form partial to see what's going on there.

You can read more about partials in the crails-html layout and rendering guide.

5.2.2 Using a form builder

We will use a feature of Rails called a form builder to create our form. Using a form builder, we can write a minimal amount of code to output a form that is fully configured and follows Crails conventions.

Let's replace the contents of app/views/article/form.html with the following:

app/views/article/form.html
#include "app/models/article.hpp"
#include <crails/html_form_builder.hpp>

Article& @model;
Crails::FormBuilder<Article> form = Crails::FormBuilder<Article>(this, model);
// END LINKING
<%= form_for(model, "/article") yields %>
  <div>
    <%= form.label_for("title") %>
    <%= form.text_field("title", &Article::get_title) %>
  </div>
  <div>
    <%= form.label_for("content") %>
    <%= form.text_area("content", &Article::get_content) %>
  </div>
<% yields-end %>

The form_for creates a <form> tag with the proper action and method attributes. It also includes the CSRF token for the query (About CSRF).

Then, we use the Crails::FormBuilder template class and call methods like label_for and text_field to output the appropriate form elements.

The resulting output from out form_for call will look like:

<form action="/article" method="post">
  <input type="csrf-token" value="...">
  <div>
    <label for="article[title]">title</label>
    <input type="text" name="article[title]">
  </div>
  <div>
    <label for="article[content]">content</label>
    <textarea name="article[content]"></textarea>
  </div>
</form>
To learn more about form builders, see Html form helpers.

5.2.3 Finishing up

We can now create an article by visiting http://localhost:3000/article/new. To finish up, let's link to that page from the bottom of the article index:

app/views/article/index.html
#include "app/models/article.hpp"

std::vector<Article>& @models;
// END LINKING
<h1>Article index</h1>

<ul>
  <% for (const Article& article : models) do %>
    <li>
      <%= article.get_name() %>
    </li>
  <% end %>
</ul>

<a href="/article/new">New Article</a>

5.3 Updating an Article

We've covered the "CR" of CRUD. Now let's move on to the "U" (Update). Updating a resource is very similar to creating a resource. They are both multi-step processes. First, the user requests a form to edit the data. Then, the user submits the form. If there are no errors, then the resource is updated. Else, the form is redisplayed with error messages, and the process is repeated.

These steps are conventionally handled by a controller's edit and update actions. Let's take a look at the implementation of these actions that was generated by our scaffold, below the create action:

app/controllers/article.cpp
void ArticleController::edit()
{
  render("article/edit");
}

void ArticleController::update()
{
  model->edit(params[Article::resource_name]);
  database.save(*model);
  redirect_to("article/" + boost::lexical_cast<std::string>(model->get_id()));
}

Notice how the edit and update actions resemble the new and create actions.

The edit action doesn't need to fetch the article from the database, since this was already handled by the initialize method (see 5.1.1 Initialize and finalize). The edit action merely needs to render app/views/article/edit.html.

The update action update the article's attribute using the Article::edit method, saves the changes to database, then redirects the user to the show action.

5.3.1 Finishing up

We can now update an article by visiting its edit page, e.g. http://localhost:3000/article/1/edit. To finish up, let's link to the edit page from the bottom of app/views/article/show.html.

5.4 Deleting an Article

Finally, we arrive at the "D" (Delete) of CRUD. Deleting a resource is a simpler process than creating or updating. It only requires a route and a controller action. And our resourceful routing (resource_actions) already provides the route, which maps DELETE /article/:id requests to the destroy action of ArticlesController.

So, let's take a look at the destroy action from app/controllers/article.cpp, below the update action:

void ArticleController::destroy()
{
  database.destroy(*model);
  redirect_to("/article");
}

The destroy action calls database.destroy on the model that was fetched in the initialize method. Then, it redirects the browser to the article index path.

Now, let's add a link at the bottom of app/views/articles/show.html so that we can delete an article from its own page:

app/views/articles/show.html
#include "app/models/article.hpp"
#include <boost/lexical_cast.hpp>

Article& @model;
std::string article_uri = Crails::uri("article", model.get_id());
// END LINKING
<h1><%= model.get_title() %></h1>

<p><%= model.get_content() %></p>

<ul>
  <li>
    <%= link(article_uri + "/edit", "Edit") %>
  </li>
  <li>
    <%= link(article_uri, "Delete", {
      {"method","delete"}, {"confirm", "Are you sure ?"}
    }) %>
  </li>
</ul>

In the above code, we use the Crails::uri() helper to generate an URI from parameters: it serializes each parameter as a string and separate them with slashes. In this case, it will result in the following string: /article/1 (depending on the article id).

Right below, we use the html template link helper to generate our links. This helper provides some options allowing us to use the DELETE method in our delete link, but also to add a confirm dialog. Note that these options won't work in an environment where JavaScript has been disabled.

And that's it! We can now list, show, create, update, and delete articles! InCRUDable!

6. Adding a second model

It's time to add a second model to the application. The second model will handle comments on articles.

6.1 Generating a Model

We're going to see a similar generator to the one we used before when creating the Article model, views and controllers. This time we'll create a Comment model to hold a reference to an article. Run this command in your terminal:

crails scaffold model -m Comment -p std::string/commenter std::string/body

Now that the model is generated, we'll add a reference to the Article object:

app/models/comment.hpp
#pragma once
#include <crails/odb/model.hpp>
#include <crails/datatree.hpp>
#include "article.hpp" // add the include file for Article

#pragma db object
class Comment : public Crails::Odb::Model
{
  odb_instantiable()
public:
  static const std::string resource_name;

  #pragma db view object(Comment)
  struct Count
  {
    #pragma db column("count(" + Comment::id + ")")
    size_t value;
  };

  void edit(Data);
  void set_body(const std::string& value) { this->body = value; }
  const std::string& get_body() const { return body; }
  void set_commenter(const std::string& value) { this->commenter = value; }
  const std::string& get_commenter() const { return commenter; }

  // Add getter and setters for the article property:
  void set_article(const std::shared_ptr<Article>& value) { article = value; }
  const std::shared_ptr<Article>& get_article() const { return article; }

private:
  std::string commenter;
  std::string body;
  std::shared_ptr<Article> article; // Add the article property
};

Now, run the following commands to update your SQL database schema:

$ crails build
$ build/tasks/odb_migrate/task odb

If you check out the SQL table for Comment, you'll see that it now has an article_id column. By default, when loading the model from database, the associated Article model will also be loaded within the comment.

Always loading associated objects might not be the best choice for performances. For this reason, you will often rely on ODB views to customize your queries.

6.2 Adding a route for comments

As with the article controller, we will need to add a route so that Crails know where we would like to navigate to see comments. Open up the router file again, and edit as below:

app/routes.cpp
#include "app/controllers/comment.hpp" // Add comment controller include
#include "app/controllers/article.hpp"
#include "app/controllers/welcome.hpp"
#include "config/router.hpp"

ApplicationRouter::ApplicationRouter()
{
  resource_actions(article, ArticleController);
  scope("article/:article_id", [this]()
  {
    resource_actions(comments, CommentController);
  });
  match_action("GET", "/", WelcomeController, index);
}
To learn about routing helpers such as scope, check out the Routing manual.

6.3 Generating a controller

With the model in hand, you can turn your attention to creating a matching controller. This time, we'll use a controller generator:

$ crails scaffold controller -m Comment

This creates two files:

File/Directory Purpose
app/controllers/comment.hpp The comment controller header.
app/controlelrs/comment.cpp The comment controller source.

Like with any blog, our readers will create their comments directly after reading the article, and once they have added their comment, will be sent back to the article show page to see their comment now listed. Due to this, our CommentController is there to provide a method to create comments and delete spam comments when they arrive.

So first, we'll wire up the Article show template to let us make a new comment:

app/views/articles/show.html
#include "app/models/article.hpp"
#include "app/models/comment.hpp"

Article& @model;
Comment new_comment;
Crails::FormBuilder<Comment> form =
  Crails::FormBuilder<Comment>(this, new_comment);
std::string new_comment_path =
  Crails::uri("article", model.get_id(), "comments");
// END LINKING
<h1><%= model.get_title() %></h1>

<p><%= model.get_content() %></p>

<h2>Add a comment:</h2>
<%= form_for(new_comment, new_comment_path) yields %>
  <div>
    <%= form.label_for("commenter") %>
    <%= form.text_field("commenter", &Comment::get_commenter) %>
  </div>
  <div>
    <%= form.label_for("body") %>
    <%= form.text_area("body", &Comment::get_body) %>
  </div>
  <input type="submit" value="Send" />
<% yields-end %>

This adds a form on the Article show page that creates a new comment by calling the CommentController create action.

Let's wire up the create in our comment controller:

app/controllers/comment.cpp
void CommentController::create()
{
  std::shared_ptr<Article> article;
  Comment model;
  auto query = odb::query<Article>::id == params["article_id"];

  if (database.find_one(article, query))
  {
    model.set_article(article);
    model.edit(params[Comment::resource_name]);
    database.save(model);
    redirect_to("article/" + params["article_id"].as<std::string>());
  }
  else
    respond_with(HttpStatus::not_found);
}

You'll see a bit more complexity here than you did in the controller for articles. That's a side-effect of the nesting that you've set up. Each request for a comment has to keep track of the article to which the comment is attached, thus the initial call to the database.find_one, used with an odb::query to get the article in question.

You can learn more about how to write database queries with ODB's manual «Querying the Database » section.

In addition, we call the set_article method we created in the Comment model, before initializing and saving the model to database.

Once we have made the new comment, we send the user back to the original article using the redirect_to method. As we have already seen, this calls the show action of the ArticleController which in turn renders the show.html template. This is where we want the comment to show, so we'll add that to the view in the next chapter.

6.4 Display associated comments on an article

We will now add comments on the article show view. Firstly, we need to fetch the comments associated to an article. Open the article controller source and edit the show method as following:

app/controllers/article.cpp
#include "app/models/comment.hpp"

...

void ArticleController::show()
{
  odb::result<Comment> comments;
  auto query = odb::query<Comment>::article->id == model->get_id();

  database.find<Comment>(comments, query);
  render("article/show", {
    {"comments", &comments}
  });
}

Now, before rendering the show view, our ArticleController will query the database for any comments bound to the fetched article. The database fills up a odb::result container which you can then use to loop over each comments.

Lastly, we'll update the view so that it displays each comments above the new comment form:

app/views/articles/show.html
#include "app/models/article.hpp"
#include "app/models/comment.hpp"

Article& @model;
odb::result<Comment>& @comments; // shared odb::result object
Comment new_comment;
Crails::FormBuilder<Comment> form =
  Crails::FormBuilder<Comment>(this, new_comment);
std::string new_comment_path =
  Crails::uri("article", model.get_id(), "comments");
// END LINKING
<h1><%= model.get_title() %></h1>

<p><%= model.get_content() %></p>

<h2>Comments</h2>
<% for (const Comment& comment : comments) do %>
  <p>
    <strong>Commenter:</strong>
    <%= comment.get_commenter() %>
  </p>
  <p>
    <strong>Comment:</strong>
    <%= comment.get_body() %>
  </p>
<% end %>

<h2>Add a comment:</h2>
<%= form_for(new_comment, new_comment_path) yields %>
  <div>
    <%= form.label_for("commenter") %>
    <%= form.text_field("commenter", &Comment::get_commenter) %>
  </div>
  <div>
    <%= form.label_for("body") %>
    <%= form.text_area("body", &Comment::get_body) %>
  </div>
  <input type="submit" value="Send" />
<% yields-end %>

Now you can add articles and comments to your blog and have them show up in the right places.

7. Security

7.1 Basic Authentication

If you were to publish your blog online, anyone would be able to add, edit and delete articles or delete comments.

Crails provides an HTTP authentication system that will work nicely in this situation.

In the ArticlesController we need to have a way to block access to the various actions if the person is not authenticated. Here we can use the Crails require_basic_authentication method, which allows access to the requested action if that method allows it.

To use the authentication system, we specify it in the initialize method of our ArticleController. In our case, we want the user to be authenticated on every action except index and show, so we write that:

app/controllers/article.cpp
void ArticleController::initialize()
{
  Crails::Odb::Controller<ApplicationController>::initialize();
  string action_name = get_action_name();

  if (action_name != "index" && action_name != "show")
    require_basic_authentication("username", "password");

  if (action_name == "show" || action_name == "edit" || action_name == "update" || action_name == "destroy")
    require_model(params["id"]);
}

Now if you try to create a new article, you will be greeted with a basic HTTP Authentication challenge:

TODO: image

After entering the correct username and password, you will remain authenticated until a different username and password is required or the browser is closed.

In more advanced use cases, you will want users to be able to register into your application: to learn how Crails can help you with this behavior, move on to the Sign-In tutorial.

7.2 Other security considerations

Security, especially in web applications, is a broad and detailed area. Security in your Crails application is covered in more depth in the Crails Security Guide.

8. What's next ?

Now that you've seen your first Crails application, you should feel free to update it and experiment on your own.

Remember, you don't have to do everything without help. As you need assistance getting up and running with Rails, feel free to consult these support resources: