How do you add a custom route to a singleton resource? - ruby-on-rails

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

Related

How can I alter url paths?

For example, because my controller is called listings, whenever I post a new listing it is for example domain.com/listings/listing-name however I would like it to be domain.com/businesses/listing-name instead.
How can I do this?
Current routes for this controller:
resources :listings do
member do
post :leadcreate
post :storycreate
end
end
Use path option
resources :listings, :path => "businesses" do
end
If you also want to rename the route helpers, then
resources :listings, :path => "businesses", :as => "businesses" do
end

what does this rails code do? :on => :member

please can you explainme what does this code do?
resources :products do
get :who_bought, :on => :member
end
the code complete is from the book pragmatig programing, but it not explain why we use that code, ":on => :member""
Depot::Application.routes.draw do
resources :orders
resources :line_items
post 'line_items/decrease'
resources :carts
get "store/index"
resources :products do
get :who_bought, :on => :member
end
root :to => 'store#index', :as => 'store'
thanks
passing :on => :member means that you are working on a specific record in the database, in this case products. so the url that route generates is
/products/:id/who_bought
which means that you want the get the product whose id is :id and process the who_bought action. the counterpart, :on => :collection, expects the action to work on a list of products so the url will look like
/products/who_bought
if you change member to collection. you can see that the route doesn't require an :id passed because it doesn't expect you to work on a single record.

scope accessible rails 3.1

I have this scope:
scope ":user_id", :as => "user" do
resources :boards, :controller => 'users/boards'
end
I get this route:
http://localhost/hyperrjas/boards/
I want a url without boards then on routes.rb I add:
scope ":user_id", :as => "user" do
resources :boards, :controller => 'users/boards', :path => '/'
end
That works great, but it is still accessible via "/boards" ... How do I prevent that? (I'm using Rails 3.1)
You shouldn't have to specify the controller names when using resources and in this caseI would use nested resources:
resource :user, only: :show do
resources :boards
end
This should give you the following:
/:user_id
/:user_id/boards
/:user_id/boards/new
/:user_id/boards/:id/
/:user_id/boards/:id/edit
and of course your restful routes!

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

How to add a new action in Ruby on Rails?

I have this in routes.rb
map.namespace :admin do |admin|
admin.resources :projects, :has_many => :products
end
What I would like to is to be able to set the something in the routes.rb file in order for me to use new action in the products controller. Actions added by hand after scaffolding.
I tried something like this
map.namespace :admin do |admin|
admin.resources :projects, :has_many => :products , :collection => {:plan => :get}
end
plan being my new action in the products controller
Did not work and I have not find any good solutions anywhere. Please help.
As klew already pointed out you probably want a member action and not a collection action.
But before you go this route think if you really needed. adding custom actions is discouraged. You better stay within the constrains of the 7 CRUD operations. The way to do it is to add more controllers :)
For example if you have users controller and groups controller, then adding person into the group is not join_group action in the users controller, and not add_user action in the group controller, its a regular create action in memberships controller :).
and remember that controllers do not always correspond to models, and models not necessarily correspond to database tables, you can be more flexible.
Back to your case, I think you might want to just add a singleton resource nested inside the project resource like this
map.namespace :admin do |admin|
admin.resources :projects, :has_many_products, :has_one => :plan
end
and implement :show action in the plans_controller, which should be mapped to /admin/projects/:project_id/plan
Change
map.namespace :admin do |admin|
admin.resources :projects, :has_many => :products
end
in
map.namespace :admin do |admin|
admin.resources :projects do |project|
project.resources :products, :member => { :new_action => :get }
end
end
Here you have some examples.
Your example should generate urls like: /admin/projects/plan. If you want to have url like /admin/projects/2/plan then use:
map.namespace :admin do |admin|
admin.resources :projects, :has_many => :products , :member => {:plan => :get}
end
And don't forget to add plan method in your admin/products_controller.rb:
def plan
...
end
I'm not sure, but probably you will need to restart your server after changing routes to get it working.

Resources