#35
May 23, 2007

Custom REST Actions

REST adds many constraints. It restricts your controllers to seven actions. Normally this is okay, but sometimes you need to add your own custom actions. Learn how in this episode.
Download (20.8 MB, 8:44)
alternative download for iPod & Apple TV (12.1 MB, 8:44)
# routes.rb
map.resources :tasks, :collection => { :completed => :get }, :member => { :complete => :put }
<!-- tasks/index.rhtml -->
<%= link_to "Mark as complete", complete_task_path(task), :method => :put %>
...
<%= link_to "Completed Tasks", completed_tasks_path %>

RSS Feed for Episode Comments 48 comments

1. weskycn May 23, 2007 at 00:16

最近学习REST,感谢老大提供这么好的指导,
i'm a chinese guy,
i love your screencasts,
go on


2. Ralph May 23, 2007 at 00:48

I've always wondered if I can use custom identifiers for members. For example, I have an articles controller. The show action needs the id of the article, so I have urls like /articles/1. But I want urls like /articles/some-article-title. Is this possible somehow?


3. vigosan May 23, 2007 at 03:41

For friendy URLs you can use this plugin http://sargon.interinter.net/acts_as_sluggable/


4. Neeraj Kumar May 23, 2007 at 05:04

Rock on!!!


5. Mereghost May 23, 2007 at 05:13

Ralph:

AFAIK you could implement that as a custom action (find_by_post_title) mapped more or less like that:

map.connect '/articles/:title', :controller => 'articles', :action => 'find_by_post_title'

I'm not sure about the RESTful way, so a big guess would be:

map.resources :articles, collection => {:find_by_post_title => :get}
--
BTW great screencasts ^^


6. Manuel May 23, 2007 at 05:36

Thanks for this great screencast! However I'm still not convinced about REST. It's to limiting without bringing too much benefit I think. Maybe in the future...


7. Chris May 23, 2007 at 05:59

Thank you for this great screencast.


8. Zubin May 23, 2007 at 06:06

@Ralph, @Merehost:

If you want URLs like "/articles/some-article-title" you can add something like this to the Article model (article.rb):

def to_param
  "#{id}-#{name.downcase.gsub(/[^a-z]+/, "-")}"
end

and link to it with article_path(@article)
# do not use @article.id as this will bypass to_params


9. David Parker May 23, 2007 at 06:24

@Ralph,
Zubin is correct. Also, if you want to learn more, you should buy Peepcode's RESTful tutorial. It's $9, 85 minutes long, and comes with all the source code. It's an excellent resource for REST!


10. Ryan Bates May 23, 2007 at 07:27

@Manuel, I hear you. I wasn't convinced of REST initially either. I don't think it's possible to convince someone, it's just something you need to try for yourself. Then compare the code before/after - that's what convinced me.

Ironically as it may sound, the biggest benefit of REST is the limitations it brings. It's what keeps your code clean and design normalized.


11. Sintaxi May 23, 2007 at 10:37

Thats great practical information on REST. I think it should be noted that you dont need to create a new model if you want to make those actions RESTful.

If you find yourself creating CRUD like actions to manage your "completed" column. Create only a new (RESTful) controller. This has often worked well for me.


12. Trevor Turk May 23, 2007 at 10:49

Something I've noticed with REST is that having a shared find_by_params method saves a bit of repetition. So, you could have:

before_filter :find_task :except => [:index, :new, :create]

def find_task
  @task = Task.find(params[:id])
end


13. Ryan Bates May 23, 2007 at 11:41

@Sintaxi, so right! This is something I often forget.


14. Todd Breiholz May 23, 2007 at 14:17

Another great screencast. I had one question, though. Is the determination of the plurality of the link generation method (i.e. action_task_path vs action_tasks_path) made by whether you've added it to the collection hash vs. the member hash in the routes.rb file?


15. Ryan Bates May 23, 2007 at 15:45

@Todd, yep, exactly. "collection" makes a plural named route (action_tasks_path), "member" makes a singular named route (action_task_path).


16. Bastiaan Peters May 24, 2007 at 16:04

Hi Ryan, these screencasts are great. Thanx a lot for all the effort.


17. Zubin May 26, 2007 at 22:35

I'm a little confused about collections in named routes. Can you pass a parameter to define the collection?

eg /products/tagged/interesting

from map.resources.products, :collection => {:tagged => :get}

but where would :id (tag) go? Or is this not possible using REST?


18. Ryan Bates May 26, 2007 at 23:52

@Zubin, a "collection" route doesn't accept an :id parameter. It is only for predefined collections. You can still pass the name/id of a tag and search based off of that, but it will show up at the end of the url:

/products;tagged?tag=interesting

If you don't want this behavior you'll have to create a custom route outside of REST - or display the products from inside the tags show page.


19. Zubin May 27, 2007 at 01:01

Thanks Ryan, I presume you mean it has to be a :member in order to pass the :id?

Any reason not to fall back on standard routes? So the path would look like this: /products/tagged/interesting (calls "tagged" method in listings_controller).


20. Michael May 27, 2007 at 11:19

This was a great railscast. There is one thing, however, that I'd recommend with screencasts of this nature. Expand on the existing theme. For instance give us information on what we need to do if we have more than one action to add of a certain type:

map. resources :items, :collection => {:recent => :get}

. . .how do I add more than one? Do I need another "collection" declaration or can I put each one inside the same set of brackets?

As these screencasts are already amazing, expounding in this manner would be priceless.

Thanks!


21. Ryan Bates May 27, 2007 at 13:25

@Zubin, right, it has to be a :member to pass "id". The reason being is "id" should represent one task (or whatever the resource is). It should be unique to that task.

You could fall back on the standard route, but this is going away from REST.

@Michael, you would supply it in the same :collection hash, no need to specify it twice.

:collection => { :recent => :get, :completed => :get }


22. Ben May 28, 2007 at 15:04

Ok, so how would you go about creating a RESTful controller while not creating the model.. I'm brand new to REST, and the only way I know of generating a resource is scaffold_resource? Any tips?


23. jeroen May 29, 2007 at 01:10

Great espisode!

I'm pretty sure it's better to add a specialzed controller for this "CompletedTasksController" which has the default REST actions. DHH also mentiod something simlar in his World of Resources presentation.


24. Ryan Bates May 29, 2007 at 08:03

@Ben and jeroen, right, I should have mentioned this in the episode. I may revisit this in a future episode because I think it's a pretty important point.


25. diego Jun 03, 2007 at 02:52

you rock ryan!

thank you for your screencast man :)


26. Fred Jun 04, 2007 at 07:42

Hey Ryan. Great screen casts! I've watched them all, and have recommended them to all my Rails developers.

Anyway, I thought it would be a great series of screen casts to talk through using RESTful designs for those non-obvious resources, like you mentioned in this screen cast. Such as, the "completed" resource. Maybe, a simple one would be to use Users and Sessions as an example of RESTful User login.


27. Phillip Jun 04, 2007 at 13:13

Hi Ryan,

I, too, enjoy your screencasts. Two questions, one about this episode specifically:

1) is it possible to get the named routing of

map.resources projects

without getting the REST (ha, no pun intended). I'm very new to REST and will have to give some thought to how to handle some actions that I have (such as forgot_password and things like that)

2) what's the chances of the iTunes podcast getting the iPod compatible m4v files? I subscribed to the podcast and was merrily watching on my notebook, but then disappointed when the videos wouldn't go the iPod. I just downloaded all the m4v version manually and imported them into iTunes. I'd like to get the new episodes automatically if I could, which is why I subscribed to the podcast.

Thanks for contributing to the Rails community!

Phillip


28. Phillip Jun 04, 2007 at 13:40

Nevermind on #2. I just revisited iTunes and see the other podcast.

Thanks!
Phillip


29. Ryan Bates Jun 04, 2007 at 13:50

@Phillip, the named routes generated by map.resources is where the RESTful design is specified as well, so there's no way to separate it. You'll have to create your own named routes if you don't want to use REST. You may want to look into abstracting this into a custom route method (like map.resources) so it's easy to generate the named routes. I'm not sure on the details though.


30. macovsky Jun 26, 2007 at 10:56

Ryan, thank you very much for sharing wisdom clouds :). I can't stop watching it :).

Btw you mentioned that you'd revisit creating a RESTful controller instead of creating the model?

Did you do that?:) I can't find it... the next episode is about SVN.

Peace.


31. Ryan Bates Jun 26, 2007 at 14:02

@macovsky, I haven't done it yet. I made one attempt but wasn't satisfied so I didn't publish it. Still planning to talk more about this in the future.


32. macovsky Jun 27, 2007 at 01:30

Ok, Ryan, will be waiting patiently :).


33. DAZ Jul 19, 2007 at 06:42

Hi Ryan,

Thanks for these screencasts - they are brilliant. Everybody seems to be talking about REST, but this is one of the only resources I can find that introduces RESTful Rails. More on the topic would be great.

I have one question though - how come you need to specify the fact that the complete action was a :put in the routes.rb file and then again in the link_to method? I would have thought that once it has been set up in the routes.rb file that should be enough?

thanks again,

DAZ


34. Ryan Bates Jul 19, 2007 at 07:28

The named route mentioned in the link_to just returns the URL to the action, it doesn't include the HTTP method used to access it. I suppose it would be nice if you could embed the HTTP verb inside of the URL, but that's not really possible.


35. cbmeeks Jul 21, 2007 at 11:25

Your RailsCasts ROCK!

Keep up the great work

http://eblarg.com


36. Josh Aug 14, 2007 at 04:38

Hi, Ryan.
I'm new to Rails, and I'm glad I found your site!
I've heard about the PeepCode podcast about RESTful development, but I was wondering if you could point me to any other resources about it.
Thanks!


37. David Allen Aug 21, 2007 at 08:18

Hey, great site, thanks so much. This episode got me over the hump. One idea for a podcast might be just a simple search function by something other than the default integer ID. Most legacy systems do not have that kind of ID.

Good luck


38. 蒋丛德 Nov 09, 2007 at 15:51

你的英语讲的很好,内容也不错,我们很喜欢!
但是,有点小遗憾,就是有些东西讲的太少了.
我看过layout那部分,partial那部分没有涉及到!


39. Marcek Nov 14, 2007 at 06:24

Hi! It,s possible to send two or more parameters in RESTful action? For example
some_action_path(@news, @comment) ??
Btw. Great screencast!


40. Suraj Nov 16, 2007 at 09:26

I have an Items model and a Categories model. Item belongs_to Category and Category has_many Items.

I would like to filter Items by Category. As I understand it, it would have to be something like '...items/filter/[cat_id]'.

modifying the routes file like this:

map.resources :items, :member => {:filter => :get}

Here is my first question, I am filtering Items by category_id, I feel I need to use :collection but then I would not be able to use an id. Am I doing something I am not supposed to?

Also, instead of getting the reoute I want when using filter_items_path I am getting: .../items/[category_id]/filter

Appreciate if you could help.


41. JN2 Nov 21, 2007 at 11:02

Hi Ryan, I searched for hours on how to do custom actions (outside the standard 7) but with no success. Until I found your podcast - thank you!


42. noocx Dec 17, 2007 at 18:34

Thanks for the screencasts!


43. Di Marcello Jan 18, 2008 at 01:02

I have a question about your archive path.
why and how do you use the semicolon between episodes and archive in the path?
rc.com/episodes*;*archive


44. elise May 09, 2008 at 03:29

thank you for the screencasts - i'm working my way through them and they teach me a lot (also between the lines, like coding style).
Great stuff !


45. Bijesh May 12, 2008 at 06:28

Hi,

thanks, this is really a great screencast.
I can use the :collection and :member but still dont know the exact difference between them ? Could anybody tell me ?

Thanks !


46. kino May 23, 2008 at 01:56

On the other hand, our a priori concepts are what first give rise to, in view of these considerations, the phenomena.


47. Andrés Jun 01, 2008 at 01:29

Hello Ryan,

This screencast is very good.

Thank you.


48. Rajeev Ramani Jun 25, 2008 at 01:58

I think this is one of the most wonderful sites. Thanks Ryan. Your efforts are brilliant and appreciated greatly.

Thanks

Add your comment:

(SKIP THIS ONE)

(required)

(not shown)


(use pastie or gist for code)

sponsored by:
if you want to help:
required:
Get Quicktime Player