Forum: Ruby on Rails Confused about REST and custom actions

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
E884d559ed311e48ec9831b895719b2a?d=identicon&s=25 Yanni Mac (yannimac)
on 2009-05-05 15:58
I am new to the whole RESTful idea and I am trying to use it in a new
version of my app.  I might be totally missing the ball here, but this
is what I am trying to do.  I have an application that is 95% read-only
to the public users, with a back end administration available to our
staff (basically a CMS).  I want to create an API so that my users can
get an XML representation of a "widget" based on find criteria..
something like this:

http://myapp.com/widgets/123?height=1&weight=15.

This would return XML for all widgets that had a height 1 and weight 15.
The users would also be able to create widgets (using REST) and some
client apps that run on the cron would have full CRUD access to the
widget resource.

At the same time I have a bunch of custom actions that should not be
RESTful and only available to users that are viewing the app with a
browser.  I could add a bunch of logic to the "show" action, but I think
it would get pretty messy and I like having the action name in the URL
for SEO purposes.  By making the widget a resource, I can't use the
traditional "widgets/myaction" URL, because it tries to use "show"
instead of the action.  I am guessing I can fiddle with the routes and
add my custom actions, but I am wondering if this is bad design?  Should
I approach this a different way?
280b78a61a968391b7e07e912be102a8?d=identicon&s=25 Robert Walker (robert4723)
on 2009-05-05 17:18
Yanni Mac wrote:
> I am new to the whole RESTful idea and I am trying to use it in a new
> version of my app.  I might be totally missing the ball here, but this
> is what I am trying to do.  I have an application that is 95% read-only
> to the public users, with a back end administration available to our
> staff (basically a CMS).  I want to create an API so that my users can
> get an XML representation of a "widget" based on find criteria..
> something like this:
>
> http://myapp.com/widgets/123?height=1&weight=15.

Using a respond_to block on the show action of your widgets resource
using:
http://myapp.com/widgets/123.xml?height=1&weight=15

It's also possible to request the XML representation using the ACCEPTS
HTTP header.

> This would return XML for all widgets that had a height 1 and weight 15.
> The users would also be able to create widgets (using REST) and some
> client apps that run on the cron would have full CRUD access to the
> widget resource.

Trying not to fall into the trap that resources and models are
one-to-one. Resources are independent of any back-end storage model.

> At the same time I have a bunch of custom actions that should not be
> RESTful and only available to users that are viewing the app with a

Why shouldn't they be RESTful? Are you sure you understand what it means
to be RESTful?

> browser.  I could add a bunch of logic to the "show" action, but I think

Or add more resources to your application to manage the complexity in a
RESTful approach.

> it would get pretty messy and I like having the action name in the URL
> for SEO purposes.  By making the widget a resource, I can't use the
> traditional "widgets/myaction" URL, because it tries to use "show"
> instead of the action.  I am guessing I can fiddle with the routes and
> add my custom actions, but I am wondering if this is bad design?  Should
> I approach this a different way?

I'm not exactly sure how having the action in the URL is any more
friendly to SEO. I'm not SEO expert, but if that's the case it sounds
like it's SEO that's broken. That's not how the traditional "static" web
is designed.

Uniform Resource Identifiers are all about about getting, posting,
putting or deleting things. The "action" is not part of the URI. The
action is dependent on the HTTP verb (GET, POST, PUT or DELETE) used in
the request and is independent of the URI.

The RESTful approach to dynamic web applications intends to bring this
concept back. It's unfortunate that web browsers don't fully support
this concept, but Rails does an adequate job remedying their
shortcomings.

What you need to keep in mind is that resources aren't controllers and
they certainly aren't models. Yes there will likely be controllers and
models supporting your resources, but they aren't the same thing.

Check out these links for a lot more reliable explanation than I could
ever provide myself:
http://dablog.rubypal.com/2008/3/23/splitting-hair...
http://dablog.rubypal.com/2008/4/24/splitting-hair...
Dd2d775dea75b381edb1bbf0600a0907?d=identicon&s=25 Marnen Laibow-Koser (marnen)
on 2009-05-05 17:43
Robert Walker wrote:
[...]
> Trying not to fall into the trap that resources and models are
> one-to-one. Resources are independent of any back-end storage model.

That's true, but for the typical simple Rails app, they map pretty
closely.

[...]
> I'm not exactly sure how having the action in the URL is any more
> friendly to SEO.

More friendly than what?  The OP didn't mention an alternative, and
neither did you.

> I'm not SEO expert, but if that's the case it sounds
> like it's SEO that's broken. That's not how the traditional "static" web
> is designed.

The traditional "static" Web isn't designed for applications at all!
The rise of Web applications has fostered a lot of clever abuse of HTTP
and URL syntax.  REST HTTP is a particularly clever and well-thought-out
example of such abuse.

>
> Uniform Resource Identifiers are all about about getting, posting,
> putting or deleting things. The "action" is not part of the URI. The
> action is dependent on the HTTP verb (GET, POST, PUT or DELETE) used in
> the request and is independent of the URI.

True in the REST sense of "action".  Not true in the Rails sense of
"action".

Personally, I see nothing wrong in having /object/change_color right
alongside /object/create, /object/show, and all the REST. :)

>
> The RESTful approach to dynamic web applications intends to bring this
> concept back. It's unfortunate that web browsers don't fully support
> this concept, but Rails does an adequate job remedying their
> shortcomings.

Yes -- if you keep in mind that not everything can be expressed by a
"standard" REST URL.

>
> What you need to keep in mind is that resources aren't controllers and
> they certainly aren't models. Yes there will likely be controllers and
> models supporting your resources, but they aren't the same thing.

No, but the OP probably doesn't need to worry about that right away.
For learning purposes, a resource can be thought of as a model object
for simplicity's sake.

>
> Check out these links for a lot more reliable explanation than I could
> ever provide myself:
> http://dablog.rubypal.com/2008/3/23/splitting-hair...
> http://dablog.rubypal.com/2008/4/24/splitting-hair...

I'll have to look at those.  I am, however, not convinced that such
abstruse theoretical discussion will be much help to the OP, who seems
to be just trying to use RESTful design patterns in a simple app.

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
280b78a61a968391b7e07e912be102a8?d=identicon&s=25 Robert Walker (robert4723)
on 2009-05-05 17:58
Marnen Laibow-Koser wrote:
> I'll have to look at those.  I am, however, not convinced that such
> abstruse theoretical discussion will be much help to the OP, who seems
> to be just trying to use RESTful design patterns in a simple app.

From my experience starting off with an incorrect assumption about what
something is can engrain bad habits that are hard to break later. It's
best, at least for me, to approach learning something new by
understanding the theory behind it and only then applying that theory to
daily practice.
Dd2d775dea75b381edb1bbf0600a0907?d=identicon&s=25 Marnen Laibow-Koser (marnen)
on 2009-05-05 18:02
Robert Walker wrote:
[...]
> From my experience starting off with an incorrect assumption about what
> something is can engrain bad habits that are hard to break later.

Fair enough.  I do agree with you there.

> It's
> best, at least for me, to approach learning something new by
> understanding the theory behind it and only then applying that theory to
> daily practice.

To some extent.  But I think that in this case, the theory is a little
too weird to focus on before at least a little practical experience has
been had.

Best,
--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
44a43e7fef8e933e802a7802b4bd3525?d=identicon&s=25 John Small (johnsmall)
on 2009-05-05 18:24
As this thread is about REST and custom actions then I'll post a
question here.

I have a REST client, it will be working with a non-Rails application,
but for initial development and testing I'm using a separate Rails REST
server to emulate the non-Rails app.

As part of the sequence of actions, a customer will log in and pass
their id and password information to the REST server and get back a
valid account (with ActiveResource as ancestor not ActiveRecord).

Following the pattern described in
http://api.rubyonrails.org/classes/ActiveResource/... I
have a custom method 'authorize' and routes set up in client and server
as

 map.resources :accounts,:new => { :authorize => :post }

If I'm reading the example right then when I do this

Account.new(:email=>'test@test-company.com',:password=>'123test').post(:authorize)

I'm expecting to get back an account id. But I don't, I get
#<Net::HTTPOK 200 OK readbody=true>
and the new account resource doesn't have the id set up.

So what am I doing wrong?
280b78a61a968391b7e07e912be102a8?d=identicon&s=25 Robert Walker (robert4723)
on 2009-05-05 19:26
John Small wrote:
>  map.resources :accounts,:new => { :authorize => :post }

what is :new => { :authorize => :post }

Shouldn't that be something like:

map.resources :accounts, :collection => { :authorize => :post }

But, this does bring up an interesting extension to this discussion
about resources.

In your example your mapping is essentially say this:
POST: http://example.com/accounts/authorize

According to this, sending a POST to authorize should "create" a new
account. That's not, however, what you're really wanting to do. What's
really going on in terms of REST is that you are creating a new session.

Session is not necessarily an ActiveRecord model object, yet it
certainly can be a resource. Now with that in mind, the design is back
to basic CRUD actions. No custom actions are required.

When you want a new session you POST to the session resource collection.
When you want terminal a session you send a DELETE to the session
resource collection.  You can then back the session resource with it's
own controller containing the create and destroy actions.

It's also okay to have more than one URI that refers to the session
resource.

So:
POST: http://example.com/session
and
POST: http://example.com/login

Could both reference the same action of the same resource performing the
same function.

Likewise:
DELETE: http://example.com/session
and
POST: http://example.com/logout

Could also both have the same functionality of deleting an active
session effectively logging out.
44a43e7fef8e933e802a7802b4bd3525?d=identicon&s=25 John Small (johnsmall)
on 2009-05-06 08:42
> In your example your mapping is essentially say this:
> POST: http://example.com/accounts/authorize
>
> According to this, sending a POST to authorize should "create" a new
> account. That's not, however, what you're really wanting to do. What's
> really going on in terms of REST is that you are creating a new session.
>
> Session is not necessarily an ActiveRecord model object, yet it
> certainly can be a resource. Now with that in mind, the design is back
> to basic CRUD actions. No custom actions are required.
>

You're quite right it is creating a session. I was just using the
example given in the ActiveResource documentation as a starting point to
see if it did what it says it should. It doesn't.

The other thing is that the REST server isn't a Rails application in
this case. I'm using Rails to act as a front end to a legacy Windows
application.

I've worked out how to deal with the data that comes back and convert it
into an account ActiveResource, so I'll change the naming a bit to make
it clear that we're starting a session not creating a new record and
move on.

Thanks for the hint

John Small
280b78a61a968391b7e07e912be102a8?d=identicon&s=25 Robert Walker (robert4723)
on 2009-05-06 20:34
John Small wrote:
> You're quite right it is creating a session. I was just using the
> example given in the ActiveResource documentation as a starting point to
> see if it did what it says it should. It doesn't.

Similarly, I was using the example to highlight one of the most
misunderstood aspects of REST versus the Rails implementation of REST.
It's easy to think that a resources == controller + model. That's not
the case at all. The session is a good example of this fact.

> The other thing is that the REST server isn't a Rails application in
> this case. I'm using Rails to act as a front end to a legacy Windows
> application.

That one of the other unfortunate aspects of various implementations of
REST. REST is such a misunderstood technique that, more often than not,
it's implemented incorrectly.
E884d559ed311e48ec9831b895719b2a?d=identicon&s=25 Yanni Mac (yannimac)
on 2009-05-06 20:40
Thanks for the info and links guys.  I have been researching it more
this week and its starting to sink in.  I see what you mean about the
resource not being 1-to-1 with the model and it is a conceptual thing,
not the implementation.  But I think most of the time in my app, the
resource will be pretty close to the model.  Maybe you can help clarify
something for me, though.  One resource is a widget and I will be using
the regular 7 rails actions for this.  The users will be asking for a
collection of widgets, but in many different ways and criteria.  The web
app is going to provide them with shortcuts so they can just click on a
certain type of collection.  For example here are a few shortcuts (there
will probably be at least 20 of these):

Top 10 heaviest widgets
Top 10 lightest widgets
Top 10 most expensive widgets
Top 10 most popular widgets
etc....

So the resource they are looking for would be "Top 10 Most Popular
Widgets", right?  Which would be a different resource from "Top 10
Heaviest Widgets".  But I would not want to have a separate controller
for each of these, since they are all related to a widget collection.
How does this fit in with REST and the ruby implementation of REST?  Do
I just code a bunch of different conditionals in /widgets and use a
"type" parameter -- /widgets?type=heaviest and then check for each of
these in the "def list"?  It just makes more sense to me to create a
specific action for these "/widgets/heaviest".  I think I'm still
confused ;-)
280b78a61a968391b7e07e912be102a8?d=identicon&s=25 Robert Walker (robert4723)
on 2009-05-07 04:35
Okay, I'm going to take a stab at this and I hope I'm not way
off-base... Here goes...

Yanni Mac wrote:
> So the resource they are looking for would be "Top 10 Most Popular
> Widgets", right?  Which would be a different resource from "Top 10

No, the "resource" is still widgets. That's how you mapped it
(map.resources :widgets). "Top 10 Most Popular Widgets" is a
"representation" of the "widgets" resource containing the top 10 most
popular ones. The resource is separate, and independent, of the
representations of the given resource.

> Heaviest Widgets".  But I would not want to have a separate controller
> for each of these, since they are all related to a widget collection.
> How does this fit in with REST and the ruby implementation of REST?  Do
> I just code a bunch of different conditionals in /widgets and use a
> "type" parameter -- /widgets?type=heaviest and then check for each of
> these in the "def list"?  It just makes more sense to me to create a
> specific action for these "/widgets/heaviest".  I think I'm still
> confused ;-)

I don't believe the REST theory is going to define the implementation of
how to get from the resource mapping to the various representations of
the resource.

Consider the following URIs:
http://example.com/widgets?type=popular&top=10
or
http://example.com/popular_widgets?top=10
or
http://example.com/widgets/popular

These could all represent the exact same representation of the widgets
resource. What changes is the implementation. REST defines the API not
the implementation. It's up to you to weigh the pros and cons of each
method and decide what's best for your application.
E884d559ed311e48ec9831b895719b2a?d=identicon&s=25 Yanni Mac (yannimac)
on 2009-05-08 20:55
Robert,

This makes more sense now.  Thanks for the explanation!

If anyone is interested, I decided to do it this way (from Agile Web
Development with Rails book) :

map.resources :widgets, :collection => { :popular => :get, :heaviest=>
:get }

With this I can still add the custom method to my widgets controller.
This approach makes the most sense to me considering how my application
is organized.
This topic is locked and can not be replied to.