Forums Ruby

RoR Technical Questions

Subscribe to RoR Technical Questions 7 post(s), 3 voice(s)

 
Avatar Andrew Shind... 27 post(s)

I’m going through the “Agile Web Development with Rails” book, and just got to the link_to method. This is what I am talking about:

<%= link_to "actual link", :action => "link-page" %>

My question is this: is :action the equivalent of this in javascript?

 
Avatar Scott Woods Administrator 41 post(s)

In rails, all URLs are eventually translated into a single Controller and a single Action, which is actually just the public method that will be called on the controller. This is the purpose of “routing”—it translates URLs to controllers and actions, and vice-versa.

So let’s say I have a URL like this:

/person/show/12

Using the default rails-1.x style routes, this will call the “show” method on the PersonController, with ” :id => 12” in the params hash.

Likewise, if I call link_to like this:

link_to "Show this person", :controller => "person", :action => "show", :id => 12

It will generate a link that points to ”/person/show/12”.

This is because the default routing (translation of URLs to controllers/actions/params and vice versa) is defined as ”/:controller/:action/:id” (you can verify this in config/routes.rb). This means the first chunk of the URL maps to the controller, the second chunk maps to the action, and the third chunk maps to the :id in the params hash. If for some reason the default routing had been defined as ”/:action/:controller/:id”, then the same call to link_to would have produced a link that points to ”/show/person/12”, with the same end result—it would call the “show” action on the “person” controller, with ” :id => 12” in the params hash.

If you don’t specify the controller (like in your example), it will presume that the controller is the same one as the view that the “link_to” appeared in. So if I was already in one of the views for the person controller (like app/views/person/list.rhtml), I could just write:

link_to "Show this person", :action => "show", :id => 12

and it would still call “show” on the person controller.

So let’s say your example also takes place in one of the views for a hypothetical PersonController. Your example would then create a link that points to the URL ”/person/link-page”, which would call the “link-page” method on the PersonController, and render app/views/person/link-page.rhtml.

The only problem is, I don’t know if you can have dashes in your actions, since you can’t have a dash in a ruby method name (it looks like subtraction to the ruby interpreter), so you couldn’t actually have a “link-page” method in the PersonController. The safe thing to do is to use underscores instead, which are fine when naming Ruby methods.

So long story short, :action determines which method/action will get called on the controller when someone clicks that link.

Let me know if that doesn’t make sense…

 
Avatar Andrew Shind... 27 post(s)

Question about controllers: should every model have a corresponding controller? Or should every view have a controller? What are the general guidelines for creating controllers?

 
Avatar Anthony Stau... 3 post(s)

A model needs a controller if it’s going to have any interface to the web. For example, if I have a model called Employee.rb that maps to a database table named employees, If I want to be able to have a page to edit an employee’s details I need to have a controller that exposes the “edit” action. Like Scott said, Rails figures out what to do by parsing the url into “controller”/”action”/”id”. So if I go to www.examplerailsapp.com/employees/edit/12 , Rails will look for a controller called employee_controller, then try to find an action named “edit”, and perform that action passing in 12 as the id number of the employee to edit.

If I had no controller for the model, I could only reference that model from other controllers in order to manipulate instances of that model. For example I may have anoter model named Store, and a corresponding controller called store_controller. I could implement an action inside of store_controller called “edit_employee”. In order to edit the details for an employee I would use a URL like www.examplerailsapp.com/store/edit_employee/12 . Rails would then find this action inside the store_controller and that code could load the Employee model out of the database using the supplied id number, present a form to the client, and update the employee when the form is submitted.

However, I belive it’s generally bad form to present management of one type of object through another object’s controller. Scott can correct me on this if I’m wrong, but doing this starts to make it hard to reliably know where an object is managed from.

 
Avatar Scott Woods Administrator 41 post(s)

Hey Andrew,

Every view does need a controller. You can put static HTML pages in /public, but if it has ruby code in it, it will need to go in /app/views/#{controller_name}/. Note the path there—the controller/view relationship is a strict hierarchy in RoR, where each view belongs to one and only one controller. This is used to represent the default one-to-one relationship between controller actions and their views. Each controller action (public method of the controller) will automatically render its corresponding view in /app/views/#{controller_name}/#{action_name}.rhtml (or .html.erb if you’re on edge rails). You can and should think of each view as being a template for its corresponding action.

However, things get more nebulous (creative?) with models and controllers. Whereas the relationship between controllers and views is fairly strictly defined, the relationship between models and controllers is wide open. Any controller can and should call upon any model. How you organize your controllers in your application is completely up to you.

There are a couple guiding principles that help out a lot though. First of all, you don’t want your controllers to have a million actions. After my controllers start getting more than 10 actions, alarm bells start going off. Granted, each situation is different, and it’s ridiculous for me to mention a firm figure for the number of actions you should have in a controller, but I wanted to give you some idea of the range I’m usually thinking of.

Second of all, it’s considered good form to keep the controller actions themselves as short and succinct as possible, and push the guiding application logic down to the models, and any rendering logic up in to view helpers (also using view partials to reduce repetition). Here’s the original article on “Skinny Controller, Fat Model”, followed by an even more succinct writeup on the same topic on the Rails Way (same guy, Jamis Buck):

http://weblog.jamisbuck.org/2006/10/18/skinny-c…
http://www.therailsway.com/2007/6/1/railsconf-r…

So how do you decide what controllers to have in the first place? What do you call them?

If you’re designing an application using REST principles, then each controller represents a “resource” that people would want to access. For example, if you’re designing a bookstore, then you might very well have a /books controller (REST controllers are usually plural). People could list the books using a GET request to /books, look at a particular book using GET /books/23, and the admin interface would remove a book by issuing a DELETE request to /books/46. The actions that are available are generally limited to the standard create, show, index, update, delete, edit, and new, which is a feature of REST. Once you know the resource name and what it represents, you pretty much know how to use it.

Note that there’s no rule that your resource (controller) matches a model! For example, Amazon might have a /products controller, which would encompass their Book model, their HomeAndGarden model, and their Clothing model. The key is that they decided that it made sense to expose “products” as a single resource, so that’s how they designed their (hypothetical) REST controller. That controller’s actions would then be dealing with all the different models.

That said, in practice I find that many simple REST applications do wind up having a one-to-one relationship between models and controllers.

If you’re not doing RESTful controllers, then you have more flexibility. You can stick with the default scaffolding actions, or you can have entirely different ones. You can lay out your controllers however you’d like. You can have actions like /admin_interface/run_nightly_reports . At this point, how you lay out your controllers is largely a matter of taste and experimentation. On the one hand, you don’t want too many controllers, because then it gets cumbersome to reference them all in your links, because you’re always having to specify the controller (you don’t have to specify the controller in link_to params if the linked-to action is in the same controller as the current view). On the other hand, controllers with 50 actions can look and feel a bit ridiculous.

The tradeoffs in non-RESTful controller/action design are similar to how you’d design your module/function layout in a procedural program. A lot of times, the final guiding principle for me is how the URLs look and behave. Generally, when I’ve hit upon the right combination, the URLs make sense and flow between one another logically. Sometimes I’ll even start the whole process by designing all the URLs and their interplay first, and then figuring out how to implement them (I always do it this way for REST controllers).

Finally, you don’t have to settle on one controller design for your application. Quite frequently, we’ll have to settle for one non-RESTful controller in an otherwise RESTful application. Sometimes you run into views/actions that just don’t want to be shoehorned into CRUD. Hopefully the non-RESTful part is not a core part of the application though—it’s nice to have a consistent style of interface for the core functions.

Hope this helps, looking forward to others’ feedback.

Scott

 
Avatar Andrew Shind... 27 post(s)

wow… thanks for the help, guys! This confirms how I might want to proceed: finish making my interface mockups, think of good urls for them, then think about what controllers are needed.

Edit: I already have (most) of my models figured out.

 
Avatar Scott Woods Administrator 41 post(s)

Sounds like a great plan. I’ve really enjoyed that workflow before too, but just wound up there through a combination of luck and procrastinating on the hard parts: figure out what the user interface should look like, so you know what you’re building, for whom, and why. Then figure out the models for how you’re going to store the information and do the application logic. Finally, write the controllers to glue everything together (usually the most nebulous part), using your ideal URLs as a guide. Maybe do several iterations like that for each distinct chunk of functionality in the site. Neat. Maybe I’ll intentionally try for that workflow next time!

Forums Ruby