Questions about rails3 routes - ruby-on-rails-3

I'm upgrading my app to rails 3, and I am a bit confused about some of the routes. The resourceful ones are easy enough, but how can I set a generic rule for all actions in a specific controller. I tried something like this:
get 'custom/:action/' => {:controller => :custom}
But that didn't work. It seems the new format is "controller#action", but how can I specify the action to be variable?
Also, other than using named routes or resources, is it possible to do shorthand notation to name routes in a specific controller?
i.e. rather than:
get '/tasks', :controller => :home, :action => :tasks, :as => 'tasks_home'
get '/accounts', :controller => :home, :action => :accounts, :as => 'accounts_home'
is it possible to do something a little cleaner, like:
controller => :home do
get :tasks
get :accounts
end
And that would automatically created the named routes?

You can use action as a variable like this:
resource :custom do
match ':action'
end
This will generate
/custom/:action(.:format) customs#:action
custom POST /custom(.:format) customs#create
new_custom GET /custom/new(.:format) customs#new
edit_custom GET /custom/edit(.:format) customs#edit
GET /custom(.:format) customs#show
PUT /custom(.:format) customs#update
DELETE /custom(.:format) customs#destroy
So it will handle your action as a variable URL-s and will add some default CRUD actions as well.
Note that the controller name here is in plural. If you would like to use a route for a controller which name is in singular, use resources instead of resource.
The answer to the second question is almost identical to the first one, use resource:
resource :home do
get :tasks
get :accounts
end
generates:
tasks_home GET /home/tasks(.:format) homes#tasks
accounts_home GET /home/accounts(.:format) homes#accounts
home POST /home(.:format) homes#create
new_home GET /home/new(.:format) homes#new
edit_home GET /home/edit(.:format) homes#edit
GET /home(.:format) homes#show
PUT /home(.:format) homes#update
DELETE /home(.:format) homes#destroy
Note that the matched controller names are in plural again, because of the convention.

Looks like this is related to the persisted field being set to false on nested ActiveResource objects: https://github.com/rails/rails/pull/3107

Related

how can I make a simple route on rails and can I use it for an ajax form?

Ive been trying to create a simple route on rails, following this instructions
http://guides.rubyonrails.org/routing.html
my problem is that when I want to enter to my method I get a weird error.
I have a controler user and on my routes I wrote something like this
resources :users do
match "/custom/" => "user#custom"
end
So, at my controller I add this code
def custom
#user = User.find(params[:user_id])
end
but when I try to enter doing localhost:3000/users/1/custom I get an error like
uninitialized constant UserController
doing rake routes I can see
user_custom /users/:user_id/custom(.:format) user#custom
Any idea how to solve this problem?
I want this route to submit a form... is it possible to use this route (if i make it run) for use ajax? I want to submit a form.
Thanks
Change your route to:
resources :users do
match "/custom/" => "users#custom"
end
You should avoid the use of match though, since it will be deprecated in Rails 4. Try this instead
resources :users do
get :custom, on: :member
end
get is the verb, :custom the route and on: :member means that you are looking for a /users/:id/custom route instead of a /users/custom one. If you are looking for the latter, do this:
resources :users do
get :custom, on: :collection
end
Another way to do it is like this, which I prefer:
resources :users do
get 'custom', on: :collection
end
That gives you a route of /users/custom. If you were do use on: :member, then it would give you a route of /users/:id/custom.
You can also use a block for defining multiple custom actions for collections or members.
For example:
resources :users do
collection do
get 'custom'
post 'some_other_method'
end
member do
get 'some_action'
end
end

Singular route serving parameter :object_id instead of :id

Say I have an object called invoice. In routes.rb I have
resources :invoices do
get "pay"
end
When I run rake routes, the route is generated as
invoice_pay GET /invoices/:invoice_id/pay(.:format) invoices#pay
and the parameter is :invoices_id instead of :id
If I use a match statement:
match "invoices/:id/pay" => "invoices#pay", :via => :get
I get:
GET /invoices/:id/pay(.:format) invoices#pay
It seems to me that the route should be pay_invoice_path(#invoice), however, I have not found suitable documentation on this. Any suggestions?
i think what you are trying to do is
resources :invoices do
get "pay", :on => :member
end
have a look at the guides: http://guides.rubyonrails.org/routing.html

Pagination in Rails 3 with Kaminari always "destroys" one part of the URL

I'm using a Rails 3 App with Kaminari for Pagination. Because of my language, SEO and friendly URLs I want to change my URLs looks like. But it seems I have to decide between friendly pagination links and or displaying the correct method names. But first things first:
I have a model, which is called "pages" and contains some pages which can't be applied to only one model or don't contain any model. The following is a snippet of my "routes.rb":
match'/neugikeiten', :to => 'pages#neuigkeiten'
scope(:path_names => { :new => "neu", :edit => "bearbeiten", :delete => "loeschen", :index => "index", :page => "seite" }) do
resources :news, :path => "neuigkeiten"
end
As you can see, I match every page from pages to a single name and match all my other models and methods (I only used one as example) with scope. All of this works just fine. But now I have to add the Kaminare routes, for which the route looks like this:
resources :pages do
get 'seite/:page', :action => :neuigkeiten, :on => :collection
end
So, basically what I want to get is an URL like this:
/neuigkeiten/seite/2
The 2 is just an example and seite is the german word for page. But what i do get, is this:
/pages/seite/2
So, I get the model name, instead of the name I defined for this single page. I already tried switching the different codes but either I just have the original model name in the URL or I don't get nice URLs for pagination, which look like this:
/neuigkeiten?page=2
And if I try to match the pages without the resource like this:
match'/tagebuch/seite/:page', :to => 'pages#tagebuch', :on => :collection
I get the following error.
can't use collection outside resources scope
It seems to me that there should be another method to do this, because I can't be the only one having this kind of problem.
I'm glad for any help!
If anybody is interested in an solution, I found one myself. It's not perfect, but it works.
I had to install an extra gem for the localisation called "i18n_routing". Then in my routes.rb I did:
localized do
resources :news, :path => "neuigkeiten" do
get 'seite/:page', :action => :index, :on => :collection
end
end
The rest is done in the localized language file (en.yml).

Naming params of nested routes

resources :leagues do
resources :schedule
end
This generates:
leagues/:id
leagues/:league_id/schedule/:id
How can I keep the league ID from changing param names?
So it'll be:
leagues/:id
leagues/:id/schedule/:schedule_id
No, please do not do this.
The reason for it being this way is that it provides a common interface for nested resources across every single application. By making it different in your application, you're effectively going "against the grain" of Rails. Rails has a strict set of conventions that you should stick to. When you stray from this path, things get messy.
However, if you do want to shoot yourself in the foot, metaphorically speaking, you will need to define the routes manually. Here's the routes for the seven standard actions in a controller:
get 'leagues/:id/schedules', :to => "schedules#index", :as => "league_schedules"
get 'leagues/:id/schedule/:schedule_id', :to => "schedules#show", :as => "league_schedule"
get 'leagues/:id/schedules/new', :to => "schedules#new", :as => "new_league_schedule"
post 'leagues/:id/schedules', :to => "schedules#create"
get 'leagues/:id/schedule/:schedule_id/edit', :to => "schedules#edit", :as => "ed it_league_schedule"
put 'leagues/:id/schedule/:schedule_id', :to => "schedules#update"
delete 'leagues/:id/schedule/:schedule_id', :to => "schedules#destroy"
As you can see, it's quite ugly. But, if you really really really want to do it this way, that's how you'd do it.
You can set "param" option on resource route to override the default "id" param:
resources :leagues do
resources :schedule, param: schedule_id
end
refs to the Rails Routing Doc: http://guides.rubyonrails.org/routing.html#overriding-named-route-parameters
It appends the ID to the nested_param which is a bummer because I would like mine to be without the singular name. It looks like they really don't want you to make it only like :id as it could have conflicts. Plus it would be a bit of a diff from the normal restful routing that rails likes to use.
https://github.com/rails/rails/blob/5368f2508651c92fbae40cd679afbafdd7e98e77/actionpack/lib/action_dispatch/routing/mapper.rb#L1207
namespace :account, defaults: { type: 'account' }do
resources :auth, param: :lies_id, only: [] do
get :google
end
end
Rake routes returns the following
$ rake routes | grep /account/auth
account_auth_google GET /account/auth/:auth_lies_id/google(.:format)
So the solution which seams simpler is to just change the controller to use the nested param name it creates.

Rails 3 routes: How to avoid conflict with 'show' action?

I currently have the following routes set up for photos:
resources :photos
match 'photos/:user' => 'photos#user', :as => :user_photo
match 'photos/:user/:key' => 'photos#show', :as => :show_photo
Those two match routes give me URLs like:
http://example.com/photos/joe_schmoe
http://example.com/photos/joe_schmoe/123xyz
...similar to the way Flickr formats its URLs.
The problem I'm having, though, is that the photos/:user route is either interpreted as the show method, or if I put those custom routes before the resources then routes like /new get interpreted as the user method.
How can I get around that without having to do a ton of custom routes?
You'll want to put this custom route above the resources :users, so that it is matched first in the routes.
match 'photos/:user' => 'photos#user', :as => :user_photo
resources :photos
Additionally you can disable the show action altogether:
resources :photos, :except => :show
It's not a good idea to mix restful routes with custom match routes on the same resource. As you observed these two routes will intercept each others actions. Your best choice is to pick only one routing system for a resource and stick with it. If you want flickr style routes you should remove the restful route and add the other necessary match routes for new/create/etc you might need. If you desperately want to keep both of these routes You either need to disable show from the rest route with the rest route being on top, or you disable new from the match route while match being on top. You can use regexp to filter out some requests from match like this:
match 'photos/:user' => 'photos#user', :as => :user_photo, :constraints => { :user => /.*[^n][^e][^w].*/ }
This gets ugly really fast tho and I suggest just not using the rest route at all.