RESTful style: multiple plot styles, query strings

In my quest to practice good style, I'm a bit stumped on the following.

Assume an app that lets a user visualize recent web activity on various domains ("user has many domains"). In ordinary RESTful style, the path /user/:user_id/domain invokes 'index' and lists all the domains, the path /user/:user_id/domain/:domain_id shows particulars about one domain.

Our app needs a 'display graph' button on the 'show' page to plot historical activity for a particular domain. The plot page has lots of options (set time scale, filter different kinds of packets, etc), and I can imagine these being tacked onto a path as query strings.

I'm a bit stumped as to how this really fits into RESTful style. Would you create a Plot controller ("domain has one plot") and control it with a path like /user/:user_id/domain/:domain_id/plot?plotoptions... ?

Also, the app calls for radically different page layouts depending on selected display style (e.g. plot vs table view) -- can one controller invoke multiple views? Or must you create one controller per view?

And finally, would you back the controller with a Plot model? Except for caching complex plots, I don't see a real motivation for db persistence, unless, of course, the Rails framework really wants a Plot model to go with a Plot controller.

I welcome your insights...

- ff

I love REST, but I dont practice it like religion. Developing an application using nothing but seven controller actions will quickly defeat the purpose of a clean API.

The question here is: What do you do with operations that doesn't neatly fit into basic CRUD? - Put it into one of the seven default REST actions anyway, and use a bunch of if-tests to make one action handle two types of requests? - Define a new RESTful action on the existing resource using the collection/member parameter in the resource mapping? - Define a new resource? - Make an unRESTful action, either in a new or existing controller?

All 4 options has it's uses. Which one works best depends on the actual case.

A couple of principles that helps me decide: I want one controller action to only do one thing, or more specific: have one concern. I dont define a new resource, or add a custom RESTful action unless the resulting API makes logical sense.

Reports are a good example of something that often doesn't fit well into a RESTful style. Finding good short names for the actions become hard, and hence the resulting routes becomes cumbersome. What I usually do is to have an unRESTful reports controller.

Graphs are a kind of report so it could be best for you to make these actions unrestful, but it's not really possible for me to say without knowing what the actual code looks like. Think about each of the options you have and try to figure out which solution would be cleanest. REST is all about a clean API, but the code behind it should also be maintainable. That's why using nothing but seven controller actions rarely works well in practice.

Also, the app calls for radically different page layouts depending on selected display style (e.g. plot vs table view) -- can one controller invoke multiple views? Or must you create one controller per view?

You can use "render :template => " to control which template a controller action renders. This allows you to use if-tests if you need one action to render different views depending on something. Another option is to have one view that renders different partials depending on something.

And finally, would you back the controller with a Plot model? Except for caching complex plots, I don't see a real motivation for db persistence, unless, of course, the Rails framework really wants a Plot model to go with a Plot controller.

Whether or not you need a new model has nothing to do with REST. If you are going to work with an object that doesn't have a model already, then a new model would help you put the code in the right place. A model does not need to be DB-backed.

Fearless Fool wrote:

In my quest to practice good style, I'm a bit stumped on the following.

Assume an app that lets a user visualize recent web activity on various domains ("user has many domains"). In ordinary RESTful style, the path /user/:user_id/domain invokes 'index' and lists all the domains, the path /user/:user_id/domain/:domain_id shows particulars about one domain.

Our app needs a 'display graph' button on the 'show' page to plot historical activity for a particular domain. The plot page has lots of options (set time scale, filter different kinds of packets, etc), and I can imagine these being tacked onto a path as query strings.

I'm a bit stumped as to how this really fits into RESTful style. Would you create a Plot controller ("domain has one plot") and control it with a path like /user/:user_id/domain/:domain_id/plot?plotoptions... ?

No. The basic 7 REST actions are common, not gospel. There's nothing wrong with making plot a custom REST action (route would be the same as above, but on the same footing with domain/:domain_id/edit ).

And please: use shallow routes. There's probably no reason to have the user_id in the domain path.

Also, the app calls for radically different page layouts depending on selected display style (e.g. plot vs table view) -- can one controller invoke multiple views? Or must you create one controller per view?

What do you mean here?

And finally, would you back the controller with a Plot model? Except for caching complex plots, I don't see a real motivation for db persistence, unless, of course, the Rails framework really wants a Plot model to go with a Plot controller.

You don't have to have a model for each controller. You don't have to have a controller for each model. You don't have to have a database table for each model.

I welcome your insights...

- ff

Best,

Marnen Laibow-Koser wrote:

...

No. The basic 7 REST actions are common, not gospel. There's nothing wrong with making plot a custom REST action (route would be the same as above, but on the same footing with domain/:domain_id/edit ).

If I understand correctly, when you say "on the same footing with domain/:domain_id/edit", you are recommending adding new actions to DomainController beyond the RESTful seven, so we might have domain/:domain_id/plot or domain/:domain_id/report -- is this what you meant?

And please: use shallow routes. There's probably no reason to have the user_id in the domain path.

In this (slightly contrived) example, specific domains belong to individual users, so the user/:user_id/domain/:domain_id seems appropriate. (If you disagree, take a look at To nest or not to nest: RESTful style - Rails - Ruby-Forum and let me know if I've been mis-informed... :slight_smile:

Also, the app calls for radically different page layouts depending on selected display style (e.g. plot vs table view) -- can one controller invoke multiple views? Or must you create one controller per view?

What do you mean here?

The user can choose between viewing the data in tabular form or as a plot, but the .erb code that generates those are radically different. I think @Sharagoz answered my question here: the controller can select a "table" template or a "plot" template accordingly. (That was news to me.)

And finally, would you back the controller with a Plot model? ...

You don't have to have a model for each controller. You don't have to have a controller for each model. You don't have to have a database table for each model.

Noted. @Sharagoz pointed that out as well.

Thanks both!

- ff

Fearless Fool wrote:

Marnen Laibow-Koser wrote:

...

No. The basic 7 REST actions are common, not gospel. There's nothing wrong with making plot a custom REST action (route would be the same as above, but on the same footing with domain/:domain_id/edit ).

If I understand correctly, when you say "on the same footing with domain/:domain_id/edit", you are recommending adding new actions to DomainController beyond the RESTful seven,

Beyond the *common* RESTful 7. Again, those actions are common, but not exhaustive.

so we might have domain/:domain_id/plot or domain/:domain_id/report -- is this what you meant?

Yes.

And please: use shallow routes. There's probably no reason to have the user_id in the domain path.

In this (slightly contrived) example, specific domains belong to individual users, so the user/:user_id/domain/:domain_id seems appropriate. (If you disagree, take a look at To nest or not to nest: RESTful style - Rails - Ruby-Forum and let me know if I've been mis-informed... :slight_smile:

Domains belong to users, but since the domain already knows whom it belongs to, there's usually no reason to put that information in the path and make longer URLs.

Also, the app calls for radically different page layouts depending on selected display style (e.g. plot vs table view) -- can one controller invoke multiple views? Or must you create one controller per view?

What do you mean here?

The user can choose between viewing the data in tabular form or as a plot, but the .erb code that generates those are radically different.

(Aside: get into the habit of using Haml, not ERb.)

I think @Sharagoz answered my question here: the controller can select a "table" template or a "plot" template accordingly. (That was news to me.)

It can, but usually if you want different view files, you probably have things that should be different controller actions.

And finally, would you back the controller with a Plot model? ...

You don't have to have a model for each controller. You don't have to have a controller for each model. You don't have to have a database table for each model.

Noted. @Sharagoz pointed that out as well.

Thanks both!

- ff

Best,

Marnen Laibow-Koser wrote:

so we might have domain/:domain_id/plot or domain/:domain_id/report -- is this what you meant?

Yes ... Domains belong to users, but since the domain already knows whom it belongs to, there's usually no reason to put that information in the path and make longer URLs.

Hey Marnen:

I'm game, so I've been trying what you recommended. I've stripped out plots and reports just to understand the basics, so my route.rb looks like:

ActionController::Routing::Routes.draw do |map|   map.resources :users   map.resources :locations end

I can create, view, edit, delete Users without trouble. But I'm conceptually stuck on how to create Locations. Since Users has_many Locations, I need to stuff the user_id into a Location when I create it. My location_controller code for create() is something like:

  def create     Rails.logger.debug("#{self}.create(): params = #{params}")     @user = User.find(params[:user_id]) # THIS LINE FAILS: no :user_id present     @location = @user.locations.build(params[:location])     if @location.save       flash[:notice] = 'Location was successfully created.'       format.html { redirect_to(@location) }     else       format.html { render :action => "new" }     end   end

This code fails because no params[:user_id] is define when coming in via POST, and I don't see where to pick up the :user_id. Inasmuch as you're advocating flat routes, you must know some trick that I'm overlooking.

(Pardon if this is garbled -- it's late and I've been wrestling with this for a while.)

What am I missing?

Best,

- ff

I encourage you to check out Ryan Bates' episode on nested resources:

I encourage you to check out Ryan Bates' episode on nested resources:

Sharagoz wrote:

I can create, view, edit, delete Users without trouble. But I'm conceptually stuck on how to create Locations. Since Users has_many Locations, I need to stuff the user_id into a Location when I create it.

I encourage you to check out Ryan Bates' episode on nested resources: #139 Nested Resources - RailsCasts

@Sharagoz:

I don't generally like webcasts (I personally find reading an easier way to absorb info), that was very helpful and got me 85% over the hump. The last 15% was courtesy of Adam Wiggins in:

http://adamblog.heroku.com/past/2007/12/20/nested_resources_in_rails_2/

Without that bit, I would not have known that form_for requires ([parent_id, child_id]) ... as its first arg. Now I know.

And (best of all) it's all working. Thanks!

- ff

Minor correction: I should have said that the proper syntax for nested routes in a form_for is

   <% form_for [@parent, @child] do |f| %> ...

(I just don't someone else to get tripped up by an incorrect example).