Advertisement

From CodeIgniter to Ruby on Rails: A Conversion

by
Student iconAre you a student? Get a yearly Tuts+ subscription for $45 →

Today, we'll be creating a simple shoutbox using the CodeIgniter PHP framework. We'll then port this exact application, piece-by-piece, to Ruby on Rails!


Final Product

Throughout this tutorial, we'll create a simple shoutbox application in CodeIgniter, and then port it over to Ruby on Rails. We'll be comparing the code between the two applications to see where they are similar, and where they differ. Learning Rails is much easier if you are already familiar with an MVC (Model, View, Controller) framework structure.

Do I Need Previous CodeIgniter Experience?

Yes, or experience in PHP and another MVC framework (CakePHP, Zend etc.). Check out some of the other CI tutorials here on Nettuts, including the from scratch video series!

Do I Need Previous Ruby/Rails Experience?

While it would help, no. I've done my best to make direct comparisons between Ruby and PHP, and between Rails and CodeIgniter, so by applying your existing knowledge it won't be too hard.

Hello, CodeIgniter

Begin by downloading the CodeIgniter framework to your local/web server, and set up your database (I named it ci_shoutbox) with the following SQL commands:

CREATE TABLE `shouts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `email` varchar(255) NOT NULL,
  `message` text NOT NULL,
  PRIMARY KEY (`id`)
);

INSERT INTO `shouts` (`id`,`name`,`email`,`message`)
VALUES
  (1,'Dan Harper','email@example.com','Hello, World!'),
  (2,'Bob','a@a.com','This looks great!'),
  (3,'Dan Harper','email@example.com','Hey, thanks for the comment!');

As usual in CodeIgniter, enter your details into /system/application/config/database.php:

$db['default']['hostname'] = "localhost";
$db['default']['username'] = "root";
$db['default']['password'] = "";
$db['default']['database'] = "ci_shoutbox";

Then config.php:

$config['base_url']	= "http://localhost/shoutbox/";
...
$config['global_xss_filtering'] = TRUE;
...
$config['rewrite_short_tags'] = TRUE;

And autoload.php:

$autoload['libraries'] = array('database', 'form_validation', 'session');
$autoload['helper'] = array('url', 'form');
...
$autoload['model'] = array('shout');

Finally set the default controller inside routes.php:

$route['default_controller'] = "shouts";

Controller & Model

Since this is such a simple application, we only need one controller and one model. Inside /controllers/ create shouts.php:

<?php
class Shouts extends Controller {

  function Shouts() {
    parent::Controller();
  }

}

And the model as /models/shout.php:

<?php
class Shout extends Model {

  function Shout() {
    parent::Model();
  }

}

Retrieving the Shouts

Our front page, the index, outputs the 10 latest shouts from the database. Start by adding the following to the Shouts controller:

function index() {
  $data['shouts'] = $this->shout->all_shouts();
  $this->load->view('shouts/index.php', $data);
}

On line 2, we call the all_shouts() function from the Shout model.
Following this, we load the relevant view file, and pass the $data variable along to it (CodeIgniter will automatically take apart the $data array, so that we can access the shouts simply as $shouts instead of $data['shouts']).

Calling the Database

Let's create the all_shouts() function in our model now:

function all_shouts() {
  $data = array();
  $this->db->order_by('id', 'DESC');
  $q = $this->db->get('shouts', 10);
  
  if ($q->num_rows() > 0) {
    foreach ($q->result() as $row) {
      $data[] = $row;
    }
  }
  
  return $data;
  $q->free_result();
}

This is relatively straight-forward. We retrieve the 10 most recent records from the 'shouts' table, and output them in descending order. This will build the following SQL command behind-the-scenes:

SELECT * FROM `shouts` ORDER BY `id` DESC LIMIT 10;

If any records were found, they are added to the $data array and returned to the controller.

The View

Inside the /views/ directory, create the following files and folders:

  • footer.php
  • header.php
  • /shouts/
    • index.php

Add the following to /shouts/index.php:

<?php
$this->load->view('/header.php');

if ($shouts) {
  echo '<ul>';
  foreach ($shouts as $shout) {
    $gravatar = 'http://www.gravatar.com/avatar.php?gravatar_id=' . md5(strtolower($shout->email)) . '&size=70';
    ?>

    <li>
      <div class="meta">
        <img src="<?= $gravatar ?>" alt="Gravatar" />
        <p><?= $shout->name ?></p>
      </div>
      <div class="shout">
        <p><?= $shout->message ?></p>
      </div>
    </li>

    <?php
  }
}
else {
  echo '<p class="error">No shouts found!</p>';
}

$this->load->view('/footer.php');

On line 2 we include header.php.
If any shouts were returned from the database, we loop through them using foreach and create a basic list with the author's Gravatar, their name and the message. If no shouts were found, we display an error message.
Finally, we include the footer.php.

Note that on line 7, we build the Gravatar URL. The URL contains an MD5 hash of the user's email address. We use PHP's strtolower() function to ensure the email is all lower-case in case the Gravatar service is case-sensitive.

Continuing on, we need a form at the bottom of the page to add a new shout. Before we include the footer view file, add the following:

echo form_open('shouts/create'); ?>
  <h2>Shout!</h2>

  <div class="fname">
    <label for="name"><p>Name:</p></label>
    <input type="text" name="name" value="<?= set_value('name') ?>" />
    </div>

  <div class="femail">
    <label for="email"><p>Email:</p></label>
    <input type="text" name="email" value="<?= set_value('email') ?>" />
  </div>

  <textarea name="message" rows="5" cols="40"><?= set_value('message') ?></textarea>

  <p><input type="submit" value="Submit" /></p>
  <?php
echo form_close();
$this->load->view('/footer.php');

On line 1 we use the form_open() function from CodeIgniter's Form helper. This generates a <form> opening tag with the relevant parameters, and will point to shouts/create (the Create function inside the Shouts controller).
The rest is a normal HTML form, except we echo the set_value() function for each 'value'. We will be adding validation to the form shortly and this ensures that if there are any errors, the submitted data is automatically re-entered into the form.

Finally, we'll need to create the header & footer. Add the following to /views/header.php:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Shoutbox in CodeIgniter</title>
<link rel="stylesheet" href="<?php echo base_url(); ?>css/style.css" type="text/css" />
</head>
<body>
<div id="container">

  <h1>Shoutbox</h1>
  <h5>
    <a href="http://www.danharper.me" title="Dan Harper">Dan Harper </a> :
    <a href="http://net.tutsplus.com" title="Nettuts - Spoonfed Coding Skills">Nettuts</a>
  </h5>

  <div id="boxtop"></div>
  <div id="content">

See on line 8, when including the stylesheet, we use the base_url() function from CodeIgniter's URL helper to generate the URL to the root of the site – this will put the stylesheet at http://example.com/css/style.css.

Add the following to /views/footer.php:

  </div><!--/content-->
<div id="boxbot"></div>
</div><!--/container-->
</body>
</html>

Submission & Validation

Since the form directs to a Create function in the Shouts controller, let's create that now:

function create() {
  $data['shouts'] = $this->shout->all_shouts();
  
  $this->form_validation->set_rules('name', 'Name', 'required|max_length[255]|htmlspecialchars');
  $this->form_validation->set_rules('email', 'Email', 'valid_email|required|max_length[255]|htmlspecialchars');
  $this->form_validation->set_rules('message', 'Shout', 'required|htmlspecialchars');
  
  if ($this->form_validation->run() == FALSE) {
    $this->load->view('shouts/index.php', $data);
  }
  else {
    $this->shout->create();
    $this->session->set_flashdata('success', 'Thanks for shouting!');
    redirect('shouts/index');
  }
}

On line 2 we retrieve the shouts (as we did on the index function), as they still need to be shown.
Lines 4-6 are our validation rules. We first pass the input's 'name' from the submitted form to check, followed by a 'human-readable' version. The third parameter holds a pipe-separated ('|') list of validation rules.

For example, we set all the fields as 'required' as well as run them through 'htmlspecialchars' before submission. 'Name' and 'Email' have a max length set, and the submitted email field must resemble an email address.

If the validation fails, the index view is loaded again; otherwise we run the create() function from the Shout model. On line 13, we set a success message in the flash data which will be displayed on the next executed page.

Before continuing, we must display the error and success messages (see images above) in our view. Add the following directly after we include the header in /views/shouts/index.php:

if ($this->session->flashdata('success')) {
  echo '<p class="success">' . $this->session->flashdata('success') . '</p>';
}

echo validation_errors('<p class="error">', '</p>');

Insert Into Database

Inside the Shout model, enter the following:

function create() {
  $data = array(
    'name'    =>  $this->form_validation->set_value('name'),
    'email'   =>  $this->form_validation->set_value('email'),
    'message' =>  $this->form_validation->set_value('message')
  );
  $this->db->insert('shouts', $data);
}

Very simple, we compile an array containing the submitted data, and insert it into the `shouts` table.

Styling

In the very root directory for your application – in the same directory as CodeIgniter's /system/ folder, create two new folders: /css/ and /images/.
In the CSS folder, add the following to a file named style.css:

* {
margin: 0;
padding: 0;
}

body {
background: #323f66 top center url("../images/back.png") no-repeat;
color: #ffffff;
font-family: Helvetica, Arial, Verdana, sans-serif;
}

h1 {
font-size: 3.5em;
letter-spacing: -1px;
background: url("../images/shoutbox.png") no-repeat;
width: 303px;
height: 66px;
margin: 0 auto;
text-indent: -9999em;
color: #33ccff;
}

h2 {
font-size: 2em;
letter-spacing: -1px;
background: url("../images/shout.png") no-repeat;
width: 119px;
height: 44px;
text-indent: -9999em;
color: #33ccff;
clear: both;
margin: 15px 0;
}

h5 a:link, h5 a:visited {
color: #ffffff;
text-decoration: none;
}

h5 a:hover, h5 a:active, h5 a:focus {
border-bottom: 1px solid #fff;
}

p {
font-size: 0.9em;
line-height: 1.3em;
font-family: Lucida Sans Unicode, Helvetica, Arial, Verdana, sans-serif;
}

p.error, .errorExplanation li {
background-color: #603131;
border: 1px solid #5c2d2d;
padding: 10px !important;
margin-bottom: 15px;
}

p.success {
background-color: #313d60;
border: 1px solid #2d395c;
padding: 10px;
margin-bottom: 15px;
}

#container {
width: 664px;
margin: 20px auto;
text-align: center;
}

#boxtop {
margin: 30px auto 0px;
background: url("../images/top.png") no-repeat;
width: 663px;
height: 23px;
}

#boxbot {
margin: 0px auto 30px;
background: url("../images/bot.png") no-repeat;
width: 664px;
height: 25px;
}

#content {
margin: 0 auto;
width: 600px;
text-align: left;
background: url("../images/bg.png") repeat-y;
padding: 15px 35px;
overflow: hidden;
}

#content ul {
margin-left: 0;
margin-bottom: 15px;
}

#content ul li {
list-style: none;
clear: both;
padding-top: 30px;
}

#content ul li:first-child {
padding-top:0;
}

.meta {
width: 85px;
text-align: left;
float: left;
min-height: 110px;
font-weight: bold;
}

.meta img {
padding: 5px;
background-color: #313d60;
}

.meta p {
font-size: 0.8em;
}

.shout {
width: 500px;
float: left;
margin-left: 15px;
min-height: 110px;
padding-top: 5px;
}

form {
clear: both;
margin-top: 135px !important;
}

.fname, .femail {
width: 222px;
float: left;
}

form p {
font-weight: bold;
margin-bottom: 3px;
}

form textarea {
width: 365px;
overflow: hidden;
}

form input, form textarea {
background-color: #313d60;
border: 1px solid #2d395c;
color: #ffffff;
padding: 5px;
font-family: Lucida Sans Unicode, Helvetica, Arial, Verdana, sans-serif;
margin-bottom: 10px;
}

And save the following images into the /images/ folder: (the images will be the correct size when saved!)

back.png
bg.png
bot.png
shout.png
shoutbox.png
top.png

To confirm, your directory structure should look like the image below:

And... done! If you think we wrote very little actual back-end code in CI, wait until we get to Rails where all database interaction is automated!

 

Hello, Rails

For this tutorial, I assume you already have Ruby, Rails and MySQL (or your preferred database engine) installed correctly on your system. If not, see the Rails installation guide and MySQL download page. If you happen to be running Mac OSX Leopard, Ruby and Rails both come pre-installed.

It's also useful to know how to navigate in your operating system using the command line. For example to change directory, use cd foldername, or cd C:\Users\Dan etc.

Rails provides a number of command-line tools to speed up development of your application. Inside the Terminal (or Command Prompt in Windows), navigate to the folder you wish to store your Rails projects in. Personally I store them in a /rails/ directory in my User area.

The Set Up

Run the following to dump a copy of Rails for your application:

rails -d mysql shoutbox

Here, Rails will install into a /shoutbox/ directory. As MySQL is my preferred database engine, I specify -d mysql. If you prefer to use MySQLlite (the default in Rails), you would simply run rails shoutbox.
Navigate into the shoutbox directory:

cd shoutbox

Now, we'll generate the Controller and Model:

ruby script/generate controller Shouts

ruby script/generate model Shout

Rails has now generated our 'Shouts' controller and 'Shout' model. It's important to know that Rails encourages a strict naming style.
Your controller should be named the plural of whatever it will deal with (eg. Shouts, Users, Listings). A controller typically will only deal with one model, which is named in the singular (eg. Shout, User, Listing). Following this standard will ensure Rails can use all it's functionality.

Inside your shoutbox directory, open the /config/database.yml file, this stores our database details. Enter your details under the 'development' section:

development:
  adapter: mysql
  encoding: utf8
  reconnect: false
  database: shoutbox_development
  pool: 5
  username: root
  password:
  socket: /tmp/mysql.sock

You can see Rails has automatically set the configuration file to be using the MySQL adapter. You will likely only need to alter the username and password.
Don't worry, you should not have created the shoutbox_development database yet. We'll create that next.

Next, open the /db/migrate/***_create_shouts.rb file (most versions of Rails will prefix the file name with the date and time):

class CreateShouts < ActiveRecord::Migration
  def self.up
    create_table :shouts do |t|

      t.timestamps
    end
  end

  def self.down
    drop_table :shouts
  end
end

This is a database migration file. Rails use these to help developers keep their databases structured correctly since they can become messy when several developers are altering database tables at the same time.
You will also be able to roll your database back to a previous version if you need to.

In here, we will add our database fields:

class CreateShouts < ActiveRecord::Migration
  def self.up
    create_table :shouts do |t|
      t.string :name
      t.string :email
      t.text :message
      t.timestamps
    end
  end

  def self.down
    drop_table :shouts
  end
end

This may look scary, but it's very simple! t.string :name creates a 'name' field in the database, which will be stored as a string (ie. 'varchar'). The same goes for the 'email' field.
A 'message' field is added and stored as text (as it is in SQL).

You will notice Rails automatically included t.timestamps. This includes 'created_at' and 'updated_at' fields which Rails will automatically fill in when a field is created or updated.
Also, Rails will automatically create an 'id' field, so we don't need to define that ourselves.

This is equivalent to the following SQL command:

CREATE TABLE `shouts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `message` text,
  `ipaddress` varchar(15) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
);

While it can take a little getting-used-to when creating these migrations, they're much easier to type than SQL!

Finally, run the following two commands in the Terminal to create and set up the database:

rake db:create

rake db:migrate

The first command creates the database, named 'shoutbox_development'. The second runs any out-standing database migrations (in this case, our ***_create_shouts.rb file).

Start the server with and then visit http://localhost:3000 (or whatever port the server says it is running on – in most cases it's 3000):

ruby script/server

To remove the default welcome page, first remove the file at /public/index.html, then inside /config/routes.rb, insert the following anywhere between the first and last lines:

map.root :controller => "shouts"

You will need to restart the server whenever you make alterations to the controller or to the routes – press Ctrl+C, then re-run ruby script/server.

That concludes the configuration process for Rails. While this whole process may seem complicated right now, you can do it in just a few minutes, and once you get used to it, it's much quicker than setting up a CodeIgniter project.

Dummy Data

Before display the shouts, we'll need some data in the database to display. You could create these entries through your preferred database management program, however we're going to use a great Rails feature instead – the interactive console.

From your application folder in the Terminal, run ruby script/console. This is an interactive Ruby interpreter, which is also hooked up to our Rails application, so we can interact with it!

One very important thing to remember with regard to Rails, is that your model is automatically linked with the corresponding database table. For example, your 'Shout' model is linked with the 'shouts' table in your database – this is a key reason why following Rails' naming conventions can be very useful!

Run the following ruby code inside the interactive console:

shout = Shout.new(
  :name => 'Dan Harper',
  :email => 'email@example.com',
  :message => 'Hello, World!')

Here, we invoke Rails' new method on the 'Shout' model – this will create a new record in the shouts database table. We also pass a hash containing the data we want to enter.
This is stored in the 'shout' variable.

Note: A 'hash' is what PHP calls an associate array – an array where you can also set your own key.
A rough PHP equivalent of this code would be:

$shout = $this->shout->new(
  'name' => 'Dan Harper',
  'email' => 'email@example.com',
  'message' => 'Hello, World!');

Finally, save this into the database with:

shout.save

If you entered the code correctly, ruby should return 'true'.
You can now retrieve this from the database with:

shout = Shout.find :last

shout.name

The first command will find the most recent row in the shouts table and store it in the 'shout' variable. We can find look at a specific item with shout.name which should return whatever you entered as the 'name' for the record.

Repeat the Shout.new and Shout.save process a few more times to add more records to the database.

Displaying Shouts

Inside the /app/controllers/shouts_controller.rb file, enter the following between the existing class statement:

def index
  @shouts = Shout.all_shouts
end

We call the all_shouts method from the 'Shout' model, and store the result inside the shouts instance variable (as seen by the @).

In CodeIgniter, the equivalent code was:

class Shouts extends Controller {

  function Shouts() {
    parent::Controller();
  }

  function index() {
    $data['shouts'] = $this->shout->all_shouts();
    $this->load->view('shouts/index.php');
  }

}

Some noticeable differences are:

  1. Rails uses the '<' symbol in place of 'extends' when creating a class;
  2. To create a function/method we use def instead of function;
  3. Rails doesn't require a constructor method;
  4. Rails automatically loads the view file for the current method. In this case, the view file is located at /app/views/shouts/index.html.erb;
  5. It is already noticeable that code is much cleaner in Rails. You don't need parenthesis (brackets) when defining a funcion/method if it doesn't contain any parameters, we don't need to add a semi-colon at the end of each statement and there are no curly brackets in sight!

The Model

Inside /app/models/shout.rb enter the following inside the class to define the all_shouts function:

def self.all_shouts
  Shout.find(:all, :limit => 10, :order => 'id DESC')
end

Here, we run Rails' find on the Shout model (ie. the shouts table). We pass a hash telling the method to limit the number of results to 10 in descending order.
This is the equivalent CodeIgniter code:

function all_shouts() {
  $data = array();
  $this->db->order_by('id', 'DESC');
  $q = $this->db->get('shouts', 10);

  if ($q->num_rows() > 0) {
    foreach ($q->result() as $row) {
      $data[] = $row;
    }
  }

  return $data;
  $q->free_result();
}

Yeah… so much simpler!
Also note that in Ruby, if you don't specifically return something, the last statement is automatically returned. For example, we could have used the following instead (although it would make no difference):

def self.all_shouts
  return Shout.find(:all, :limit => 10, :order => 'id DESC')
end

The View

In CodeIgniter, we had to manually include header & footer files in each view. Rails takes a slightly different approach. Inside /views/layouts/ create a file named application.html.erb with the following inside:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Shoutbox in Ruby on Rails</title>
<%= stylesheet_link_tag 'style', :media => 'screen' %>
</head>
<body>
<div id="container">

  <h1>Shoutbox</h1>
  <h5>
    <a href="http://www.danharper.me" title="Web Developer">Dan Harper </a> :
    <a href="http://net.tutsplus.com" title="Nettuts - Spoonfed Coding Skills">Nettuts</a>
  </h5>

  <div id="boxtop"></div>
  <div id="content">

    <%= yield %>

  </div><!--/content-->
<div id="boxbot"></div>
</div><!--/container-->
</body>
</html>

This is predominantly a normal HTML layout, combining both our header and footer. On line 7 we include a link to a stylesheet. Rails provides a /public/ folder in which you include images, stylesheet, javascript files and any static HTML files. In this case, the code will output a link to a stylesheet at /public/stylesheets/style.css.

Further down, on line 21 is <%= yield %>. This will insert the main view file for the specific page in this place.

Why .html.erb? All view files in Rails first have the extension for the specific format you want to output as, followed by .erb so the server will interpret the Ruby code inside it.
This means you could also have a .xml.erb file if you want to export in XML for certain pages, etc.

<%= %> Ruby uses <% and %> to wrap any Ruby code to be interpreted, in the same way PHP uses <?php and ?>.
On HTML pages, we use <%= ... %> (note the equals) to 'print' code. In PHP we use either <?php echo ... ?> or <?= ... ?> to 'echo' code.

Inside /public/stylesheets/ create a file named style.css containing the same CSS we used in the CodeIgniter section.
Also, paste the images from the CodeIgniter section into /public/images/.

If you restart the server now, and reload your browser, you should see a 'Template is missing' error page. This is because we haven't yet created the actual view for the current page.

The 'Template is missing' error page tells us what file it was expecting:
"Missing template shouts/index.html.erb in view path app/views"
So let's create that file now, and enter the following to test it out:

Hello, World!

Reload the page, and you should be greeted with the following (if not, check you have the stylesheet & images in the correct locations):

Looping

You will remember that in CodeIgniter, we used the code below to loop through and display each retrieved shout:

<?php
echo '<ul>';
foreach ($shouts as $shout) {
  $gravatar = 'http://www.gravatar.com/avatar.php?gravatar_id=' . md5(strtolower($shout->email)) . '&size=70';
  ?>

  <li>
    <div class="meta">
      <img src="<?= $gravatar ?>" alt="Gravatar" />
      <p><?= $shout->name ?></p>
    </div>
    <div class="shout">
      <p><?= $shout->message ?></p>
    </div>
  </li>

  <?php
}
echo '</ul>';

The equivalent in Ruby is the following (replace the 'Hello, World!' message with this):

<ul>
<% for shout in @shouts

  gravatar = 'http://www.gravatar.com/avatar.php?gravatar_id=' + Digest::MD5.hexdigest(h(shout.email.downcase)) + '&size=70' %>

  <li>
    <div class="meta">
      <img src="<%= gravatar %>" alt="Gravatar" />
      <p><%= shout.name %></p>
    </div>
    <div class="shout">
      <p><%= shout.message %></p>
    </div>
  </li>

<% end %>
</ul>

On line 2 is a normal 'foreach'-style loop in Ruby. Instead of the foreach ($shouts as $shout) in PHP, we use for shout in @shouts in Ruby.

The loop closes at end on line 16.

On line 4 we build the Gravatar URL in almost the same way as in PHP, with a few exceptions:

  1. In PHP, we use the dot character (.) to concatenate statements, Ruby uses the plus character (+), just like in JavaScript;
     
  2. Ruby's version of PHP's md5() function is Digest::MD5.hexdigest();
     
  3. The h() function in Ruby is equivalent to PHP's htmlspecialchars();
     
  4. PHP uses -> to access an object (eg. $shout->email). Ruby uses a dot, (eg. shout.email);
     
  5. To ensure the email address is in lower-case, we use strtolower($shout->email) in PHP. In Ruby, everything is an object and so we simply add .downcase to the end of a string or variable – shout.email.downcase. You could also .upcase, .reverse, etc.

Refresh the page in your browser, and you should now see the shoutbox messages!

Submission Form

Continuing on, we need to include the form at the bottom of the page for submitting new shouts. First, take a look at the following line:

<% form_for :shout, :url => { :action => 'create' } do |f| %>
  ... code here ...
<% end %>

This is a block from Rails' form helper to easily create forms. form_for :shout links the form directly with the Shout model.

:url = { :action = 'create ' } is a hash detailing where the form will direct to when submitted. In this case, the form will go to /shouts/create/ (Shouts controller, Create method). We could have typed: :url = { :controller = 'shouts', :action = 'create }, however the lack of a 'controller' key in the hash tells Rails to use the current controller.

Finally, do |f| stores everything before in an f variable. For example, we could create a textbox linked to this using f.text_field

Now we've explained that, enter this full form code at the bottom of your index view file:

<% form_for :shout, :url => { :action => 'create' } do |f| %>

  <h2>Shout!</h2>

  <div class="fname">
    <label for="name"><p>Name:</p></label>
    <%= f.text_field :name, :size => 20 %>
  </div>

  <div class="femail">
    <label for="email"><p>Email:</p></label>
    <%= f.text_field :email, :size => 20 %>
  </div>

  <%= f.text_area :message, :rows => 5, :cols => 40 %>

  <p><%= submit_tag 'Submit' %></p>

<% end %>

And compare it with the CodeIgniter PHP version:

echo form_open('shouts/create'); ?>
  <h2>Shout!</h2>

  <div class="fname">
    <label for="name"><p>Name:</p></label>
    <input type="text" name="name" value="<?= set_value('name') ?>" />
  </div>

  <div class="femail">
    <label for="email"><p>Email:</p></label>
    <input type="text" name="email" value="<?= set_value('email') ?>" />
  </div>

  <textarea name="message" rows="5" cols="40"><?= set_value('message') ?></textarea>

  <p><input type="submit" value="Submit" /></p>
  <?php
echo form_close();

We make more use of Rails' form helper when creating the input fields, textarea and submit button. With f.text_field :name, :size = 20 we are creating a textbox named 'name' (matching the column in our database this should insert into). This would output in HTML as:

<input type="text" name="shout[name]" id="shout_name" size="20" />

In the CodeIgniter version of the app, we set each input's 'value' field to <?= set_value('email') ?> so CodeIgniter will automatically re-populate the fields if they fail the validation tests.
In Rails, this is handled automatically since we are using Rails' form helper to create the inputs.

Refresh the page in your browser, and you should see the form at the bottom:

Submission & Validation

The next step is to insert any submitted data into the database. The form directs to shouts/create, so let's create the create action now (inside the Controller):

def create
  @shouts = Shout.all_shouts

  @shout = Shout.new(params[:shout])
  if @shout.save
    flash[:notice] = 'Thanks for shouting!'
    redirect_to :action => 'index'
  else
    render :action => 'index'
  end
end

On line 2, we retrieve all our current shouts from the database since they still need to be displayed.

At line 4 we load Rails' new function on our Shout model. You will remember we used Shout.new when inserting dummy data into the database via the interactive console.
We pass new our submitted data with params[] – this is how Rails accesses POST data.
This is loaded into the @shout instance variable.

When we used the interactive console, we used shout.save to save the data. If it was successful, it returns 'true', otherwise 'false' is returned. So on line 5 we check whether the shout can be saved into the database, if so, we load a success notice in the session's 'flashdata', and redirect back to the index.
Otherwise, we load index's view file through the render function.

Validation

You may be wondering where our validation is handled, since we did this in the controller in CodeIgniter. In Rails, we place the rules inside the Model.
When Rails attempts to insert data to the database (eg. with @shout.save), the Model will automatically check the data against our rules before attempting to save it. Thus, if validation fails, then @shout.save will return 'false', the view will be re-loaded and the error messages will display.

Inside the Shout model, insert the following before we define self.all_shouts:

validates_presence_of :message
validates_length_of   :name,  :within => 1..255
validates_format_of   :email, :with   => /^[a-z0-9_.-]+@[a-z0-9-]+\.[a-z.]+$/i

In true Ruby style, the validation rules read pretty much like natural English. On line 1 we ensure a 'message' exists.

Next we check the length of the 'name' field – it must be between 1 and 255 characters in length. 1..255 defines a range. We could define a range of between 10 and 15 with 10..15, for example.

Finally, we check the submitted email is in the format of an email address using a regular expression. Basically, the submitted email must contain five parts:

  1. A string containing a combination of letters, numbers, an underscore, full-stop or hyphen;
  2. The @ symbol;
  3. A string containing a combination of letters, numbers or a hyphen;
  4. A dot;
  5. A string containing a combination of letters or a hyphen.

And finally...

We now just need to display the error/success messages in the view (see above). Add the following at the top of the index view file:

<%= error_messages_for :shout, :header_message => nil, :message => nil %>
<%= '<p class="success">' + flash[:notice] + '</p>' if flash[:notice] %>

On the first line we render any error messages for the shout form. We set :header_message and :message to nil (Ruby uses 'nil', PHP uses 'null') to stop the function displaying the normal messages it displays (we just want the error messages).

The second line displays our flash'ed success message. Note the if flash[:notice] at the end of the line. This ensures everything before it will attempt to display if the flash notice exists.
Obviously, we could also have done the following instead; however it would have had the same effect:

<% if flash[:notice] %>
  <%= '<p class="success">' + flash[:notice] + '</p>' %>
<% end %>

That's It!

Go try it out, it should function exactly as the CodeIgniter version yet in quite a bit less code (which is also much easier to read!)

CodeIgniter:
Controller: 31 lines
Model: 33 lines
View: 69 lines
Total: 133 lines

Ruby on Rails:
Controller: 19 lines
Model: 11 lines
View: 64 lines
Total: 94 lines

While this tutorial may have made the process of developing a Rails application long-winded, take a look back through your code and see how simple it all is!

If this tutorial has sparked your interest in Ruby and Ruby on Rails, I strongly recommend picking up a copy of Rails for PHP Developers, Agile Web Development with Rails and/or Programming Ruby from the Pragmatic Programmers.

Now go! Go explore the world of Rails!


Advertisement