adding Austin, Texas, and Durango, Colorado.
It's time to write some code ourselves, instead of letting Rails do all the work.
You're going to need to update your trails table to point to the right row in the
new locations table. You'll do so by adding a new database column that points
to location_id, like this:
alter table trails add location_id int(6);
You also need to tell Rails about the relationship. Modify the trails model and
the Location model to reflect the new relationships, like this:
class Trails < ActiveRecord::Base
belongs_to :location
end
class Locations < ActiveRecord::Base
has_many :trails
end
Figure 7-1. This application has less than 10 lines of code and configuration,
because Rails inferred the structure from the database
A little description here is interesting. You've created a subclass of the
ActiveRecord
class in the Base module. You've then fired a method called
belongs_to and passed it a symbol for the Locations class. This
method will
fire more metaprogramming code that actually adds the properties and methods to
your code that will manage the relationships for you.
Next, you're going to have to edit the
trails view and controller to edit a
location. The scaffolding created the new controllers and views under trails
and
locations, respectively.
It's time to modify some of the view code. The view code consists of HTML, with
Ruby scripting mixed in, between the <% and %> tags. First, you'll need to make
rows="20" wrap="virtual">
<%= @trail.description %>
</textarea> </p>
<input type="submit" value="Update" />
</form>
<a href="/trail/show/<%= @trail.id %>">
Show
</a> |
<a href="/trail/list">
Back
</a>
</body>
</html>
As with most applications, your scaffolding won't hold up infinitely. Often you'll
want to replace most of the view code. Rails lets you build toward the goal,
instead
of creating all of a model, view, and controller right off the bat.
Notice the code in bold. It adds an option value for all the locations (which you
specified in the edit method of the controller), and selects the one that matches
the one that reflects the model, shown in the variable
trails.location.city.
Finally, you'll need to show the new data in the trail list, and in the show method.
The idea is exactly the same. Add a line to the show.rhtml view right above the
links on the bottom of the page:
<p>
<b>Location:</b> <%=h @trail.location.city %>
</p>
That's pretty simple. You're just getting the location from the model passed in by
the controller. The list view uses the same technique. You can edit the table from
the app/views/trails/list view:
that you've seen what Rails can do, take a look under the hood to see some of this
magician's secrets.
7.3. Under the Hood
As you've seen, the Rails framework is also made up of several existing
frameworks, including Active Record, Action Pack, and a few others. Active
Record handles relational database access. Action Pack processes requests, and
manages the model/view/controller separation. Rails provides the integration and
the rest.
7.3.1. Active Record
Active Record implements the Active Record design pattern by Martin Fowler in
Patterns of Enterprise Application Architecture
(Addison Wesley). It's effectively a
wrapper around a database table, with domain logic built into the wrapper. The
Rails implementation adds two important innovations: you can do inheritance and
manage relationships. These are some of the major features.
7.3.1.1. Automatic properties
Active Record automatically adds properties, with accessors, to model objects. It
also adds methods for simple CRUD database methods automatically. For
example, in the view you just wrote, the view accesses the name property in
trail, though the root model was empty:
class Trail < ActiveRecord::Base
end
7.3.1.2. Association management
Rails uses methods to add methods that manage associations, automatically. You
saw this example where a location has many trails:
class Location < ActiveRecord::Base
controller, which invokes any model logic and sends the request to a template-
driven view system. The template engine fires the Ruby template, which may
execute Ruby code, and returns the resulting HTML to the browser. The flow,
shown in Figure 7-3, is reminiscent of Struts. There are a few differences. For
example, the controller has a group of actions, instead of encapsulating each action
in a different class. If you wanted to refactor, you'd let actions share methods.
Figure 7-3. Ruby on Rails is actually made up of several existing frameworks,
most notably Active Record and Action Pack
The Action Pack splits the request into a controller part and a view part. With
Rails, a whole lot happens automatically. In some ways, that's bad. You can't see
all the methods or the attributes on your class, and you don't even know what they
are unless you look at the database. In other ways, it's a highly productive way to
work. You can change your model, schema, and view in many cases just by adding
columns to the schema. Let's take a fuller look at the capabilities of Action Pack.
7.3.2.1. Capabilities
Action Pack goes beyond simple request processing. It contains many capabilities
that make it easier to develop web applications. I'll touch on some of the major
capabilities here.
As you've seen, Action Pack uses Ruby as the scripting language. Java developers
frown on embedding Java into a JSP, but I'd suggest that code will be in the view
regardless of whether it's in Ruby. Early on, some vocal zealots overreacted to the
early proliferation of Java scriptlets and decreed that MVC means "no code on the
page." Many Ruby developers believe that code that is concerned with the view
(and only the view) does belong on the page. Burying Java code in a custom tag
only complicates and confuses the issue.
Ruby provides a far friendlier scripting language than JSTL tags, for example. Like
servlets, Action Pack lets you attach filters for things like authentication. Action
Pack also handles some convenience design elements, like automatically
paginating your result sets and providing navigation links.