At the Forge - Ruby on Rails
Now that we know how to see the default message, let's move toward a “Hello, world” program. In Rails, there are two basic ways to do this. We can create a controller that returns HTML to the user's browser, or we can create a view that does the same. Let's try it both ways, so that we can better understand the relationship between controllers and views.
If all we want to do is include a simple, static HTML document, we can do so in the public directory. In other words, the file blog/public/foo.html is available under WEBrick—started by executing blog/script/server—at the URL /foo.html.
Of course, we're interested in doing something a bit more interesting than serving static HTML documents. We can do that by creating a controller class and then defining a method within that class to produce a basic “Hello, world” message. Admittedly, this is a violation of the MVC separation that Rails tries to enforce, but as a simple indication of how things work, it seems like a good next step.
To generate a new controller class named MyBlog, we enter the blog directory and type:
ruby script/generate controller MyBlog
Each time we want to create a new component in our Rails application, we call upon script/generate to create a skeleton. We then can modify that skeleton to suit our specific needs. As always, Rails tells us what it is doing as it creates the files and directories:
exists app/controllers/ exists app/helpers/ create app/views/my_blog exists test/functional/ create app/controllers/my_blog_controller.rb create test/functional/my_blog_controller_test.rb create app/helpers/my_blog_helper.rb
Also notice how our controller class name, MyBlog, has been turned into various Ruby filenames, such as app/views/my_blog and app/helpers/my_blog_helper.rb. Create several more controller classes, and you should see that all of the names, like FooBar, are implemented in files with names like foo_bar. This is part of the Rails convention of keeping names consistent. This consistency makes it possible for Rails to take care of many items almost magically, especially—as we will see next month—when it comes to databases.
The controller that interests us is my_blog_controller.rb. If you open it up in an editor, you should see that it consists of two lines:
class MyBlogController < ApplicationController end
In other words, this file defines MyBlogController, a class that inherits from the ApplicationController class. As it stands, the definition is empty, which means that we have neither overridden any methods from the parent class nor written any new methods of our own. Let's change that, using the built-in render_text method to produce some output:
class MyBlogController < ApplicationController def hello_world render_text "Hello, world" end end
After adding this method definition, we can see its results by going to http://192.168.2.3:3000/MyBlog/hello_world.
Notice how the URL has changed: static items in the public directory, such as our file foo.html, sit just beneath the root URL, /. By contrast, our method hello_world is accessed by name, under the controller class that we generated. Also notice that we did not need to restart Rails in order to create and test this definition. As soon as a method is created or changed, it immediately is noticed and integrated into the current Rails system.
If we define an index method for our controller class, we can indicate what should be displayed by default:
class MyBlogController < ApplicationController def hello_world render_text "Hello, world" end def index render_text "I am the index!" end end
Of course, it's not that exciting to be able to product static text. Therefore, let's modify our index method such that it uses Ruby's built-in Time object to show the current date and time:
def index render_text "The time is now " + Time.now.to_s + "\n" end
And voilï¿½ As soon as we save this modification to disk, the default URL (http://192.168.2.3:3000/MyBlog/, on my computer) displays the time and date at which the request was made, as opposed to a never-changing “Hello, world” message.
Let's conclude this introduction to Rails by separating the controller from its view once again. In other words, we want to have the controller handle the logic and the view handle the HTML output. Once again, Rails allows us to do this easily by taking advantage of its naming conventions. For example, let us modify our index method again, this time removing its entire body:
def index end
This might seem strange at first glance. It tells Rails that the MyBlog controller class has an index method. But it doesn't generate any output. If you attempt to retrieve the same URL as before, Rails produces an error message indicating that it could not find an appropriate template.
Because the template is a view, we can define it inside of the blog/app/views directory of our application. And because we are defining the index view for the MyBlog class, we modify the index.rhtml file in the my_blog subdirectory of views. Notice how Rails turns ThisName into this_name when it comes to directories. Doing so saves users from having to think about capitalization in URLs, while staying consistent with traditional Ruby class naming conventions.
.rhtml files are a Ruby version of the same kind of template that you might have seen before. It acts similarly to ASP and JSP syntax, with <% %> blocks containing code and <%= %> blocks containing expressions that should be interpolated into the template. However, nothing stops us from creating an .rhtml template that actually is static:
<html> <head> <title> Hello, again! </title> </head> <body> <p>Hello, again!</p> </body> </html>
Consider what happens now if you attempt to load MyBlog in your browser. The controller class MyBlog is handed the request. Because no method was named explicitly, the index method is invoked. And because index doesn't produce any output, the my_blog/index.rhtml template is returned to the user.
Finally, let's take advantage of our template's dynamic properties to set a value in the controller and pass that along to the template. We modify our index method to read:
def index @now = Time.now.to_s end
Notice how we have used an @ character at the beginning of the variable @now. I found this to be a little confusing at first, as @ normally is used as a prefix for instance variables in Ruby. But it soon becomes fairly natural and logical after some time.
Finally, we modify our template such that it incorporates the string value contained in @now:
<html> <head> <title> Hello, world! </title> </head> <body> <p>Hello, world!</p> <p>It is now <%= @now %>.</p> </body> </html>
Once again, you can retrieve the page even without restarting Ruby. You should see the date and time as kept on the server, updated each time you refresh the page.