Custom RESTful route within has_many nesting - ruby-on-rails

Projects have many tasks and a task has a custom RESTful action called 'approve'.
I'm expecting the helper to look something like this approve_project_task_url
This isn't working for me:
map.resources :projects,
:has_many => :tasks,
:member => { :approve => :post }

I once had the same problem but I never searched long and hard for a fix. Instead I just opted for the older style which since then I've always used:
map.resources :projects do |project|
project.resources :tasks, :member => {:approve => :post}
end
That will give you your required approve_project_task_url(#project, #task) routes/helpers.
I guess you may already know this approach? If so and you don't like it hopefully I'll learn something from your other responses :)

**This is syntax correction to above solution**
map.resources :projects do |project|
project.resources :tasks, :member => {:approve => :post}
end

Related

How do you add a custom route to a singleton resource?

map.resource :basket, :collection => { :checkout => :post }
The above does not work for a resource, as you would expect since basket is a resource (ie. singular) not resources, so there is no concept of a collection, everything should be scoped to the current_user. In this case User has_one Basket.
However I would like to specify a custom route without having to resort to adding another line in routes, eg:
map.checkout 'basket/checkout', :controller => 'baskets', :action => 'checkout'
Is this possible?
Of course my other option is to add a checkouts controller.
Just use :member option instead of :collection:
map.resource :basket, :member => {:checkout => :post}
If the basket is scoped to the user I'd make it a nested resource:
map.resources :users do |users|
users.resource :basket, :member => { :checkout => :post }
end
... or in Rails 3 ...
resources :users do
resource :basket do
post :checkout, :on => :member
end
end
This way you'll be able to scope the basket to the user who's checking out. The URL will end up looking like this:
/users/5/basket/checkout
You also get the nicely worded named route 'checkout_user_basket'.

How to specify a route member inside a block in Rails?

The following code:
map.resources :users, :has_many => :items
Could be written like this in a block:
map.resources :users do |user|
user.resources :items
end
How could I write the following code in a block?
map.resources :users, :member => { :start => :post }
Also, where could I find documentation on writing routes in blocks?
The Routes Documentation does not seem to show it.
Thank you!
Rails 2.x doesn't allow you to use blocks for member definition.
With Rails 3.x you can write
resources :users do
member do
post :start
end
end
You can do it like so in Rails 2 (2.3.5 is the version I tested it on):
map.resources :users, :member => { :start => :post } do |user|
user.resources :items
end
From here: http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/

Routes - Tidily define multiple resources with customisation

My routes are getting out of hand due to the fact that this isn't possible (correct me if I'm wrong):
map.resources :english_pages, :as => :english, :spanish_pages, :as => :spanish do |article|
Due to nested routes things are spiralling out of control (tidiness).
map.resources :english_pages, :as => :english, :member => {:manage => :get} do |article|
article.resources :topics do |topic|
topic.resources :posts
end
end
map.resources :spanish_pages, :as => :spanish do |article|
article.resources :topics do |topic|
topic.resources :posts
end
end
How can I avoid duplication of this:
article.resources :topics do |topic|
topic.resources :posts
end
Is there some way to store routes as a block somewhere, or perhaps define, in my case, english_pages and spanish pages aliases seperately?
Thanks very much in advance.
All you need is a little bit of meta-programming.
resources is just a method call, and the symbols end up being evaluated as strings anyway, so this becomes pretty easy.
[:english, :spanish].each do |language|
map.resource "#{language}_pages", :as => language, :member => {:manage => :get} do |article|
article.resources :topics do |topic|
topic.resources :posts
end
end

Rails functional tests, routing error

I'm new to testing, and I'm having some difficulties trying to run a functional test.
I've a messages_controller, and a user_controller here.
in the routes, I've defined that the users resources has_many message resources.
Now I'm trying to run a simple test in the messages controller:
def test_index
get :index, { :user_id => 1 }
assert_template 'index'
end
But get a routing error from rails, that he cant find a route to messages. I don't want to include a route to messages only because of the tests. How can I tell the test that he must access from the /users/messages url?
the full routes.rb:
ActionController::Routing::Routes.draw do |map|
map.login 'login', :controller => :user_sessions, :action => :new
map.logout 'logout', :controller => :user_sessions, :action => :destroy
map.signin 'signin', :controller => :users, :action => :new
map.connect 'search/:action/:word', :controller => :search
map.connect 'search/:word', :controller => :search, :action => :index
map.resources :forums do |forums|
forums.resources :forum_posts, :collection => {:preview => :post }, :as => :posts do |post|
post.resources :forum_posts, :as => :reply
post.resources :reports
end
end
map.resources :newsitems, :as => :news do |news|
news.resources :comments do |comment|
comment.resources :reports
end
end
map.resource :user_sessions
map.resources :users,
:as => :profiles,
:controller => :profiles,
:has_many => [ :messages ]
map.resource :profiles
map.resource :me,
:controller => :me,
:has_many => [ :messages ]
map.resources :comments, :has_many => [ :reports ]
map.resources :forum_posts, :has_many => [ :reports ]
map.resources :reports
map.home '/', :controller => :home
map.root :controller => :home
map.namespace :admin do |admin|
admin.namespace :forum do |forum|
forum.resources :categories
forum.resources :posts
forum.resources :forums
forum.root :controller => :home
end
admin.resources :notices
admin.resources :users
admin.workflow 'workflow/:action', :controller => :workflow
admin.resources :newsitems
admin.resources :reports
admin.resources :comments
admin.root :controller => :home
end
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
end
UPDATE
I've noticed that every functional test get a routing error. Even the simpliests ones like newsitem. I've no idea why.
I recreated your scenario in a blank rails app with the routing code and and test you specified, and it worked without a problem, as it should. I'll paste my controller code here since that's the only part you left out:
class MessagesController < ApplicationController
def index
#messages = User.find(params[:user_id]).messages
end
end
If yours is doing basically the same thing, then a routing issue could be caused by a conflict in your routing file, which is what I suspect might be the case. Can you post it? FYI, I wrote an article on testing your routes, and that would be a very good idea because it would catch routing errors early, before they interfere with controllers.
Anyway, if you can post your routes I can take a look.
UPDATE: After looking at your routes, there are a couple conflicts. You can have messages as a sub-resource of more than one other resource, but in your messages controller you're going to have to account for the possibility of either a params[:me_id] or params[:profile_id]. It looks like they're both really the user model underneath, so it can be as simple as:
#user = User.find(params[:me_id] || params[:profile_id])
and you'll probably want to abstract that out into a method you call with before_filter.
The other issue is that you have two overlapping profiles routes, and I'm not sure why. I don't think it's a routing error in the test, because tests bypass the routing engine anyway. I think it's an error in the index view, because it probably contains links to messages with improperly formatted urls. If you have a link to a message, for instance, and you have a #profile object, then you'll need to call them like this:
<%= link_to message.name, profile_message_path(#profile, #message) %>
However, if you're using non-nested paths like message_path(#message), it will fail because there are no non-nested message routes.
It was a problem in my "journey" gem. They made routes more stricter in journey 1.0.4 which only show up on "test" environment. It is good for "developement" and "production".
** Ensure you are using exactly the same parameters as declared in routes **
Either add:
get :index, :locale => "en"
or in your Gemfile update:
gem 'journey', '1.0.3'
The second solution is a workaround for time being. Ideally you should be testing your routes will all exact params. Journey 1.0.4 is a lot stricter

routing urls and paths in rails

I have a controller which has a method called history
class UsersController < ApplicationController
def history
User.return_history(params[:id])
end
end
I have the following in my routes.rb file
map.resources :users, :shallow => true do |user|
user.resources :friends, :shallow => false
user.resources :posts, :collection=>{:no_access => :get}
user.resources :photos
end
How do I try to Ajax call the history method of the users_controller.rb? Using link_to_remote in the following way
link_to_remote 'History', :url=>history_user_path(#user), :update=>"history", :method=>'get'
throws me an error saying history_user_path() not found. How can this be? edit_user_path() shows no error and edit is not even explicitly defined in the User.rb file. Thanks.
mapresources :users creates a bunch of url/path helper methods, including edit_users_path. If you need others. you've got to add it as either a :member, or :collection option for map.resources.
This will let you do what you want:
map.resources :users, :shallow => true, :member => {:history => :get} do |user|
user.resources :friends, :shallow => false
user.resources :posts, :collection=>{:no_access => :get}
user.resources :photos
end

Resources