I've read the documentation, but I'm still not sure I understand everything.
Especially why there's a :new parameter. As far as I understand, it could be replaced with the :collection parameter.
So what's the difference between those three types of routes?
The difference is the URL generated.
Let's guess three resources :
map.resources :users, :collection => { :rss => :get }
map.resources :users, :member => { :profile => :get }
map.resources :users, :new => { :draft => :get }
The first route will create :
/users/rss
With nothing between the controller name and the action name. We don't need any other parameter to get the user's list rss feed.
The second one will create the action "profile" as a member of the object. So we'll have :
/users/1/profile
The "1" is the user's to_param. We need a user's id to display a profile.
The third one will create the action "draft" as a member of the new action. So we'll have :
/users/new/draft
The "draft" action displays a draft of the user before accepting its creation.
So that's the difference between :collection, :member and :new. Every of them creates different routes, each one with their own purpose.
:member creates path with pattern /:controller/:id/:your_method
:collection creates path with the pattern /:controller/:your_method
:new creates path with the pattern /:controller/:your_method/new (please note that the last element of the path i.e. new is constant)
New differs from Collection mainly on the ideological layer. That's how REST gurus see the creation of the REST "subresource" within the bigger resource.
Damiens explanation is mostly right except for the section about :new
Have a really good read of the ruby on rails routing guide at http://guides.rubyonrails.org/routing.html It explains routing from the inside out, and then back again. Section 3.11.3 (Adding New Routes) describes what :new does, and it is very different to :member and :collection.
Basically map.resources :photos, :new => { :upload => :post } will create /photos/upload using the POST HTTP verb.
Related
I have a model named Entree for which the new action needs a parameter, the id of another model named Cave. I don't want to nest Entree in Cave since Cave is already nested.
What I did was declaring the resource Entree as follow in routes.rb:
resources :entrees, :except => [:new]
match "/entrees/new/:id", :to => "Entrees#new", :as => 'new_entree'
That works, but the problem is when there's an error in the create action, I want to display the page again with the invalid input. But since there's no new action, I must do a redirect_to new_entree_path, which does not keep the user input.
I have tried the following (simplest) route:
resources :entrees
But then the path http://localhost:3000/entrees/new/32 returns an error:
No route matches [GET] "/entrees/new/32"
The question is, how can I declare the Entree resource in the routes file with a parameter for the new action ?
I'm not sure if that's a hack or not, but the following works and seems cleaner than 2-levels nesting.
resources :entrees, :except => [:new] do
collection do
get 'new/:id', :to => "entrees#new", :as => 'new'
end
end
Now I can do a render "new" instead of a redirect_to.
I must say that I must have asked my question wrongly, my bad.
Rails has a route helper called path_names that does this:
resources :entrees, path_names: { new: 'new/:id' }
To improve gwik 's solution (which in fact didn't work for me):
resources :things, except: [:new] do
new do
get ':param', to: 'things#new', as: ''
end
end
It gets you new_thing_* helpers (instead of new_things_*) for free.
If you want to use Rails resource routes, you will have to nested them according to how they work
resources :caves do
resources :entrees
end
to get the route /caves/70/entrees/new
Otherwise, you are in a world of creating manual match routes.
match "/entrees/new/:id", :to => "entrees#new", :as => 'new_entrees'
I do not understand why you are forced to use a redirect? The new_entrees route is valid. You will not be able to use the form_for helper, since it is not a resource, but the form_tag helper will work.
UPDATE: Render and Route
The Route does not directly change what view is rendered in the Controller. That is determined by the controller itself. Render examples:
render :new will render the new action's view
render 'entrees/new' will render the entrees/new template
I found this generates the correct new_thing_path method not new_things_path as Antoine's solution.
resources :things, :except => [:new] do
with_scope_level(:new) do
get 'new/:param', :to => "things#new", :as => ''
end
end
i have a controller "Apps". It consists of one action "index". Now I want to add a new action called "buy":
def buy
respond_to do |format|
format.html
end
end
i added a buy.html.erb to the views, but when browsing to /apps/buy, i get following message:
Unknown action - The action 'show' could not be found for AppsController
in the routes I added this:
match '/apps/buy', :controller => 'apps', :action => 'buy'
thanks in advance!
The url is being caught by the standard /apps/:id route, I assume you also have resources :apps in your routes?
Simply place the buy route first:
match '/apps/buy', :controller => 'apps', :action => 'buy'
resources :apps
Bear in mind that routes are executed in the order they are defined, so the specific ones need to precede the general.
A simpler approach as #Ryan suggests is adding a collection route to the resource:
resources :apps, :collection => { :buy => :get }
I want to copy the twitter profile page and have a url with a username "http://www.my-app.com/username" and while I can manually type this into the address bar and navigate to the profile page I can't link to the custom URL.
I think the problem is in the routes - here's the code in my routes.rb
map.connect '/:username', :controller => 'users', :action => 'show'
Also, I have Question and Answer models and I want to link to them with the customized URL like so:
http://www.my-app.com/username/question/answer/2210
There's nothing wrong with your route. Just remember to define it at the end, after defining all other routes. I would also recommend using RESTful routes and only if you want to have better looking URLs use named routes. Don't use map.connect. Here's some good reading about Rails routes.
Here's how this could look:
map.resources :questions, :path_prefix => '/:username' do |question|
question.resources :answers
end
map.resources :users
map.user '/:username', :controller => 'users', :action => 'show'
Just a draft you can extend.
To create urls you need to define to_param method for your user model (read here).
class User < ActiveRecord::Base
def to_param
username
end
end
I know this questions is old but it will help someone.
You could try the below. I've used it in a rails 4 project and all seems to be working great. The reason for the as: :admin is I also had a resources posts outside of this scope. It will add a admin to the helper calls e.g. admin_posts_path
scope ":username", module: 'admin', as: :admin do
get '', to: 'profiles#show'
resources :posts
end
I have used like this
In View part
portfolio.user.name,:id =>portfolio) %>
and in rout.rb
map.show_portfolio "portfolios/:username", :action => 'show_portfolio', :controller => 'portfolios'
I'm reading these two pages
resources
Adding more RESTful actions
The Rails Guides page shows
map.resources :photos, :new => { :upload => :post }
And its corresponding URL
/photos/upload
This looks wonderful.
My routes.rb shows this
map.resources :users, :new => { :signup => :get, :register => :post }
When I do: [~/my_app]$ rake routes
I see the two new routes added
signup_new_user GET /users/new/signup(.:format)
register_new_user POST /users/new/register(.:format)
Note the inclusion of /new! I don't want that. I just want /users/signup and /users/register (as described in the Rails Routing Guide).
Any help?
When you expose a controller as a resource, following actions are automatically added:
show
index
new
create
edit
update
destroy
These actions can be categorized in to two groups:
:member actions
The URL for the member action has the id of the target resource. E.g:
users/1/edit
users/1
You can think of :member action as an instance method on a class. It always applies on an existing resource.
Default member actions: show, edit, update, destroy
:collection actions
The URL for the :collection action does not contain the id of the target resource. E.g:
users/login
users/register
You can think of :collection action as a static method on a class.
Default collection actions: index, new, create
In your case you need two new actions for registration. These actions belong to :collection type( as you do not have the id of the user while submitting these actions). Your route can be as follows:
map.resources :users, :collection => { :signup => :get, :register => :post }
The URL for the actions are as follows:
users/signup
users/register
If you want to remove a standard action generated by Rails use :except/:only options:
map.resources :foo, :only => :show
map.resources :foo, :except => [:destroy, :show]
Edit 1
I usually treat the confirmation action as a :member action. In this case params[id] will contain the confirmation code.
Route configuration:
map.resources :users, :member => { :confirm => :get}
URL
/users/xab3454a/confirm
confirm_user_path(:id => #user.confirmation_code) # returns the URL above
Controller
class UsersController < ApplicationController
def confirm
# assuming you have an attribute called `confirmation_code` in `users` table
# and you have added a uniq index on the column!!
if User.find_by_confirmation_code(params[id])
# success
else
# error
end
end
end
This can be taken as just another syntax -- something good to know may be.
Syntax 1:
resources :users do
member do
get 'signup'
post 'register'
end
end
Rake Route Output will include
signup_users GET /users/signup(.:format) {:action=>"signup", :controller=>"users"}
register_users POST /users/register(.:format) {:action=>"register", :controller=>"use
rs"}
Syntax 2:
If you have only one collection route
resources :users do
get 'signup', :on => :collection
end
If i'm understanding your question right, you just want to rename the urls of the new and create actions.
This would be done like so:
map.resources :users, :path_names => {:new => 'signup', :create => 'register'}
If you really would like to add new routes with corresponding controller actions, then Damiens answer is the way to go.
The new option allows you to create new routes for creating new objects. That's why they're prefixed with that term.
What you're looking for is the :collection option.
map.resources :users, :collection => { :signup => :get, :register => :post }
Which will create the /users/signup and /users/register urls.
I have a rails model called 'audioclip'. I orginally created a scaffold with a 'new' action, which I replaced with 'new_record' and 'new_upload', becasue there are two ways to attach audio to this model.
Going to /audioclips/new_record doesn't work because it takes 'new_record' as if it was an id. Instead of changing this, I was trying to just create '/record_clip' and '/upload_clip' paths.
So in my routes.db I have:
map.record_clip '/record_clip', :controller => 'audioclips', :action => 'new_record'
map.upload_clip '/upload_clip', :controller => 'audioclips', :action => 'new_upload'
When I navigate to /record_clip, I get
ActionController::MethodNotAllowed
Only get, head, post, put, and delete requests are allowed.
I'm not extremely familiar with the inner-workings of routing yet. What is the problem here? (If it helps, I have these two statements above map.resources => :audioclips
Yes, your two routes are conflicting with your mapped resource (the map.resources => :audioclips bit).
If you want to continue using resources, I suggest you change that line to:
map.resources => :audioclips,
:new => {
:new_record_clip => :post,
:new_upload_clip => :post }
If you want some more information, the Rails guide is incredibly helpful on this topic:
http://guides.rubyonrails.org/routing.html#adding-more-restful-actions