Starting a Rails App, Part 1

Many testers these days will find themselves in Rails shops and, even if that’s not the case, Rails is a good environment within which to build custom test solutions that require a database and a web interface. This is largely because when using Rails, you are using one of the most concise and expressive languages out there: Ruby. I plan to document some of my learning of the Rails ecosystem in two posts.

I covered some basics of learning Rails and the basis of Rails in previous posts, so I won’t repeat all that here. If you want to follow along, you should be able to install Ruby and Rails on your operating system of choice. I am using Ruby 2.0 and Rails 4.1.1. I also plan on using SQLite3 for the database. This is not going to be a post on “how to create a Rails app from soup to nuts.” This really is just about building a solid enough base where you feel like you can trust Rails enough. That was something I struggled with early on.

The Application

For the purposes of my learning, I’m going to create a very simple “Planets” application. This is going to be thrown together as I go, mainly based on what I think I can learn. The initial goal of this application is simple: be able to showcase some information about the planets in our solar system and, at some point, allow a user to calculate what their weight would be on one of these planets.

In true agile fashion, I certainly can’t be charged with overthinking this too much!

Essentially I’ll design what I need as I need it with only a rough guide as to where I’ll end up. Realistically I should probably do this “test-first”; practically, however, I’ve found doing anything test-first when you are also learning the framework you are writing in is an exercise in frustration.

I’ll write a lot of this in the first person (as in “I take this action”) but I’ll probably describe aspects in the second person (as in “you’ll notice that that action resulted in some effect”). I find I tend to do that anyway so I may as well call it out and hope it isn’t too jarring.

Finally, I’m not going to cover every aspect and nuance of Rails as I go or learn it. What I’m going to do is distill a sort of “first start” tutorial to a reader of this post based on what I’ve learned via my own research. I’m aiming to present just enough of what I consider to be the “right amount of information” with a focus on getting a reader as much exposure to Rails internals as possible and exploring how to get a working application started.

Creating the App

First I’ll use the Rails script to create a new application:

rails new planets

Rails spews out a lot of information about the project directory that just got created. Probably the most important, in terms of getting started, is recognizing that the app directory is where you store all of the components of your application. There are directories within that app directory for each of the major aspects of an MVC style application, specifically, models, views, controllers, and helpers. Beyond that, the config directory is where you store all aspects of configuration for your application and the db directory is where you store all files related to the database you are using. If you’re using SQLite3 the db directory is where your file-based database will be stored, at least by default.

Speaking of databases, creating an application like I did above has Rails assuming you want to use the default database system of SQLite3. You can create an application that uses another database system by using the -d option. For example, you could use commands like these:

rails new planets -d mysql
rails new planets -d postgresql
rails new planets -d oracle

Specifying a database management system like this will impact what you see in the config/database.yml file. That file contains all the information about the databases that your application will be using. For SQLite3, the database is file-based and, as I said, will be stored in the db directory by default, although you can change that. Another difference is that databases like MySQL, PostgreSQL, and Oracle will likely have authentication required to connect to a database whereas SQLite3 does not.

As this post goes on, I’m going to create a model, some views, a controller, and a migration or two; ultimately all of this will be for interacting with a planets table in my database. I’ll preface all that activity by saying that I could do all of it with just one command. Essentially, I could have Rails generate a scaffold for a Planet model. I’m not going to do that right away, however. I’m going to go through some of the steps.

Creating the Database

Rails uses different databases for development, test, and production environments. I’ll create those databases (based on information from the database.yml file) by doing the following:

rake db:create:all

If you’re using SQLite, you technically don’t have to do this because Rails takes care of creating the database for you when you operate in one of the environments. However, it doesn’t hurt to get into the habit of doing this even if you are using SQLite. Do keep in mind that for any other database, you must do this. For a database that requires authentication, as long as your authentication information for each database management system is correct in the database.yml file, Rails will be able to connect in and create the database within the DBMS.

I was curious if I could accidentally wipe out all my databases in one fell swoop but, as it turns out, nope. This command will not overwrite an existing database. So there is no harm in running this more than once.

Creating the Model

Now that the database is there, it’s time to create a model. Looked at simplistically, a Rails model exists to let your application use a database table. Models in Rails correspond to database table names. Rails actually gets pretty specific about this, due to conventions, so here’s a good heuristic to keep in mind: model names are camel-cased singular and correspond to tables that are lower-cased plural. This means that a model named Planet will be associated with a database table named planets. So I create my Planet model:

rails generate model Planet

Incidentally, some tutorials show you the command like I just did: with a capitalized model name. Others show you the command with a lowercase name (“planet”). As it turns out, it doesn’t matter; either way will work just fine.

The output from the command I get is something like the following:

create    db/migrate/20140520104833_create_planets.rb
create    app/models/planet.rb
create    test/models/planet_test.rb
create    test/fixtures/planets.yml

You get a database migration that has a timestamp as part of the filename. A migration represents a change that you want to make to the data or the schema that represents the data. More specifically, a migration contains instructions for creating or modifying the tables and columns of your database. Just know that whenever you generate a new model, a migration is created along with it. Incidentally, it’s called a “migration” because it allows you to evolve, or migrate, your schema.

Along with the migration, you get the model itself (planet.rb). You also get a unit test stub (planet_test.rb) for testing your model’s functionality. Finally, you get a planet fixture (planets.yml) which is a textual representation of table data that you can use for testing purposes. The fixture is essentially a way to make sure you always have valid data available for your tests for a particular model. I’ll get more into fixtures later.

These files are just stubs at this point and contain little useful beyond their presence. For example, take a look at the model file, planet.rb:

Just an empty class. You can see, however, that my Planet class derives from ActiveRecord::Base. I’ll jump the gun a bit here and say that all adding, editing, and deleting of planets is going to happen through Active Record. Active Record lets you communicate with your database using Ruby code rather than SQL.

Getting a little more detailed, Active Record is an Object Relational Mapping (ORM) tool but an interesting thing about this is that Rails is agnostic about the ORM you use. You can use Active Record completely outside of a Rails context and you can bring your ORM of choice into the Rails context. Active Record is simply what Rails supports by default. One final note on this, just because I think it’s interesting: Active Record is actually a design pattern. The basis of the pattern is a one-to-one mapping between a database record and the object that represents it.

The test for this model is similarly barren:

Since there is nothing in the model, there’s nothing to test. However, the file that Rails generates does give you some information. You can see that PlanetTest is derived from ActiveSupport::TestCase. Rails uses the MiniTest framework that is part of Ruby so tests are derived from MiniTest::Unit::TestCase. However, Rails provides an enhanced version of that framework as part of ActiveSupport. The commented test gives you the basic pattern of how to write tests. Specifically, tests are implemented as blocks using the test() method. The first parameter to this method is a description of that test. Within a test, assertions are used to test expectations.

Incidentally, this brings up a good point: when Rails generates files, take the time to look them over. I see many people who, during the process of learning Rails, simply accept whatever the console output tells them without looking into what Rails is doing.

Finally, here’s the initial state of the migration file:

I’m going to add a few things to this migration before I do anything with it. Keep in mind that the migration is going to say what my database schema looks like. So here I’m going to make sure I put in a few columns that I want my database to have.

Incidentally, I could have had those fields created for me in the migration by specifying them at the time I created the model. So I could have done this:

rails generate model Planet name:string image_url:string details:text facts:text

With the logic in the migration file, you can see that the create_table() method is called which provides the name of the table to create (planets). Inside the block, the string and text methods each create a column that is named after the parameter. So t.string :name creates a field (column) named name with the type string.

You’ll have noticed that the one thing that was provided for you is the t.timestamps. This is used by Rails to create two fields — created_at and updated_at. As you can no doubt imagine, Rails sets the value of these fields to the date when a given database record is created and updated, respectively.

Now that the migration is how I want it to be, I’ll run the migration to create the table.

rake db:migrate

When you run this command, the Rake tool looks for all the migrations not yet applied to the database and applies them. How does Rake know what hasn’t been applied? Well, in part, that’s exactly what the timestamp is for as part of the migration filename. In this case, the above action means that the planets table is added to the database defined by the development section of the database.yml file. Why development? Because that’s the environment or mode that Rails runs in by default.

Now, if you’re like me, you want to have options for the “What if I screwed up?” situation. You can roll back migrations that you just applied. Use rake db:rollback for that purpose. If you did that after the above command, it would simply delete (or drop, as they say) the planets table from the database, thus reversing the create_table action.

What if you had more than one migration you wanted to rollback? You could do that like this: rake db:rollback STEP=3. That would rollback the last three migrations. There are other variations that I won’t cover here; just know you have options if you find yourself in one of those “uh oh” moments.

Creating the Controller

Now I can create a controller named planets. In Rails, the convention is that controllers are plural and models are singular. This particular controller will control the operation of the application’s “planets functionality,” whatever that turns out to be. Realistically what this means is that the controller will provide a means to get information into and out of the model, the latter of which, remember, is going to store the actual data about planets.

So I’ll generate my controller:

rails generate controller planets

I get output looking like this:

create  app/controllers/planets_controller.rb
create  app/views/planets
create  test/controllers/planets_controller_test.rb
create  app/helpers/planets_helper.rb
create  test/helpers/planets_helper_test.rb
create  app/assets/javascripts/planets.js.coffee
create  app/assets/stylesheets/planets.css.scss

You get a controller (planet_controller.rb) that handles any requests or responses that have to do with planets. Along with that there’s a test stub (planets_controller_test.rb) for holding any tests for this controller. If this controller will utilize any utility methods to help it talk with views, those can be placed in the provided helper class (planets_helper.rb). And, as with the controller itself, there is a test stub (planets_helper_test.rb) created to hold tests for these utility methods.

Two other files generated may seem a little odd: the JavaScript (planets.js.coffee) and CSS (planets.css.scss) files. Or, rather, the CoffeeScript and Sassy CSS files. The idea with these files is that if any views for the planets are going to rely on scripts or stylesheets, the stubs are in place to handle that.

CoffeeScript and SASS are languages that compile into JavaScript and CSS, respectively. Rails supports these by default by using something called the “asset pipeline”, introduced in Rails 3, which became “sprockets-rails” in Rails 4. For now just know that these .coffee and .scss files get compiled into the more standard languages — .js and .css, respectively. This happens automatically and ensures that a browser actually recognizes the files.

It’s important to note that the controller generator also created an empty directory of app/views/planets. Rails doesn’t put any stub views in there but this is where you would place the templates for the planets controller.

Using Scaffolding

So now here we get a bit into the weeds and into an interesting aspect of Rails. There’s a concept in Rails called “scaffolding”, the purpose of which is to allow you to create a boilerplate-style set of actions (in a controller) and templates (for views) that make it easy to manipulate data for a specific model.

It’s important to understand that you can — and arguably should — code all that stuff without using scaffolding. (In fact, that’s what I did in my post on learning Rails.) Here, however, I’m going to use it just to see what it does.

So first I’m going to create a scaffold that supports my existing model of Planet:

rails generate scaffold Planet name:string image_url:string details:text facts:text --skip-migration

You’ll notice here I’m generating this scaffold using the same columns/fields for when I created the model earlier. This is where I’m coloring outside the lines a little bit. Normally, if planning to use scaffolding, you would not have created the controller and model as I originally did in this post. Rather you would have simply used the scaffolding as I just did with that last command.

Okay, so then why am I doing this now, essentially recreating some of what I already did? Well, the scaffold simplifies giving me the actions to test out my model while still retaining the model I already created.

Because of the fact that I already created some of this material, I’m going to be asked by the scaffold generator if it can overwrite some of that stuff with its own new versions of those files. Specifically, I got this:

Overwrite /planets/test/fixtures/planets.yml?
Overwrite /planets/app/controllers/planets_controller.rb?
Overwrite /planets/test/controllers/planets_controller_test.rb?
Overwrite /planets/app/assets/javascripts/planets.js.coffee?
Overwrite /planets/app/assets/stylesheets/planets.css.scss?

I answered ‘y’ to all of those. Also, the generator may or may not warn you about the planets.js.coffee script; I’m not sure why.

Please keep something in mind here: because my planets controller was empty, it’s entirely safe for me to overwrite it. The same goes for the test and fixture. Clearly had I done work in those files, this would have been a bit more problematic. Then again, had I already done the work in those files, I probably wouldn’t be using scaffolding.

I want to make one point very clear here: if you run the model generator, it creates a migration to in turn create the table associated with the model — unless you specify the --skip-migration option. You’ve seen both variations of this in action now. You can also generate a migration on its own, without generating a model. More on that later.

A lot of output is given with the above command, but here’s a few things to call out:

route    resources :planets
create   app/views/planets/index.html.erb
create   app/views/planets/edit.html.erb
create   app/views/planets/show.html.erb
create   app/views/planets/new.html.erb
create   app/views/planets/_form.html.erb

Resources are configured in the config/routes.rb file using the resources() method. Without going into too many details, the resources() method generated some named routes for me based on the controller called “planets”. Notice here that the app/views/planets folder — originally left as empty — is now populated with some views. These views are pages that can be called up in a browser. We’ll get to that in a bit.

Another thing that stands out: I used the --skip-migration option when calling the generator, which I mentioned a few moments ago. But why did I do that? Because a migration creating the planets table already existed. That was one of the first things I did earlier. If I did not tell Rails to skip creating a migration this time around, Rails would have created the same migration as before. Would that be so bad? Well, think back to our first migration. That migration attempts to create a table. So if a second migration was created this time, it would also be trying to create a table and Rails won’t allow a table to be created twice.

So what did I get for all this “convenience” of using the scaffold? Quite a bit, actually. The scaffold provided methods and pages (views) that allow me to create, display, update, and delete records in my planets database. I actually now have a working example of the Planet model. To see that, start up the server:

rails server

Then go to the http://localhost:3000/planets url.

This takes me to a listing page for my planets. There’s no data in the database yet so, not surprisingly, this page is empty. This page, however, corresponds to the following action in the planets_controller.rb file:

That’s an example of the controller calling out to my model (Planet) and saying “Get me all the planets in the database and store them in the instance variable @planets.” Again, there are no items in the database yet, so nothing is returned.

If you look in the console where you started the server, you’ll see that the act of visiting the page gives you some information on how Rails is working (such as calling PlanetsController#index) and getting information from the database:

Started GET "/planets" for 127.0.0.1 at 2014-05-25 05:56:53 -0500
Processing by PlanetsController#index as HTML
  Planet Load (0.0ms)  SELECT "planets".* FROM "planets"
  Rendered planets/index.html.erb within layouts/application (2.0ms)
Completed 200 OK in 11ms (Views: 9.0ms | ActiveRecord: 0.0ms)

In the browser, click the New Planet link, and you’re taken to a screen where you can enter information about a planet. Notice that the URL is http://localhost:3000/planets/new. In Rails, this URL means you’re invoking the new action on the planets controller, which looks like this (all due to scaffolding):

This is an example of a new instance of a model object (Planet) being created and stored in a @planet instance variable. Behind the scenes, you’ll see this:

Started GET "/planets/new" for 127.0.0.1 at 2014-05-22 19:27:35 -0500
Processing by PlanetsController#new as HTML
  Rendered planets/_form.html.erb (47.0ms)
  Rendered planets/new.html.erb within layouts/application (64.2ms)
Completed 200 OK in 84ms (Views: 77.7ms | ActiveRecord: 2.0ms)

It would be a shame not to see if the scaffolding generated something that actually worked, so try it out! Add a planet. The information entered into the fields at this point doesn’t matter.

You’ll notice that every time you add a planet, you’re taken to a view that shows you details of what you just entered. What actually happens here is that the create action on the controller is called and then the show action is called, the methods of which should look something like this in the controller:

It was interesting to me that show() was entirely empty but apparently that’s a part of Rails that “just works.” You can see in the create() method that there are redirect_to() and render() methods that apparently handle some or most of this in some way, which is probably opaque to you at first glance.

Behind the scenes, once again, you can confirm what’s going on when you create a new database record:

Started POST "/planets" for 127.0.0.1 at 2014-05-22 19:31:36 -0500Started POST "/planets" for 127.0.0.1 at 2014-05-25 05:59:28 -0500
Processing by PlanetsController#create as HTML
  Parameters: {"utf8"=>"?", "authenticity_token"=>"WEnpNfPy4B4LIwxxjSO883Qwe+tRS7Q1K4PbpE/mk4Y=", "planet"=>{"name"=>"Mars", "image_url"=>"mars.jpg", "details"=>"Details on Mars", "facts"=>"Facts about Mars"}, "commit"=>"Create Planet"}
   (0.0ms)  begin transaction
  SQL (1.0ms)  INSERT INTO "planets" ("created_at", "details", "facts", "image_url", "name", "updated_at") VALUES (?, ?, ?, ?, ?, ?)  [["created_at", "2014-05-25 10:59:28.940556"], ["details", "Details on Mars"], ["facts", "Facts about Mars"], ["image_url", "mars.jpg"], ["name", "Mars"], ["updated_at", "2014-05-25 10:59:28.940556"]]
   (5.0ms)  commit transaction
Redirected to http://localhost:3000/planets/1
Completed 302 Found in 27ms (ActiveRecord: 6.0ms)


Started GET "/planets/1" for 127.0.0.1 at 2014-05-25 05:59:28 -0500
Processing by PlanetsController#show as HTML
  Parameters: {"id"=>"1"}
  Planet Load (1.0ms)  SELECT  "planets".* FROM "planets"  WHERE "planets"."id" = ? LIMIT 1  [["id", 1]]
  Rendered planets/show.html.erb within layouts/application (1.0ms)
Completed 200 OK in 13ms (Views: 9.7ms | ActiveRecord: 1.0ms)

On the page you are taken to after creating a planet, you can either go “back” (redirected back to the index action, where you see all your planets listed) or you can edit the entry that you just created. For now, just go back to the index page and make sure the planet shows up on the list. Not only should it be there, but you’ll also see that links were generated that allow you to edit, delete or show the planet. Should you choose to edit the planet, you’ll actually be calling the update() method in the controller. If you look at that method and compare it with the create() method, you’ll see what seems to be an instance of Rails not being entirely “dry”, but maybe that’s just the scaffolding?

The only point there being: don’t just trust what Rails is doing or generating for you. Look at the files that are generated and try to understand what Rails is doing and how it is doing it. In this post, I’m not going to cover all of those details because it can become a bit of a rat’s nest talking about how Rails handles HTTP verbs while also just trying to get something done with your application. You’ll probably note that there are small comments in the planets_controller.rb file that tell you what HTTP verb Rails is responding to for each action method.

Enhance the Model

Now that I can see the model represented in the browser, I’m going to add some more fields to make the model capable of ultimately storing what I need it to for calculating someone’s weight on a given planet. Whenever you need to add or modify database fields, you should do so using a migration. I didn’t need to generate the last migration (the one used to create the planets table), because the model generator took care of that for me. But this time around, I’m adding new fields entirely on an existing model and so I’m going to use the migration generator. So I do this:

rails generate migration add_diameter_and_mass_to_planets diameter:float mass:float

With this command, the generator creates a migration class in db/migrate, once again prefixed by a number identifying when the migration was created. In the file you see the migration class. As with the model generator I originally used, which prefilled the migration to some extent, passing the field names and types as options to the migration generator prefills the generated class:

However, and this is a key point, this only works correctly if you refer to the correct table name at the end of the migration name. So in my case the “_to_planets” was critical to making this work.

Looking at the migration that was generated, you’ll see that it doesn’t attempt a “create_table” action because how I generated the migration (with “_to_planets”) made it clear to Rails that the table existed. Instead you will see “add_column” actions that do exactly that: add columns to the database schema.

With this new migration in place, I’ll apply the changes as I did before:

rake db:migrate

Nothing new there. If all goes according to plan, the planets table now has two new fields. There are ways I could confirm that before going further, but I’ll hold off on that for now. However, even though my database has these fields, my interface (view) still does not. At this point I could (and probably should) edit the view templates in the app/views/planets folder to add form elements for the new fields. Instead, however, I’ll keep on the scaffolding train and just call the generator again:

rails generate scaffold Planet name:string image_url:string diameter:float mass:float details:text facts:text --skip-migration

As before, you’ll be asked to overwrite some files:

Overwrite /planets/test/fixtures/planets.yml?
Overwrite /planets/app/controllers/planets_controller.rb?
Overwrite /planets/app/views/planets/index.html.erb?
Overwrite /planets/app/views/planets/show.html.erb?
Overwrite /planets/app/views/planets/_form.html.erb?
Overwrite /planets/test/controllers/planets_controller_test.rb?
Overwrite /planets/app/views/planets/index.json.jbuilder?
Overwrite /planets/app/views/planets/show.json.jbuilder?

And as before, when you generate new versions of the scaffold files, you run the risk of overwriting custom changes you may have made. This is working okay for me because I haven’t done anything in any of these files yet. I’m basically in my “generation phase.” Again, I can’t stress enough that you normally would not want to do this after you’ve done any work at all in your files.

At this point, you can check the “create a new planet” page (http://localhost:3000/planets/new) and make sure the new fields are there.

This would be a good time to once again check if all the functionality of the form works. Editing the form (perhaps to fill in the new fields) will call the update() method of the controller. It’s also possible to delete the data item. Rails gets violent and calls this “destroy” for some reason. Doing that calls the destroy() action method in the controller:

If you look back in your console after you remove the data item, you’ll see how Rails is accomplishing this:

Started DELETE "/planets/1" for 127.0.0.1 at 2014-05-25 06:32:27 -0500
  ActiveRecord::SchemaMigration Load (1.0ms)  SELECT "schema_migrations".* FROM "schema_migrations"
Processing by PlanetsController#destroy as HTML
  Parameters: {"authenticity_token"=>"WEnpNfPy4B4LIwxxjSO883Qwe+tRS7Q1K4PbpE/mk4Y=", "id"=>"1"}
  Planet Load (0.0ms)  SELECT  "planets".* FROM "planets"  WHERE "planets"."id" = ? LIMIT 1  [["id", 1]]
   (0.0ms)  begin transaction
  SQL (2.0ms)  DELETE FROM "planets" WHERE "planets"."id" = ?  [["id", 1]]
   (5.2ms)  commit transaction
Redirected to http://localhost:3000/planets
Completed 302 Found in 27ms (ActiveRecord: 9.2ms)

Seed the Model

In fact, now that I’ve destroyed my only planet, this brings up another good point. In order to keep developing this application, I’d like to have a consistent set of test data to work with in my development environment. Obviously I could just enter data in the interface form as I did but, as any good tester would say, I want the ability to load up a set of standard data on demand. Rails does help you do this by allowing you to provide seed data for your database. (Note: This is different than setting up data via the model fixture that Rails generated for you.)

To do this, I’ll modify the db/seeds.rb file. Here’s a completed example:

Here I first delete everything from the Planet table. This is seed data, meaning it’s meant to be the only data that I can consistently rely on and that the database is seeded with. So it starts from a clean slate. The “delete everything” part is really important because you don’t want to spend a lot of time adding in data only to wipe it out with your seed data.

After deleting from the table, the create!() method is called and I essentially just pass in the columns/field names with some relevant values. To see if this works, I just seed the database:

rake db:seed

Assuming you have some valid data in seeds.rb, the above command should put that data into the database and the data should be visible in the browser.

Wrapping Up

Well, this was certainly a long post, no? Hopefully it wasn’t terribly boring. One final thing I’ll say here: you now have a way to seed data and you have a series of generator commands, provided in this post, that essentially allow you to recreate your model, controllers and views at any time with the scaffolding.

That’s really important!

Why? Because this would be a great time to dig into the code that Rails has provided; play around with it; see if you can figure out how things work. Don’t be afraid to change things and see what happens. In fact, use this time to get used to the error messages that Rails will throw when something goes wrong. You can always use the seed data and the generators to get back to a fresh state. This is probably one of the best uses of scaffolding, in my opinion.

I’ve done nothing I’m afraid of losing and I can recreate the little I have done at any point. That gives me confidence to explore and it should do the same for you.

This is a good place to stop. What I end up with here is essentially a working application that can connect to a database. Further, I have a set of views that will display the data in that database as well as a form that will allow me to enter new data. One thing I don’t have right now is any sort of validation. I also don’t really have any tests. While Rails generated stubs, I’ve done nothing with them. These are areas I plan to tackle in the next post in this series.

Share

About Jeff Nyman

Anything I put here is an approximation of the truth. You’re getting a particular view of myself … and it’s the view I’m choosing to present to you. If you’ve never met me before in person, please realize I’m not the same in person as I am in writing. That’s because I can only put part of myself down into words.

If you have met me before in person then I’d ask you to consider that the view you’ve formed that way and the view you come to by reading what I say here may, in fact, both be true. I’d advise that you not automatically discard either viewpoint when they conflict or accept either as truth when they agree.

This entry was posted in Rails. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *