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? -- Posted via http://www.ruby-forum.com/.
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 aWhy 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 thinkOr 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-hairs-over-resource http://dablog.rubypal.com/2008/4/24/splitting-hairs-over-resource-part-2 -- Posted via http://www.ruby-forum.com/.
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-hairs-over-resource > http://dablog.rubypal.com/2008/4/24/splitting-hairs-over-resource-part-2I''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-sbuyVjPbboAdnm+yROfE0A@public.gmane.org -- Posted via http://www.ruby-forum.com/.
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. -- Posted via http://www.ruby-forum.com/.
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-sbuyVjPbboAdnm+yROfE0A@public.gmane.org -- Posted via http://www.ruby-forum.com/.
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/CustomMethods.html 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-w8wYwnME98NEdC79VfMSPA@public.gmane.org'',: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? -- Posted via http://www.ruby-forum.com/.
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. -- Posted via http://www.ruby-forum.com/.
> 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 -- Posted via http://www.ruby-forum.com/.
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. -- Posted via http://www.ruby-forum.com/.
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 ;-) -- Posted via http://www.ruby-forum.com/.
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 10No, 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. -- Posted via http://www.ruby-forum.com/.
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. -- Posted via http://www.ruby-forum.com/.