Saturday, October 13, 2012

Basics of Rails Part 2

In the last post, Basics of Rails Part 1, we created and ran the Rails application "attackresearch". Next,  we will change the Web Server to Unicorn as well as introduce the concept of Rake.

Something to note, Rails typically is run in three modes:
  • Test - Mode typically used for Unit Tests.
  • Development - Development environment, includes verbose errors and stack traces.
  • Production - Settings are as if you were running in this application in a production environment.
The default mode of running Rails locally on your machine is, development mode. Also, any command you enter will be run in the context of the development mode. This means both Rake tasks and Rails commands alike and also holds true for the Rails console which, can be your best friend.

Now obviously, if you've done something custom like `export RAILS_ENV=production` this would be different. Additionally, explicitly casting the mode in which something like the Rails console runs (example: rails console production) will change the default behavior or mode, rather.

What does all this mean? Well, really it means that you want to develop in development mode and run a production application in production mode. Pretty simple huh?

Time to configure for Unicorn versus the default Webrick web server. If you are asking yourself "why", the answer is fairly straightforward. Unicorn is meant for production and handles a large amount of requests better and overall,  is more configurable. For the purposes of this tutorial, we will use Unicorn for both development and production.

I want to demonstrate two ways of doing this. The first is by using a startup shell script. The other, for the purposes of an introduction to Rake tasks, will be to actually create a Rake task to start the application in lieu of a shell script.

Startup shell file:

Modify your Gemfile by uncommenting the line with the Unicorn gem. Also, while we are at it, let's uncomment the Bcrypt gem as well:

Run `bundle install`:

Make the startup script executable and fire it up:

The line `rvmsudo bundle exec unicorn $*` means...

  • rvmsudo  - Allows you to run sudo commands while maintaining your RVM environment. 
  • bundle exec = Directs bundler to execute the program which, automatically 'require'(s) all the gems in your Gemfile.
  • unicorn - Unicorn service.
  • $* - Any arguments passed to the script will be executed as part of the command inside of the script. Example: ./ -p 4444 translates to - `rvmsudo bundle exec unicorn -p 4444` and would start the server on port 4444.
Alternatively, we can just easily package this up as a Rake task. A Rake task is a repeatable task that can be executed using the `rake` command.  Nothing magical, it just harnesses Ruby goodness to convert your task definitions into an executable command.

There is an excellent tutorial on Rake available via the Railscasts site. For our purposes, let's create a Unicorn rake file. Do this under /lib/tasks and use the `.rake` extension.

Presumably, you may wish to have multiple tasks available to the Unicorn namespace. For instance, if you'd like to both start and stop the Unicorn service it would be beneficial to create a namespace titled "unicorn" with multiple tasks inside it. For the purposes of this tutorial, I will only cover building a start task as you can easily expand upon this. Also, since we are running the Unicorn service in an interactive mode, you can hit ctrl+c to stop it. 

I would like to note that having a start and stop task is very beneficial if you are running Unicorn detached (non-interactive), where the service runs in the background.

Moving along, here is the task...

Lines 1 & 9 - Begin and end the unicorn namespace definition.

Line 3 - Describe the task (useful at the console).

Line 4 - Define the task with the first argument "task" and any additional definitions (comma separated) are arguments. In this example, we except a port argument. 

Line 5 - We code some logic that says, port_command will equal either an empty string or "-p <port number>" and  if a port number is not provided (nil) it will equal an empty string.

Line 6 - This is a shell command that appends the result of port_command to `rvmsudo bundle exe unicorn`.

Let's list our tasks and see if it is available:

Success! Notice how the description and command format are auto-magically taken care of for you.'

You can run this in one of two ways.

`rake unicorn:start[4444]` (starts the Unicorn service on port 444) OR....

`rake unicorn:start` (starts it on the default port, 8080)

To recap, we've shifted off of Webrick and over to Unicorn. Also, we've introduced the concept of a Rake task.

Stay tuned for more parts in this series...




Michael said...

You can use rake -T instead of piping to grep, to search namespace like below.

$rake -T db
rake db:migrate # Migrate the database (options: VERSION=x, VERBOSE=false).
rake db:migrate:status # Display status of migrations
rake db:rollback # Rolls the schema back to the previous version (specify steps w/ STEP=n).
rake db:schema:dump # Create a db/schema.rb file that can be portably used against any DB supported by AR
rake db:schema:load # Load a schema.rb file into the database
rake db:seed # Load the seed data from db/seeds.rb
rake db:setup # Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the db first)

cktricky said...

For those that don't know Mike, he will be co-authoring this series with me and has a background in securing and building Ruby on Rails applications.

You can reach him on Twitter - @McCabe615