We are going to build a simple app to report and monitor incidents (an example of a similar app is here: http://inciweb.nwcg.gov/)
Create the app
rails new incident-reporter
Let's see if it works:
http://localhost:3000 and you should see "Yay! You're on Rails!"
Take a look at the generated directory structure.
Scaffold: Our first models
We want to create an Incident with a category, location and flags to say whether I am near or at the incident site. Something like this:
Rails offer a very quick way to start building a web app called scaffold. To create an Incident type the following in your shell
This creates a model, a controller, a set of views and a migration. Take a look at the new files in
Before starting the server, you need to migrate the DB with the new table. Migrations are just Ruby classes
Start the server and take a look around. Everything works like magic. You can CRUD incidents with 0 lines of code.
Take a look at:
routes.rbhow does it work? Take a look also at
Focus on the model: ActiveRecord
Active Record Basics
Active Record is the M (the model) in MVC. It is the layer of the system responsible for representing business data and logic. Active Record facilitates the creation and use of business objects whose data requires persistent storage to a database.
Object Relational Mapping
Using ORM (Object Relational Mapping), the properties and relationships of the objects in an application can be easily stored and retrieved from a database without writing SQL statements directly and with less overall database access code.
Active Record gives us several mechanisms, the most important being the ability to:
- Represent models and their data.
- Represent associations between these models.
- Represent inheritance hierarchies through related models.
- Validate models before they get persisted to the database.
- Perform database operations in an object-oriented fashion.
Convention Over Configuration
With Convention Over Configuration, you must follow some conventions. So, for a model called Book, you should have a database table called
books. In fact:
- Database Table - Plural with underscores separating words (e.g., book_clubs).
- Model Class - Singular with the first letter of each word capitalized (e.g., BookClub). Models are Ruby classes!
There are conventions also for some fields in database tables:
- Primary keys: by default, Active Record will use an integer column named
idas the table's primary key. When using Active Record Migrations to create your tables, this column will be automatically created.
- Foreign keys: These fields should be named following the pattern
order_id). These are the fields that Active Record will look for when you create associations between your models.
There are also some optional column names that will add additional features to Active Record instances:
created_at: Automatically gets set to the current date and time when the record is first created.
updated_at: Automatically gets set to the current date and time whenever the record is updated.
CRUD: Create, Read, Update, Delete with ActiveRecord
At this point, we have a new
Incident resource. We also have routes to manage it. Let's us take a look at the routes (It may take some seconds...) by visiting
localhost:3000/rails/info/routes. You can also view them in your shell by typing:
A list of new rules should appear. All these routes in fact are resulted from a single line written in the
Now, let's us play a bit with ActiveRecord! Open a Rails console.
Let's create a new incident. The method
.new as you might expect creates a new Article with
nil values for all its attributes
(id: nil, title: nil, body: nil, ...). The
.save method allow us to store the created incident in the database. Note that when you save an incident in the database the
id attribute is automatically incremented.
Let's add another one:
Instead of using
.new and then
.save you can just use
.create to obtain the same result.
There are a lot of default methods that allow you to read data from the database.
You can edit or update an existing article as follows:
However, you can also write it down in the following way:
In order to delete or remove an article form the database, let's use the
You can also delete all the articles as follows
Note that the methods
delete_all are not the same.
If you want to read more, you can find useful information here.
Focus on the views
The default layout for the views of our application is
application.html.erb. It acts like a container for all the HTML that will be rendered by the controllers. When a controller renders a view in response to a method call, it looks for the layout and replaces
yield with the compiled view for the invoked method.
app/views/incidents/show.html.erb and change it a bit to learn ERB.
@incident come from? Look at the controller's
What happens if I call
@pippo in my view? This will be an instant variable with value
nil. If I try to call
pippo, it will look for a local variable or method called
pippo (see the error message).
We can change a few things just as an example
Exercise! Try doing something interesting with
index.html.erb. Notice the use of
@incidents.each. For example:
The form (incidents/_form.html.erb)
This is a partial (more on these later) that uses several helpers to simplify the creation of forms (if you use the
simple_form gem you get even simpler helpers... but more headaches). Take a look and play around a bit.
Rails models can declare validations that are automatically run every time we try to write to the db. For example, we want every incident to have coordinates.
As you know, latitude and longitude have to be within specific ranges. So we can add the following validations to our
Now try entering invalid data in the form and see what happens.