Map multiple paths to a controller - ruby-on-rails

I have a controller called CardController. Currently I have routes like card_path that map to /cards/:id. I would like to make it so that I can use /trips/:id and /events/:id that map to the same /cards/:id. I know I'll have to override card_path eventually but is it possible to set up my routes file for this? Do I need to set up a Trip and Event controller that just redirect to the card actions?
Edit:
Trips should completely map to cards, meaning 'trips/1/edit' should end up at 'cards/1/edit', 'trips/1/images/12' should end up at 'cards/1/images/12'

I ended up adding some controller to the routes file.
routes.rb
def card_routes
member do
get 'test'
end
end
class TripsController < CardsController; end
resources :trips { card_routes }
resources :cards { card_routes }
Now /trips/1/test and /cards/1/test go to the same place.

You can easily do something like:
get 'trips/:id' => 'cards#show'
Try accessing different trips in your browser, trips/1 or trips/2 (if cards with those ids exist), and they should redirect to the appropriate card.
If you haven't already, I recommend taking a few minutes and reading the Routing Guide, as it's really comprehensive and shows different ways of accomplishing things:
http://guides.rubyonrails.org/routing.html

Related

Rails: help me avoid having to use such deeply nested routes

I've got really long nested routes like so:
# barracudas have many elephants
resources :barracudas do
# elephants have any barracudas, and have many gloworms too
resources :elephants do
# gloworms have any elephants, and have many chimpanzees too
resources :gloworm do
# chimpanzees have many gloworms
resources :chimpanzees do
end
end
end
end
As you can see there are a lot of has_many, :through relationships here, so it's hard to find a chimpanzees parent gloworm wihout a little help from the URL:
# chimpanzees_controller.rb
def show
#barracuda = Barracuda.find(params[:barracuda_id])
#elephant = #barracuda.elephants.find(params[:elephant_id])
#gloworm = #elephant.gloworms.find(params[:gloworm_id])
#chimpanzee = #gloworm.chimpanzees.find(params[:id])
end
I would like to hide the fact that my system has barracudas, I want to change the urls from:
http://lvh.me:3000/barracudas/1/elephants/32/gloworms/5/chimpanzees/3
to:
http://lvh.me:3000/elephants/32/gloworms/5/chimpanzees/3
Am I right in thinking that I just need to remove the nesting from the routes, and add a :barracuda_id to the session so that I can always find it in the controller? Something like this?
#barracuda = Barracuda.find(session[:barracuda_id])
What if I wanted to remove all mention of elephants and gloworms from the URLs too? I guess it would be the same idea right? Expiring session data will probably be the biggest job.
Can a chimpanzee belong to more than one gloworm (and so on up the chain)? If not, you can collapse your whole structure into unnested routes and use something like this in your Controller:
# chimpanzees_controller.rb
def show
#chimpanzee = Chimpanzee.find(params[:id])
#gloworm = #chimpanzee.gloworm
#elephant = #gloworm.elephant
#barracuda = #elephant.barracuda
# Maybe do some checking on the current user to make sure the right Barracuda is loaded?
end
As long as you have belongs_to associations all the way up, you should be golden.
If you've got has_and_belongs_to_many associations anywhere in there, then you have to have the IDs from both sides of the association. Your session approach seems reasonable, although it can fall down if your users ever open multiple tabs to browse separate Barracudas.
Another approach could be composite IDs in your routes. Something like:
get "/chimpanzees/:barracuda_id-:elephant_id-:gloworm_id-:id" => "chimpanzee#show"
The URLs will look a little non-standard, but it disguises the objects you're dealing with.

Add new view to a controller in Rails

I have a controller, clients_controller, with corresponding index, show, edit, delete, new & form views. Is there a way to create a new view like clients/prospects.html.erb that acts the same way as clients/index.html.erb, except is routed at clients/prospects/?
I've tried this:
match '/clients/prospects' => 'clients#prospects'
And some other things in routes.rb, but of course get the error "Couldn't find Client with id=prospects".
The goal here is basically to have a prospects view and a clients view, and by simply switching the hidden field to a 1, it (in the user's mind) turns a prospect into a client (it's a CRM-like app).
There's a couple of things you need to do. First you need to put the your custom route before any generic route. Otherwise Rails assumes the word "prospects" is an id for the show action. Example:
get '/clients/prospects' => 'clients#prospects' # or match for older Rails versions
resources :clients
Also you need to copy / paste the index method in your ClientsController and name it prospects. Example:
class ClientsController < ApplicationController
def index
#clients = Client.where(prospect: false)
end
def prospects
#prospects = Client.where(prospect: true)
end
end
Lastly, you need to copy the index.html.erb view and name the copy prospects.html.erb. In the example above you would have to work with the #prospects instance variable.
Create a new action in clients controller named prospects. And then define a collection route in routes.rb for it as either resource full way. Or u directly use match as you were doing.
What you're doing is not wrong (although I'd change match to get, otherwise POST and DELETE requests to that url will also render your prospects view). Presumably you have
resources :clients
in your routes file? If so, what you have will probably work if you just move the line you quoted above the resources declaration -- the problem is that /clients/prospects matches the show route for the clients resource, so if it's defined first then that's the route that gets matched.
However, there's a more idiomatic way to define this route
resources :clients do
collection do
get :prospects
end
end
See Rails Routing documentation for more
Also see migu's answer for what else needs to be done once the url is being routed correctly (though there are other things you can do -- if you the two views are similar enough, you can reuse the view template, for example).

Obtaining ID of containing resource via params[:id] for custom actions

I have the following routes in my config/routes.rb file:
resources :employees do
get 'dashboard'
get 'orientation'
end
employees refers to a regular resource handling the standard RESTful actions. dashboard and orientation are what I currently refer to "custom actions" which act on Employee instances. I apologize if I have my terminology mixed up and dashboard and orientation are really something else. These custom actions respond to URLs as follows:
http://myhost/employees/1/dashboard
i.e. They're "member" actions much like show, edit etc.
Anyway, this all works well enough. Regular actions such as show on EmployeesController obtain the ID of the associated Employee through params[:id]. However, with this current structure, dashboard and orientation have to use params[:employee_id] instead. This is not too difficult to deal with, but does lead to some additional code complexity as my regular before_filters which expect params[:id] don't work for these two actions.
How do I have the routing system populate params[:id] with the ID for these custom actions in the same way as show etc.? I've tried various approaches with member instead of get for these actions but haven't got anything to work the way I would like yet. This app is built using Ruby on Rails 3.2.
This might help you:
resources :employees do
member do
get 'dashboard'
get 'orientation'
end
end
and the above will generate routes like below, and then you will be able to use params[:id] in your EmployeesController.
dashboard_employee GET /employees/:id/dashboard(.:format) employees#dashboard
orientation_employee GET /employees/:id/orientation(.:format) employees#orientation
I haven't tested this example, but you can set the resourceful paths explicitly.
Something like this might work:
resources :employees, path: '/employees/:id' do
get 'dashboard', path: '/dashboard'
get 'orientation', path: '/orientation'
end

Can't find a row in my table sqlite3 table

I'm trying to set up a simple edit feature for an object in one of my tables. Here is my current code which I've seen online and in books but it doesn't work
def edit
#fire_chief = FireChief.find(params[:id])
end
All I have to do is figure out how to edit a specific Fire Chief in list view and I can keep programming, but I'm stuck.
The error I'm recieving is "Couldn't find FireChief without an ID"
If I use this little snippet of code it finds the edit form fine
def edit
#fire_chief = FireChief.last
end
But it always pulls up the last entry to be edited. I need it to pull the entry that i click...might be the 1st, 3rd, or 5th, etc. I know a lot of this is redundant, but I just want to be clear on what I'm trying to do.
This is my routes file:
resource :timesheet do
resource :command_officer
resources :fire_chief
resources :fire_fighters
resource :safety_officer
resources :emts
resources :hazmat_specialists
resources :command_vehicles
resources :engines
resources :emergency_supports
resources :hazmat_units
resources :field_units
resources :pumpers
resources :tankers
resources :rescue_units
end
end
I just changed the resource :fire_chief to be plural, so now its this resources :fire_chief
But I'm getting this error now:
Routing Error
uninitialized constant FireChiefController
To get an id or any other data you need from params you have to pass the data inside the url like this example:
/firechief/2/edit
This way your edit method will know the id and the method will work. You can set this manually inside your routes, making a route like this:
get "/firechief/:firechief_id/edit" => "firechiefs#edit", as => :edit_firechief
Or if you are using resources inside your routes, it shall work by default.
If you actually does not want to pass any data inside your url, you could implement a session based solution. Create a method to store the id of the firechief inside a controller that fits your needs, or just add this line if you want to create the session inside a method (like firechief#create) you already has:
session[:firechief] = #here you add the id of the firechief you want to store
Now on your controller you can do this:
def edit
#fire_chief = FireChief.find(session[:firechief])
end

Is it ok to use both nested and shallow resources in rails? How to write the controller/views?

I have resources for which it makes perfect sense to address them both as nested withing other resources and separately. I.e. i expect to use all urls like these:
/account/4/transfers # all transfers which belong to an account
/user/2/transfers # all transfers input by specific user
/project/1/transfers # all transfers relevant to a project
/transfers # all transfers
my concern is how do I write TransfersController actions (for example index) as it would double the logic found in parent models - is there a better way than doing something like
TransfersController
...
def index
if !params[account_id].nil?
#account = Account.find(params[account_id])
#transfers = #account.transfers
elsif !params[user_id].nil?
#user = User.find(params[user_id])
if #user.accesible_by?(current_user)
#transfers = #user.transfers
end
elsif !params[projects_id].nil?
.....
and the same holds for views - although they all will list transfers they will have very different headers, navigation etc for user, account, project, ...
I hope that you see the pattern from this example. I think there should be some non-ugly solution to this. Basically I would love to separate the logic which selects the transfers to be displayed and other things like context specific parts of view.
I've got an open question on this. In my question I outline the 2 methods I came up with. I'm using the second currently, and it's working pretty well.
Routing nested resources in Rails 3
The route I'm using is a bit different because I'm using usernames in place of the IDs, and I want them first. You would stick with something like:
namespace :projects, :path => 'projects/:project_id' do
resources :transfers #=> controllers/projects/transfers_controller.rb
end
# app/controllers/projects/transfers_controller.rb
module Projects
class TransfersController < ApplicationController
# actions that expect a :project_id param
end
end
# app/controllers/transfers_controller.rb
class TransfersController < ApplicationController
# your typical actions without any project handling
end
The reason I use the namespace instead of a call to resources is to have Rails let me use a separate controller with separate views to handle the same model, rather than pushing all the nasty conditional logic into my controller actions.

Resources