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.
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:

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);
}
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.
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.
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.
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:
-
find_model should look familiar. Instead of fetching a list of articles, it fetches a single
article, looking up for its ID. It then stores a pointer to the article object in the
vars
object. - require_model calls find_model, and uses respond_with to respond with 404 not found when no model were found.
- show renders the app/views/article/show.html view.
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
.
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.
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>
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.
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);
}
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.
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:
- The Crails Guides