Rails routes.rb syntax - ruby-on-rails

I have searched and searched and I cannot find a page which spells out the syntax of routes.rb in Rails 3. There are guidelines, overviews, even advanced examples but why isn't there a page that spells out the exact syntax of each keyword?? This page
http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/
contains a lot of advanced examples but doesn't take the time to discuss the behavior of all the examples given. I would appreciate it if someone could point me to a page that breaks down the syntax of routes.rb.
Here is the problem I am trying to solve. I have two models modelA and modelB. The relationship is modelA has_many modelB and modelB belongs_to modelA. I created the controller for modelB under namespace of modelA. So in my rails app folder, I have
app/controllers/modelA_controller.rb
app/controllers/modelA/modelB_controller.rb
I want my routes to be as such:
http://localhost:3000/modelA/:modelA_id/modelB/ [index]
http://localhost:3000/modelA/:modelA_id/modelB/:modelB_id [show]
etc.
I tried the following in routes.rb and none of it works:
resources :modelA do
resources :modelB
end
--
resources :modelA do
member do
resources :modelB
end
end
--
namespace :modelA do
resources :modelB
end
--
match '/modelA/:modelA_id/modelB/action', :to => '/modelA/modelB#action'
I know some of the things I tried are obviously wrong but when you have spent 2 days on a single problem, anything goes!

The reason no one has a "definitive" guide on routing syntax is that it is pretty flexible so you could probably write a few chapters just on that one subject. However, I would recommend: http://guides.rubyonrails.org/routing.html
From your question, it sounds like you're namespacing modelB under modelA but you also want the id for modelA to be within the route itself.
So if your ModelBController looks something like:
class ModelA::ModelBController < ApplicationController
# controller code here
end
then you can just do:
resources :modelA do
resources :modelB, :module => :modelA
end
However, are you sure you want to namespace the controller like that? If you just want nested resources like a typical has_many relationship, you don't need to be namespacing modelB under modelA.
Instead, you'd have:
/app
/controllers
/modelA
# some files
/modelB
# some files
And your modelB controller would be:
class ModelBController < ApplicationController
# controller code here
end
Then you could do
resources :modelA do
resources :modelB
end

Related

Handling Rails routes/models with belongs_to relationship

So this may be more of a convention question, but im writing a todo app to learn how to use rails as an API (Im somewhat intermediate with using rails normally) but this time im using it with React for the front end.
Im making a simple todo app, two models in particular being "Lists" and "ListItems". Lists has_many ListItems of course, and a ListItem belongs_to a List.
So naturally I have the routes set up like so:
resources :lists do
resources :list_items
end
Giving me routes similar to: /api/v1/lists/:list_id/list_items etc.., However I saw some people doing a similar app set it up like:
namespace :v1 do
resources :list_items
resources :lists
end
Which confuses me because how would you handle passing the actual "List" params to the route, when the route itself would not have a List_id param?
Or would this more be used for a join table somehow..but you would still have to populate the List_id regardless when creating a list_item for a specific list correct?
Is there a preferred way of doing this as far as routing goes? (And I suppose creating tables?) Since a has_many_through seems not really necessary in this case?
Unless there is more to the story, you are doing it the more conventional way. I suggest your can safely disregard that not-nested approach. The only enhancement I suggest is using shallow: true, like:
namespace :api do
namespace :v1 do
resources :lists do
resources :list_items, shallow: true
end
end
end
You can read more about shallow nesting in the guide.

Self nesting rails categories

I have an store application, where I need to make custom routing system where URL stores categories for products. For example, http://example.com/languages/ruby/rails will display category#show named 'rails', that has parent named 'ruby', that has parent named 'languages' and and URL of http://example.com/languages/ruby/rails/store will display product in this category.
Currently I have:
category.rb
belongs_to :parent, class_name: 'Category'
has_many :categories, foreign_key: :parent_id
has_many :products
routes.rb
resources :categories, :path => '', :only => [:index, :show] do
resources :products, :path => '', :only => [:show]
end
root :to => 'products#index'
but it still stacks up to 2, e.g. URL http://example.com and http://example.com/languages shows list of categories/subcategories, but http://example.com/languages/ruby have params: {"action"=>"show", "controller"=>"products", "category_id"=>"language", "id"=>"ruby"}
Removing products from routes does not help at all - then it just says that No route matches [GET] "/language/ruby", although I assume It might cause need for extra check if current URL point on category or product later on.
Also I tried get '*categories/:id', to: 'category#show' variations
+ I am using friendly_id gem so that path do not look like http://example.com/2/54/111/6
I just want to find out what is the best ruby on rails solution for this kind of situations, when you need search engine optimizations + endless (e.g. no way to define how deep such recursion can go) nested resources that nest themselves (including fact that category/language/category/ruby/category/rails just looks ugly).
Note: most information I used is taken from Stack Overflow and railscasts.com (including pro/revised episodes), so mentioning a good source with information like this will be great too.
I solved this myself recently with a CMS I built on Rails recently. I basically construct the routes dynamically at runtime from the database records. I wrote this blog post on the strategy:
http://codeconnoisseur.org/ramblings/creating-dynamic-routes-at-runtime-in-rails-4
The core of the solution (adapting the blog post above) is simply iterate over the database records and construct the routes needed for each category. This is the main class for doing that:
class DynamicRouter
def self.load
Website::Application.routes.draw do
Category.all.each do |cat|
get cat.route,
to: "categories#show",
defaults: { id: cat.id },
as: "#{cat.routeable_name}_#{cat.name}"
end
end
end
def self.reload
Website::Application.routes_reloader.reload!
end
end
For the above, the Category model should implement a "routeable_name" method which simply gives an underscored version of the category name that uniquely names that category's route (its not strictly necessary, but helps when doing "rake routes" to see what you have). and the #route method constructs the full route to the category. Notice the defaults which sets the ID param for the category. This makes the controller action a very simple lookup on the category's ID field like so:
class CategoryController < ApplicationController
def show
#category = Category.find(params[:id])
end
end

Nested routes without inadvertent top level routes

I have 3 resources that are structured in a hierarchy as follows:
house 1-* residents 1-* books
We know that its not great to have deeply nested routes, so we endeavour to have routes nested at most 1 resource deep. Defined something like:
resources :houses do
resources :residents
end
resources :residents do
resources :books
end
The problem is that we end up defining :residents as a resource that can be accessed without any nesting - as part of the definition of :books. Is there way to define :books as being nested in :residents, without inadvertently registering :residents as a top level route?
This can be accomplished by using scopes, in your case:
scope 'residents/:resident_id' do
resources :books
end
However, as I mentioned in the comment, this practice can end up confusing users who expect URLs to behave in a certain way (deleting the last bit takes them up a level).

Generated scaffold's view links are not corresponding correctly to the routes map

I have a problem with generated view paths. My routes.rb looks like following
Project::Application.routes.draw do
resources :project_templates do
resources :awards
end
...
project_template.rb like this
class ProjectTemplate < ActiveRecord::Base
belongs_to :user
has_many :awards #...
attr_accessible :user_id #...
...
award.rb like
class Award < ActiveRecord::Base
belongs_to :project_template
attr_accessible :tier #..
...
And generated view links are like this: awards_path
This way app does not work and I need to replace all with project_template_awards_path
I don't know why generator did this without project_template prefix but I ask for you to help me find a way to get around this. Maybe there is some generator command that will add up the missing suffixes to paths? I have to do the same with another class requirement.rb and there are views for that too so I hope there is some magic command for solving my issue.
rake routes | grep awards gives following output:
project_template_awards GET /project_templates/:project_template_id/awards(.:format) awards#index
POST /project_templates/:project_template_id/awards(.:format) awards#create
new_project_template_award GET /project_templates/:project_template_id/awards/new(.:format) awards#new
edit_project_template_award GET /project_templates/:project_template_id/awards/:id/edit(.:format) awards#edit
project_template_award GET /project_templates/:project_template_id/awards/:id(.:format) awards#show
PUT /project_templates/:project_template_id/awards/:id(.:format) awards#update
DELETE /project_templates/:project_template_id/awards/:id(.:format) awards#destroy
Could you try typing $ rake routes in project directory and post there output? Does it still show awards_path for Awards resources?
PS. Controllers have nothing to do here, as you can create route resources without corresponding controllers in app/.
The routes generated are correct. If you want to have this helpers, you can either add this to your route.rb
Project::Application.routes.draw do
resources :project_templates
resources :awards
end
Or implement helpers like
def awards_path(award)
project_template_awards(awards.project_template, awards)
end
More info on ruby guides

Rails RESTful delete in nested resources

Okay, so here's an example scenario. There is a student resource resources :students, and students has and belongs to many collections: resources :clubs, resources :majors, etc.
So we can set up our routes easily enough...
resources :clubs do
resources :students
end
resources :majors do
resources :students
end
resources :students do
resources :clubs
resources :majors
end
which generates us a bunch of standard RESTful routes
/clubs
/clubs/:id
/clubs/:club_id/students
/clubs/:club_id/students/:id
/majors
/majors/:id
/majors/:major_id/students
/majors/:major_id/students/:id
/students
/students/:id
/students/:student_id/clubs
/students/:student_id/clubs/:id
/students/:student_id/majors
/students/:student_id/majors/:id
So here's my question. With REST semantics, how would one delete a major for a student?
Browsing students under a major /majors/:major_id/students/:id would show that student in the specific major's 'collection'. But the route for deleting an :id, points to StudentsController#destroy, which would delete the student completely. Whoops! So maybe we go the other way, and perform DELETE on the resource at /students/:student_id/majors/:id and well now UnderwaterBasketweaving is now no longer offered at this school...Whoops!
Now granted, we could set the destroy method of ClubsController, MajorsController, or StudentsController to look for club_id, or major_id, or student_id, but lets say we also down the road want to add Fraternities and GraduatingClasses, etc. Each class would start to be comprised of huge switch conditionals looking to see what param was present... and then find the collection of either the top resource and remove the bottom resource, or vise versa. The Models themselves should decide whether they delete themselves if they have no more association records... 'Destroy' on that resource has become really a misnomer...
Is there an easier way to do this? Even popular restful rails plugins like make_resourceful or resource_controller would blow away UnderwaterBasketweaving when removing it from Joe's Majors or completely delete JohnDoe when removing him from the major UnderwaterBasketweaving. It would seem there's a potential for looking at the association to understand the desired effects of the semantics and what 'destroy' should do.
Then again, am I looking at this all wrong? Is it not UnderwaterBasketweaving -> Joe but UnderwaterBasketweaving+Joe as a single resource and what we're deleting is truly not Joe, nor UnderwaterBasketweaving, but the resource representing the combination? However, that's not easy when the controllers are Students and Majors, that in effect represent resources of the same name (MVC has really become RV...in the 'convention' approach and not developing Controllers that may have no bearing on a Model name, or the path to reach it) So you'd be deleting a Major or a Student; pick your poison...
How can I avoid managing conditionals across an infinite graph of associated resources where delete really isn't the intent when the delete is desired to be in context of a collection and not pertaining to its singularity...?
...major.student.delete... Is there a way for 'student' ActiveRecord object to know it was sent a 'delete' message in a method chain beginning with the 'major' AR object ?
Well the standard RESTful aproach is to use has_many :through and generate a cotroller for the association resource. Naming association resources is always difficult, but I'll attempt for this example.
resources :majors do
resources :studies
resources :students
end
resources :students do
resources :studies
resources :majors
end
The models would be of course:
class Major < ActiverRecord::Base
has_many :studies
has_many :students, :through => :studies
end
etc. (comment if you want me to elaborate)
Then for a student you wouldn't DELETE it's associated #student.major but his #student.studies.where :major => #major.

Resources