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
http://www.ruby-forum.com/topic/208631 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
http://www.ruby-forum.com/topic/208631 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:
http://railscasts.com/episodes/139-nested-resources

I encourage you to check out Ryan Bates' episode on nested resources:
http://railscasts.com/episodes/139-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:
http://railscasts.com/episodes/139-nested-resources

@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).