How can I use uncountable nouns in resource routes with Rails 3? - ruby-on-rails

I have a model called Aircraft, inflected as uncountable, and it's
driving me nuts. What is the correct way to handle this in Rails 3 "beta
4"?
resources :aircraft do
member do
get :flights
end
end
# Of course these work for some actions and not others.
match 'aircraft', :to => 'aircraft#index', :as => :aircraft
match 'aircraft/:id', :to => 'aircraft#show', :as => :aircraft_instance

I think it's just:
resources :aircraft, :singular => :aircraft_instance
Then you link_to them like so:
link_to 'Singular aircraft', aircraft_instance_path(#aircraft)
link_to 'All aircraft', aircraft_path(#aircraft)
Edit
It looks like beta4 thinks aircrafts in the plural of aircraft:
rails console
> :aircraft.to_s.pluralize
=> "aircrafts"
If you just put resources :aircraft into your routes, can you link_to both aircraft_path(#aircraft) and aircrafts_path successfully? If so, you may need to write an initializer for ActiveSupport::Inflector to define your own custom inflection.

Related

Better routes in rails with params

I've been working on a movie application in Rails. I have a controller/view for the actor. When passing params to the actors controller i want the URL to be pretty. Now it looks like this: http://localhost:3000/actors/show?actor=Hayley+Atwell and i want it to look like /actors/show/Hayley+Atwell or /actors/Hayley+Atwell.
How do i do this? My link in the movies view is:
<%= link_to a.name, {:controller => 'actors', :action => 'show', :actor => a.name}, :class => "label label-default" %>
My routes.rb is now like this:
get 'actors/show'
I recommend you use friendly_id gem. It perfectly satisfies your needs!
https://github.com/norman/friendly_id
You can use the following in your ./config/route.rb file:
get '/actors/:actor', to: 'actors#show'
I'll give a series of refactoring advice as follow:
Try to make use of the actor's id instead of the name
Change the route to: get '/actors/show/:id', to: 'actors#show'
Then, you can now change the link in your view to something like:
<%= link_to a.name, {:controller => 'actors', :action => 'show', :id => a.id}, :class => "label label-default" %>
Note: the : part in the :id of your route means that this could be interchanged to anything, and whatever comes in that place will be interpreted as the id. So, in /actors/show/7, the id of the actor is 7, you can now find the actor with this id in your controller action, and do anything you want with it.
If I'm right than you try to create RESTful routes anyway.
So I would recommend you to use the Rails' resources method in your routes:
Rails.application.routes.draw do
resources :actors # If you want only the :show action than add:
# , only: [:show]
end
This automatically creates the proper routes for you.
To get the names into the URL you should use the friendly_id gem. It has a great community and is very well tested.

Internationalized I18n URL helpers

Is it possible to have URL helpers in Rails behave differently for different locales, eg.
<%= link_to "Something", example_path %>
in English would go to site.com/something, and in another language to site.com/lang/blahblah
Currently, my routes are defined like
scope '(:locale)', :locale => /otherlang/ do
get '/' => 'home#show'
get 'otherlang-about' => 'about#show'
get 'otherlang-something/:id' => 'example#show'
end
get 'about' => 'about#show'
get 'something/:id' => 'example#show'
root 'home#show'
Yes it is possible, We are using this gem https://github.com/enriclluelles/route_translator in my company to get different routes_url for each languages but still pointing to the same controller#method.
You just need to define a route.yml file to define the translation for each routes.

Ruby on Rails - Setting a Custom Route with Custom Action

i'm a RoR beginner using rails 3.2.3.
I have a products resource and a nested Availability resource under it.
My routes are:
/products/:product_id/availabilities
which is working great.
However, my availability model allows to set the price of the product on an exact date, for e.g.: today the product 'x' will have 'y' availability (it is an integer) and 'z' price (price is also an attribute of the Availability Model).
My question is, I now want to add a page that allows the modification of prices (without creating a new Price Model), similar to the one I have for availability. I'm only using the index action, as it will show all the availabilities for that only selected product.
How can I create a route like:
/products/:product_id/availabilities/prices
I tried adding this to my routes rb:
match '/products/:id/availabilities/prices', :action => 'indexP', :via => [:get], :controller => 'prices'
match '/products/:id/availabilities/prices', :action => 'createP', :via => [:post], :controller => 'prices'
It is worth noting that I am using a prices controller to interact with the same availability model, but that shouldn't be a problem.
I also created in that prices controller the indexP and createP actions.
In my Availabilities View I created an indexP.html.erb to differentiate from the regular index.html.erb which contains the availabilities.
And when I run rake routes I get:
$ rake routes | grep prices
GET /products/:id/availabilities/prices(.:format)
POST /products/:id/availabilities/prices(.:format)
Which is great. However, when I access that URL, I get:
NoMethodError in Availabilities#show
{"product_id"=>"46",
"id"=>"prices"}
Why is it doing the show action instead of my custom match?
Note: I am not after something like: /products/:id/availabilities/:id/prices
just :/products/:id/availabilities/prices
I know this is probably a dumb question but I would appreciate any tips to help me understand what is going on.
Thanks in advance,
regards
How is your product model setup?
If it something like:
resources :products do
resources :availabilities
end
the first match the route does goes to the show action of the availabilities controller.
You have two options here. Limit the availabilities controller scope, with:
resources :products do
resources :availabilities, :only => [:index]
end
or to place your custom match before this block
match '/products/:id/availabilities/prices', :action => 'indexP', :via => [:get], :controller => 'prices'
match '/products/:id/availabilities/prices', :action => 'createP', :via => [:post], :controller => 'prices'
Note that this routes are not restfull. This may get you in trouble later :)
Hope this helps.

How to match hash (deep nested) params in Rails3 to make a pretty URL?

If I have this route (in routes.rb):
match 'posts', :to => 'posts#index'
It will show and match the following routes:
# Case 1: non nested hash params
posts_path(:search => 'the', :category => 'old-school')
#=> "/posts?search=the&category=old-school"
# Case 2: nested hash params
posts_path(:filter => {:search => 'the', :category => 'old-school'})
#=> "/posts?filter[search]=the&filter[category]=old-school"
If I want to make the category param part of the main URL, I could do this for the Case 1.
match 'posts(/:category)', :to => 'posts#index'
that will show and match the following routes:
# Case 1: non nested hash params
posts_path(:search => 'the', :category => 'old-school')
#=> "/posts/old-school?search=the"
But how could I do the same if the param is nested (Case 2)?
I would expect the next route definition:
match 'posts(/:filter[category])', :to => 'posts#index'
to work this way:
# Case 2: nested hash params
posts_path(:filter => {:search => 'the', :category => 'old-school'})
#=> "/posts/old-school?filter[search]=the"
But it does not work.
I found this same question in two places with no righ answer:
how-to-specify-nested-parameters-in-the-routes
how-to-accept-hash-parameters-in-routes
The Rails Guides don't specify anthing about this.
Should I assume that this can not be done in rails? really?
you could just make two different routes instead
match 'posts', :to => 'posts#index'
match 'posts/:category', :to => 'posts#index'
The next route will not work as you intended it.
match 'posts(:filter[category])', :to => 'posts#index'
The :filter is just a place holder for either the first argument thats passed into the url helper or the value for the key :filter in a has that is passed in. Any expressions in the route string will not be evaluated.
I guess the answer to your question is that you cannot do this in rails. I would suggest to you though that you do this in another way. It is very helpful in rails to follow the convention and make things easier on yourself.
Looks like you are doing three things here. The base post routes
match 'posts', :to => 'posts#index'
A route that has the category nested in it. Most likely to give the user a better url
match 'posts/:category', :to => 'posts#index'
And a search url which can be the same as the first, or to make your action cleaner, a different one
match 'posts/search', :to => 'posts#search'
There is really no reason I can think of to complicate the routes in the way your are suggesting. A search query url doesn't look nice anyways so why bother handling two urls for searches. Just one will do.
You should definitely take a look at running
rake routes
as this will tell you exactly what you have defined in your routes file. You can also set up routing tests to ensure your custom routes are performing correctly.
Your example does not work (as you indicated)
# Case 2: nested hash params
posts_path(:filter => {:search => 'the', :category => 'old-school'})
#=> "/posts/old-school?filter[search]=the"
But what you should be looking for is this
posts_path(:filter => {:search => 'the', :category => 'old-school'})
#=> "/posts?filter[search]=the&filter[category]=old-school"
This is ok to do it this way.
If you want to keep posts/:category just use this for navigation only, not for search.
Hope that helps

Rails hide controller name

i have match ":id" => "people#show" in my routes.rb
now i can access http://localhost:3000/1
but,in views <%= link_to 'Show', people %> it will generate http://localhost:3000/people/1 ,
i want to it to be http://localhost:3000/1
You could do something like this to ensure that only numeric ids are matched:
match '/:id' => 'people#show', :constraints => {:id => /\d+/}
A good alternative might be to use some kind of identifier, even if it's not the controller name: http://localhost:3000/p/1. This will at least ensure that if you add other controllers and actions you don't end up having to change your routing structure.
You could write a custom route to match that in config/routes.rb. At the bottom of your routes.rb file you will have a route like match ':controller(/:action(/:id(.:format)))'
or something like resources :people. You might have to write a route that matches the route type you want.
You have to create a named route.
match ':id' => 'people#show', :as => :person
And fix your views to use your new method person_path(user_id).

Resources