Confusing routing issue - ruby-on-rails

I'm working on a project management web app and I have to pages for viewing projects. One is for viewing all the projects, and the other one is for management of of the project the user owns (i.e is administrator).
As it is now one can reach the overview page for projects by the use of 'projects_path' (/projects). However for the project management page I want to have another url, 'projects/manage', and it's here I need help.
I have tried the following:
routes.rb:
match "/projects/manage" => "projects#manage", :as => 'manage_projects'
view:
<%= link_to "Manage projects", manage_projects_path %>
Which raises the following error:
Couldn't find Project with id=manage
app/controllers/projects_controller.rb:62:in `show'
Why do it direct me to the action 'show' when I've explicitly set it to direct me to 'manage' (projects#manage)? Apparently it want an 'id', which shouldn't be the case here because I want to show all the projects (that the user owns), not a specific.
How can I solve this?

If you are performing the manage action on multiple projects it is better to write collection action in following way,
resources :projects do
collection do
get "manage"
end
end
This will provide you the route /projects/manage, will automatically match the route to manage action and every thing will be as per the REST conventions.

When your Rails application receives an incoming request
GET /projects/17
it asks the router to match it to a controller action. If the first matching route is
match "/projects/:id" => "projects#show"
the request is dispatched to the projects controller’s show action with { :id => “17” } in params.
Similarly, When your Rails application receives an incoming request
GET /projects/manage
it asks the router to match it to a controller action. If the first matching route is
match "/projects/manage" => "projects#manage", :as => 'manage_projects'
the request is dispatched to the projects controller’s manage action without bothering about the id as not given in routes.
But if the first matching route for projects is resource itself for projects, then it will go the show action as it will treat manage as your id just like having /projects/:id and match will get skipped.
So it depends what have you given first i.e. a resource or a match. Priority is important.

This line
match "/projects/manage" => "projects#manage", :as => 'manage_projects'
is most probably added after resources :projects. Move that line above the resources one and you should be good. something like
match "/projects/manage" => "projects#manage", :as => 'manage_projects'
resources :projects
UPDATE:
Routes are ordered. The routes at the top of the file takes precedence. If resources :projects is before match "/projects/manage" => "projects#manage", :as => 'manage_projects', going to /projects/manage goes to the show action of the projects controller because it matches /projects/:id before /projects/manage

Related

rails link_to using get instead of post

I'm making a website for a class and I'm trying to implement a friend request function with a model called 'Users' and a join model called 'Relationships'. I have a button on the user#show page that should add a friend by using the create method in the Relationships controller. Here is the code for the button:
<%= link_to "Add as Friend", relationships_path(:friend_id => #user), method: :post %>
When I press the link, however, it tries to access the index method instead. After looking in the console, it looks like the link is sending a GET request, which routes to the index method, instead of a POST request, which routes to the create method. Can someone explain why this error is occurring and how I can fix it?
Edit: As requested, here is what I have in my routes:
Rails.application.routes.draw do
resources :interests
get 'interests/create'
get 'interests/destroy'
get 'home/index'
get 'sessions/create'
get 'sessions/destroy'
resources :users
resources :relationships
resources :subscriptions
# The priority is based upon order of creation: first created -> highest priority.
# See how all your routes lay out with "rake routes".
# You can have the root of your site routed with "root"
# root 'welcome#index'
root 'home#index'
get "/auth/:provider/callback" => "sessions#create"
get "/signout" => "sessions#destroy", :as => :signout
Using a link_to helper indicates to Rails that you'd like to produce an a tag in your HTML. No element of the HTML specification regarding a tags allows for producing POST requests. Because Rails understands the utility of allowing for POST and DELETE requests to be issued using links, however, it provides those options in the link_to helper. It's implementation, though, must use JavaScript under the hood in order to appropriately function.
Check that jquery-ujs is installed, and that your asset pipeline is working correctly in order to use the helper in this way.
You may also evaluate whether using a form_for and a button is better, since that will automatically POST.
I'm pretty sure you are matching the wrong route. Run rake routes and see the route that links to the Relationships#create.
Using 'url' instead of 'path' with the route helper solved the problem for me. So instead of 'relationships_path' use 'relationships_url'.

Ruby on Rails multiple methods per page in routes.rb?

Stupid question... I have two forms with two different functions on one page, my views/projects/new.html.erb file. So far I've only implemented one, with the option to "Create" a new project. I want to add another function to sort the records displayed on the same page, something like:
<%= link_to "Category", { :controller => "projects", :action => "sortTable", :filter => "Category" }, :remote => true %>
--
My routes.rb file:
Docside::Application.routes.draw do
resources :projects
resources :categories
#get "home/index"
root :to => "projects#new"
match 'project/new',:controller=>"projects",:action=>"create"
end
But I'm getting the error "No route matches {:action=>"sortTable", :controller=>"projects"}". When I tried adding " match 'project/new',:controller=>"projects",:action=>"sortTable" " my other function didn't work, and the create function got screwed up. What should I have instead?
Try that:
resources :projects do
collection do
post :sortTable
end
end
And look at this guide
You can only have one route for a given path and method combination. You're trying to define multiple routes on the same path, so only one of these will work (the first one). You should be ok if you use distinct paths for each of these actions (instead of project/new for all of them. Beware of collisions with your existing routes)
You'll also make you life easier if you stick to rails' conventions (and the code will be easier to read if someone else starts working on it). For example resources :projects already creates a route for the create action. Additional actions can be added like so
resources :projects do
collection do
get :sort_table
end
end
Sets up a collection route (ie one that isn't about a specific project) for the sort_table action and sets up a URL helper for you (sort_table_projects_path). There are alternative syntaxes you can use - I encourage you to have a look at the routing guide

How to add multiple new/create routes in Rails 3

I have a client controller and views which already functions for all the default actions. How do I add a new_account and create_account actions that also work with routing?
The idea is... there are new clients, but some clients can also function as an account that logs into the website. I want to prompt for different fields based on whether a client is being created or a login account is being created. I don't want two separate models with duplicate information.
client_controller.rb - index, new, create, edit, update, destroy, new_account, create_account
client views - I have views for each of the actions within the controller.
Routing - ../new_account should display the new_account view for the client model, not the new view.
Hopefully this makes sense. I'm guessing this isn't difficult but I'm just missing how.
Try:
match 'new_account', 'client#new_account', :via => :get
match 'create_account', 'client#create_account', :via => :post
View all your routes by running rake routes.
Refer to more info on routing here: http://guides.rubyonrails.org/routing.html
Put this in your routes.rb
resources :clients do
new do
scope type: 'account' do
get :account, to: 'clients#new'
post :account, to: 'clients#create'
end
end
end
and the actions you need will be accessible at GET|POST /clients/new/account.
In the controller you'll have params[:type] to indicate this specific case.

How to remove controller name in REST design in Rails3?

Given a User resource, it goes like this
/user/:shortname
But how can the controller name be removed to get just
/:shortname
How can I declare this in routes.rb while keeping all CRUD functionality instant?
Updated: After reading this I'm moving to Sinatra over Rails to handle this API-like design better.
Define a custom match:
match ':shortname' => 'users#action'
Replace action in users#action with the name of the action that is supposed to receive the request. Just remember to place it in the appropriate order in your routes file. Rails looks at each line of your routes file starting at the top and selects the first matching route. ':shortname' would match any first-level path, including /users! So put it below any routes using a first-level path, which would include all of your resource routes. Here's an example:
resources :users
resources :posts
match '/blog' => 'posts#index'
match ':shortname' => 'users#action'
In routes, you should be able to do something like
resource :users, :path => '/:shortname'
Try that out and rake routes to see if that comes out as expected.

Rails3: Appropriate use of routing and resources

I've recently joined the world of Rails app development (Rails3) and I may be abusing resourceful routing.
The default resourceful routing makes some really convenient helper methods for the URLs which I use constantly. My problem is that I have controllers that I specified the routing as resourceful simply to take advantage of those helper methods. I have some basic site navigation that has no business with resources.
resource :home do
member do
get 'main'
get 'about'
get 'login'
get 'help'
end
end
Is there a better way to do what I've been doing? Anything that doesn't require that I manually add routing entries each time I have a new controller action?
Just to clarify, I want to specify routing for a controller without having to explicitly add any new actions but I also want it to auto-generate helper methods. So far, I have to explicitly add routes for each action I want that for. I can get something similar by doing this (in a non-resourceful way),
match 'home/about' => 'home#about'
But I don't want to have to write that very every route that doesn't fall into the convention.
Here's another simpler one. Just add a generic route to the bottom of your routes.rb
match ":controller/:action"
and it will map directly to the specified action of the specified controller. You can be a bit more specific if you like. For example, using get instead of match to restrict to HTTP GET requests, specifying the applicaple controllers etc.
get ":controller/:action", :constraints => { :controller => /home|help/ }
You can look into your controller for public instance methods and generate routes automatically.
# routes.rb
HomeController.public_instance_methods(false).select{|m| !(m.to_s =~ /^_/)}.each do |m|
match "home/#{m}", :action => m, :controller => HomeController, :as => "home_#{m}"
end
This will take the explicit(non-inherited) public instance methods from your controller, and select the ones that don't begin with an underscore(because underscored ones are generated methods for filters, the rest are actual actions). Then it will generate a named route for each.
Keep in mind that routes.rb is processed only at server startup so you will have to restart the server after you add new actions.

Resources