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

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

Related

Rails: in controller, a new view is always rendered by `show`?

I want to implement a search function in the controller, which contains "show, new, create, etc..."
I added in route.rb:
get 'apps/search' => 'apps#search'
and in apps_controller.rb:
def show
#app_info = App.find(params[:id])
end
def search
# get parameter and do search function
end
but each time when i request the /apps/search?xxx=xxx then it will be rendered by show... and then search?xxx=xxx is the parameter for method show...
should I rather create a new controller for search? Or is it possible to implement search as my requirements?
Your routes are incorrectly prioritized - somewhere else in your routes file (before the get 'apps/search' line) you have resources :apps, which defines a route that matches the same regex as apps/search.
Routes match from top to bottom, so if you check the output of rake routes, you'll see that your request to apps/search is actually matching apps/:id - which is the show route of your apps resource.
Either move the apps/search route above the resources :apps declaration, or alternatively declare your search route as part of the apps resource, eg.
resources :apps do
get :search, on: :collection
end
(this will define apps/search in the way you want).
For more information on routing: http://guides.rubyonrails.org/routing.html
I think you should edit route.rb as the following:
get 'apps/search' => 'apps#show'
The Rails' way to "say" search is a new route to the apps controller is using collection. So, for example, supposing you already have a resources :apps, you can do:
resources :apps do
get 'search', on: :collection #or you can use another HTTP verb instead of get
end
And that would give you:
search_apps GET /apps/search(.:format) apps#search

Confusing routing issue

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

Shallow routing in Rails 3

Having a rough time with my routes in my rails 3 app, I want to have shallow routes like this:
/san-francisco/union-square
But my router insists on having them like so:
/cities/san-francisco/neighborhoods/union-square
I've used this for my routes.rb
shallow do
resources :cities do
resources :neighborhoods do
resources :locations
end
end
end
But still I have this:
city_neighborhood_locations GET /cities/:city_id/neighborhoods/:neighborhood_id/locations(.:format)
Shouldn't it look like:
city_neighborhood_locations GET /:city_id/:neighborhood_id/:id(.:format)
I'm not sure how to fix this, additionally I'm not sure what I'm doing wrong with my links, I want to be able to use the syntax:
link_to neighborhood.name, [:city, neighborhood]
but that seems to invert the :id, and :neighborhood_id when the request comes to the controller, any help on this would be really really helpful!
What you're looking for is not typically known as "shallow routing".
Shallow routing, as Rails defines it, would look like this:
city_neighborhoods GET /cities/:city_id/neighborhoods
new_city_neighborhood GET /cities/:city_id/neighborhoods/new
(create_city_neighborhood) POST /cities/:city_id/neighborhoods
neighborhood GET /neighborhoods/:neighborhood_id
Without shallow routing, that last route would be:
city_neighborhood GET /cities/:city_id/neighborhoods/:neighborhood_id
Shallow routing lets you nest a resource underneath another resource (neighborhood under city in this case), but gives you absolute/un-nested routes for the nested resource when the nesting isn't necessary.
This makes sense when you're referencing the nested resource with unique identifiers that are not dependent on the ID of the parent resource. In your example, that's not true (there's potentially a "Union Square" outside of San Francisco; there's definitely going to be duplicates like "Chinatown"), so you probably do not want shallow routes for this case.
What you're wanting is positionally-dependent routing, where the type of resource is assumed/fixed depending on where it appears in the URL. (For instance, you couldn't have anything other than a "neighborhood" follow a "city" under the scheme you outlined.)
I don't think the Rails resource(s) commands will support that by default, but you could probably do it with manual match commands. This is off the top of my head:
match ":city_id", :controller => "cities", :action => "show"
match ":city_id/:neighborhood_id", :controller => "neighborhoods", :action => "show"
This is still RESTful/resource-based, it's just not using the standard Rails way of naming routes.

Router for nested resources in a "not usual" Ruby on Rails way

I am using Ruby on Rails 3.0.7 and I am trying to set nested resource routing to make it to work in a "not regular" RoR way.
In my routes.rb file I have
resources :articles do
resources :categories, :only => [:index], :controller => 'articles/categories' # The related controller is Articles::CategoriesController
end
so that I can browse following URLs:
<my_site>/articles/1/categories
<my_site>/articles/2/categories
...
What I would to do is to access new, edit and show controller actions for categories by using the same articles/categories controller used for the nested resource stated above (that is, Articles::CategoriesController) and by accessing these URLs:
<my_site>/articles/categories/new
<my_site>/articles/categories/edit
<my_site>/articles/categories/1
<my_site>/articles/categories/2
...
How can I do that? How I must code the router?
Maybe I can do something by using the router collection method like this
resources :articles do
collection do
# match something here for the Articles::CategoriesController...
end
resources :categories, :only => [:index], :controller => 'articles/categories'
end
but I don't know how to do that.
I'm not real sure what you're trying to do with those routes, so I'm not quite sure how to answer your questions. If your intent is to be able to add a new category for a particular article, or edit all the categories for a particular article, you have to pass an ID for the article. If you're trying to create a new article and a new category all at once, you don't need category in the route, just the article and you can do something like form_for([#article,#category]) in your form and use the build method in your controller. If you can clarify, I might be able to help you further (in other words, it's not hard to construct those routes -- but it depends on what you want to do with them.

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.

Resources