#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 56 comments

1. 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?


2. vigosan May 23, 2007 at 03:41

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


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

Rock on!!!


4. 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 ^^


5. 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...


6. Chris May 23, 2007 at 05:59

Thank you for this great screencast.


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


8. 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!


9. 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.


10. 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.


11. 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


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

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


13. 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?


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


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

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


16. 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?


17. 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.


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


19. 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!


20. 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 }


21. 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?


22. 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.


23. 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.


24. diego Jun 03, 2007 at 02:52

you rock ryan!

thank you for your screencast man :)


25. 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.


26. 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


27. Phillip Jun 04, 2007 at 13:40

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

Thanks!
Phillip


28. 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.


29. 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.


30. 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.


31. macovsky Jun 27, 2007 at 01:30

Ok, Ryan, will be waiting patiently :).


32. 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


33. 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.


34. cbmeeks Jul 21, 2007 at 11:25

Your RailsCasts ROCK!

Keep up the great work

http://eblarg.com


35. 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!


36. 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


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

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


38. 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!


39. 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.


40. 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!


41. noocx Dec 17, 2007 at 18:34

Thanks for the screencasts!


42. 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


43. 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 !


44. 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 !


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

Hello Ryan,

This screencast is very good.

Thank you.


46. 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


47. Leks Nov 23, 2008 at 02:13

Thanks a lot for nice screencast!


48. Ravi Feb 26, 2009 at 18:17

awesome


49. Alex May 06, 2009 at 03:48

Clearly explains the usage of custom controller action along with :collection & :member routing resources.

Thank you Ryan


50. Martijn Jun 23, 2009 at 06:11

This railscast was still useful to me today, over 2 years later. Thanks :)


51. jack Aug 30, 2009 at 08:35

I was using a custom action instead of index.

To make Rails execute the code in custom_action.js.erb (as opposed to index.js.erb), I had to use a respond_to block with format.js, like this:

respond_to do |format|
      format.html
      format.js
end

I'm not sure why Rails doesn't respond to js requests by default?


52. jack Aug 30, 2009 at 08:39

Ughj, sorry I commented on the wrong screencast! I meant for comment #68 to be for Ajax Pagination which is screencast 174. Too many windows open.


53. UGG Gypsy Soft leather Sandals Nov 30, 2009 at 19:33

I was using a custom action instead of index.

To make Rails execute the code in custom_action.js.erb (as opposed to index.js.erb), I had to use a respond_to block with format.js, like this:

respond_to do |format|
      format.html
      format.js
end

I'm not sure why Rails doesn't respond to js requests by default?


54. Carmelo Anthony Shoes Dec 04, 2009 at 00:31

realized that you actually called the library, and my mind became unblown


55. Borland Jan 27, 2010 at 05:07

Hello, many thanks for great screen cast!

Maybe someone could help with my problem. I have model called wiw_user and action detach mapped with "map.resource :wiw_users, :member => {:detach => :put}", but the link "link_to 'Detach', detach_wiw_users_path(wiw_user)" does not work, reports that I have a nil when I didn't expect it! And that's true, however wiw_user is not nil and without passing the parameter, the URL helper generates correct URL.


56. shoe carnival Apr 05, 2010 at 18:42

 So Dorothy said good-bye to all her friends except Toto, and
taking the dog in her arms followed the green girl through seven
passages and up three flights of stairs until they came to a room
at the front of the Palace.


57. Mohan Gote Apr 20, 2010 at 18:37

Ryan, this screencast is awesome like any other screencast. I am a java/flex programmer, want to do rails. Learning a lot of tips/tricks from these posts. I wait for your posts every week. Thanks a lot again.


58. Buy Christian Louboutin Jun 03, 2010 at 02:43

I love it thank you。I really appreciate what you post.


59. barefoot shoes Jun 03, 2010 at 23:38

I am the first time on this site and am really enthusiastic about and so many good articles. I think it’s just very good.
Always yours


60. Christian Louboutin Sandals Jun 14, 2010 at 03:02

I really appreciate what you post.


61. Reconcile an Invoice Jul 02, 2010 at 01:56

That’s good explanation, thanks for the lesson very much.<a href="http://www.legalwebreview.com/accounting/reconcile-invoice-2/">Reconcile an Invoice </a>


62. jerseys Jul 22, 2010 at 20:04

Great article, I think you covered everything there. . . I would say freelancing is quite hard especially if you are not used to working on

your own, can be quite hard to motivate yourself also. . . we all know what it is like to stare at the monitor.


63. asics shose Jul 28, 2010 at 00:11

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.


67. fadewatches Aug 06, 2010 at 19:13

I am a fanatic watch collection, especially the well-known watches, you also can do, just click on my name!!!!!!!!


68. burberry polo shirt Aug 12, 2010 at 01:12

it is so useful to me.
thanks for your share.i will see next time,looking for your next article.
maybe you can see<a href="http://www.poloshirtsb2c.com/lacoste-polo-shirts-c-2.html">womens lacoste polo</a>it is so useful to me.


69. Air Jordan Retro Aug 16, 2010 at 00:36

I really appreciate what you post. I am the first time on this site and am really enthusiastic about and so many good articles.


70. cheap jerseys Aug 16, 2010 at 18:06

Great article, I think you covered everything there. . . I would say freelancing is quite hard especially if you are not used to working on your own, can be quite hard to motivate yourself also. . . we all know what it is like to stare at the monitor.


71. vibram five fingers Aug 18, 2010 at 01:08

Really trustworthy blog. Please keep updating with great posts like this one. I have booked marked your site and am about to email it to a few friends of mine that I know would enjoy reading..


72. cheap nfl jerseys Aug 24, 2010 at 19:22

<a href="http://www.nfljerseyse.com/dallas-cowboys-jerseys " title="Dallas Cowboys Jerseys ">Dallas Cowboys Jerseys </a>


73. louis vuitton shoes Aug 26, 2010 at 21:03

Thanks for sharing your article. I really enjoyed it. I put a link to my site to here so other people can read it. My readers have about the same interets


74. snow boots Aug 31, 2010 at 01:46

 It's to limiting without bringing too much benefit I think. Maybe in the future...


75. louis vuitton sunglasses Sep 01, 2010 at 21:31

Thanks for sharing your article. I really enjoyed it. I put a link to my site to here so other people can read it. My readers have about the same interets

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
Give Back to Open Source