Monday, June 28, 2010

Ruby on Rails decryption - MVC

Okay, I don't know about the rest of you, but I'm frustrated at documentation from the ruby and from the ruby-on-rails community that enforce committing to a whole new way of thought in order to produce applications. As an experienced perl developer, I'm just looking for some basic equivalents and paths to conversion. So, as I figure things out and discover some new things, I'm going to publish them here.

This is not in any way meant to disrespect or disregard the "ruby way" as much as it is intended to just share techniques that I've learned. Anyway, on with the show!

Model-View-Controller was one of the hardest concepts to wrap my mind around when I started looking at rails. So, rather than try to understand MVC directly, I'm going to look at how the app functions and later relate to MVC. This may seem backwards to those who know, but sometimes the easiest way in is through the back door.

With most static applications, the path in the URL is the path to the file being processed. Apache does have some fanciness using mod_rewrite to alter the appearance of the path, but the concept is still the same in the end. MVC apps, such as ruby-on-rails or perl's catalyst, use the path to give instruction to the application. For example, in a rails app, a URL path that begins with domain.tld/foo/bar looks for the "bar" class in the "foo" controller. By default, the "bar" class loads the "bar.erb" view, but this can be altered in the class as needed (e.g., loading an error or rickroll page)

Didn't understand any of that? Let's break it up a bit further.

Let's say you created your first rails app and just wanted to try a simple "Hello World" page to see if things were working (or even further, let's say you wanted to create a rails app that didn't require a database, so using scaffold wasn't an option). You'd first create your rails app:

[10-06-28:13:57 brian@koolaid /Users/brian/rails] rails HelloWorld
create app/controllers
create app/helpers
create app/models
create app/views/layouts

... snip ...

create doc/README_FOR_APP
create log/server.log
create log/production.log
create log/development.log
create log/test.log
[10-06-28:13:57 brian@koolaid /Users/brian/rails] cd HelloWorld

Then, you'd dilligently run your generate script to create your first controller:

[10-06-28:13:57 brian@koolaid /Users/brian/rails/HelloWorld] script/generate controller hello
exists app/controllers/
exists app/helpers/
create app/views/hello
exists test/functional/
create test/unit/helpers/
create app/controllers/hello_controller.rb
create test/functional/hello_controller_test.rb
create app/helpers/hello_helper.rb
create test/unit/helpers/hello_helper_test.rb
[10-06-28:13:57 brian@koolaid /Users/brian/rails/HelloWorld]

Wow, that's a bunch of files for an app that doesn't even really work yet. Theoretically, you should be able to start your server and see something:

[10-06-28:13:57 brian@koolaid /Users/brian/rails/HelloWorld] script/server start -d
=> Booting Mongrel
=> Rails 2.3.8 application starting on
[10-06-28:14:00 brian@koolaid /Users/brian/rails/HelloWorld]

Visiting this place in the browser returns the sexy default home page for your rails app. But, what about your controller?

Routing Error

No route matches "/hello" with {:method=>:get}

(pardon the inaccurate styling... blogspot is being challenging right now... soon I will have my own stuff hosted somewhere) So, I have to create more to produce a workable page? Understandable, but still confused since there's no here-is-what-you-do-next doc.

The default action that rails will execute when there is no action is "index" (sound familiar, apache fans?). So, in your hello controller, you'll want to create an "index" object.

Oh, and for those who don't know ruby (which I barely know at this point), you will also want to create an "initialize" function. This is, in my perspective, analogous to the "new" construct used in perl, or the "__construct" function in php. The path in your rails app to the controller is always "app/controllers/[name]_controller.rb" for easy locating.

Here's the stock hello_controller we created just a bit ago:

class HelloController < ApplicationController

Pretty dull, huh? Oh, more ruby language syntax for you... the left angle bracket means that HelloController inherits the ApplicationController features. Anything you put into ApplicationController becomes available to all your other controllers. Fun, eh? This also means we don't have to fuss with an initialize function since we're inheriting it from ApplicationController.

Ok, so here goes with a simple index action:

class HelloController < ApplicationController
def index

Not too scary. Let's check out the response when we visit the page:

Unknown action

No action responded to index. Actions:

The error changed, which is actually good. This means what we did had an impact in the system. It's recognized the index action, but nothing happened. Why? Because there's no view!

So, let's give it a view.

The path to the views for a controller is "app/views/[controller_name]/[action_name].erb". In this case, we're using the "hello" controller and the "index" action, so let's rock it!
<title>Hello World!</title>
<h2>Hello World</h2>
<p>Hello from Ruby on Rails!</p>

Engage app/views/hello/index.erb

Hello World

Hello from Ruby on Rails!

And load up

Hello World

Hello from Ruby on Rails!

Success! Now that seemed like a lot for just publishing a plain static page. We'll get more into some fun stuff in the next post, but I wanted to get the basics out. Have fun getting started!