How to add a new action in Ruby on Rails? - 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.

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

Exposing a Specific Action of a Nested Route

Using this as an example contexted:
Post has_many comments
Comment belongs_to post
I have a route that looks like:
resources :posts do
resources :comments
end
How do i create a route that exposes comments#index?
An example use case would be... I want to list ALL comments in the system on a page. Essentially using the comments resource as if it's not nested when a user hits /comments
thank you!
Try this.
resources :posts do
resources :comments, :except => :index
end
match 'comments' => 'comments#index', :as => :comments
That said, I usually look to avoid routes like this because I like a tidy RESTful routes file, but sometimes it can't be helped.
Second option:
resources :posts do
resources :comments, :except => :index
get :comments, :on => :collection
end
In the second option, you'd want to remove the index action from the comments controller and create a comments action in your posts controller.

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

Adding collection routes to nested resources

I need to add some collection routes for a nested resource. I have a subscription controller and I need two new methods to add here. change_plan and update_plan
Basically I need the urls looks like;
http://localhost:3007/admin/accounts/1/subscriptions/7/change_plan
http://localhost:3007/admin/accounts/1/subscriptions/7/update_plan
So where to add change_plan and update_plan? this is what I have so far.
map.namespace :admin do |admin|
admin.resources :payments
admin.resources :accounts, :collection=>{:profile=>:get}, :has_many => [:payments,:subscriptions]
end
Thanks for any help.
Use the alternative syntax for has_many:
admin.resources :accounts, :collection=>{:profile=>:get} do |account|
account.resources :subscriptions, :member => { :change_plan => :get, ... }
...
end

Namespaced resources

This is an excerpt from my config/routes.rb file:
resources :accounts do |account|
account.resource :profile, :except => [:new, :create, :destroy]
account.resources :posts,
:collection => { :fragment => :get },
:has_many => [:comments, :likes]
# even more code
end
I would like that each nested resource to be loaded from from the account namespace such as Account::PostsController instead of PostsController.
Using resources :accounts, :namespace => 'account' tries to load AccountPostsController.
Trying to nest the structure doesn't really work all that well:
map.namespace :account do |account|
..
end
The previous code will load the files from the locations I want, however it does add the namespace to the url and the generated paths so I'll have methods such as account_account_posts_url and similar paths.
Another alternative is to use something like:
account.resource :profile, :controller => 'account/profile'
I really don't like this as it involves both code duplication and forces me to remove some of the rails magic helpers.
Any thoughts and suggestions?
Changing my routes.rb and running rake routes I came up with the following:
map.resources :accounts do |accounts|
accounts.namespace :account do |account|
account.resource :profile, :except => [:new, :create, :destroy]
end
end
This gets you what you want. The correct url and pointing to account/... controller.
See Rails Routing for more detailed info and options on what can be done with Rails Routes.
So what's specifically wrong with namespacing? I think this is what you're trying to do:
map.namespace :account do |account|
account.resource :profile
end
This will try to load the controller at app/controllers/account/profiles_controller.rb and will generate routes such as account_profile_path.
Updated based on comment:
map.resources :accounts do |account|
account.resource :profile
end
Will give you /accounts/22/profile.

Resources