Self nested infinite routes - ruby-on-rails

Is there a way to self nest a resource in the routes infinetelly?
Suppose the following scenario: I want to create several pages that may be self nested one to another, like a products page has many product pages and each product page has several sub-pages etc.
The resource would be a page in a tree structure , like with the awesome nested set gem.
In case an admin can create pages at unknown depth level how would I make the routes?
The above example has to produce a url like /:friendly_id_of_level_1/:friendly_id_of_level_2/.../:friendly_id_of_level_n
I've tried going with dynamic routes but it has many drawbacks this way.
Any suggestions?

I do a similar thing to this in one of my apps with this route:
map.connect "/c/*modules", :controller => "content", :action => "show"
(Note this is using rails2 routing syntax, you might need to update it).
This will resolve this url
/c/123-foo/456-bar/789-baz/653-qux
to the content#show action, with params set like
params = {"modules"=>[123-foo", "456-bar", "789-baz", "653-qux"]}
The modules are in a tree structure, so i can use the sequence of module ids in params[:modules] to make a breadcrumb chain and any other heirarchical data, and i use the last one in the array as the "current" one to actually show to the user.
Note: I put the "/c/" at the start of the url to separate these nested routes out from all my other routes: otherwise it's very greedy and will match pretty much any url on your site. That's not a problem if you want to always have it as your "catchall" route down at the bottom of your routes file, but if not then you'll need to add something to make it distinct. Obviously this doesn't need to be "/c/", you could have anything which will stop it colliding with your other routes.

Related

Rails Controller - getting the index action to display only certain records

By default the rails controller will load all associated objects in the index action. What I would like to do is display only certain objects.
For example
I have a model called Car(id, make, model, year). I want list only particular makes in the index, depending on a parameter.
There are a few ways to do this, I'm just not sure which is best.
I could:
pass a parameter to the link:
cars_path(make: 'Acura')
and would give me /cars/?make=Acura
set up routes: (this seems to get messy)
match "cars/:make" => "cars#index", constraints: {make: /[A-z]{1,20}/}
or I could make a separate controller action for this
Any suggestion about what is the most "rails-y" way to do this? RoR 3.1
Usually, when we are talking of filtering data, I prefer to keep the same index action and filtering parameters via plain old GET vars (no extra route definitions) url?key=val&key-val.
This has a number of benefits among them:
url is bookmark-able
no session tinkering
I can reuse the filtering params and pass them to pagination links and such to have the filter follow the user while search is in order
I prefer not to make extra routes as the complexity of the filter can easily go too high. If the filter params are few and you are sure of what you are doing, you may define extra nice routes url/param/param but I find that those cases are few to none.
If you just want to display the cars of one make, the best url imo would be: /makes/1-Acura/cars. So you would just get the cars of this make in the cars controller.
Do you have a table for makes or is it just a string in your car table? I think you should have one.
resources :makes do
resources :cars
end
With these routes, you would have to test if there is a params[:make_id] in the index action of the cars controller, and if it's the case you would get the cars like that:
#cars = Make.find(params[:make_id]).cars
Or you could set up your routes like that
resources :makes do
scope :module => "make_scope" do
resources :cars
end
end
This way, you can have your controllers setup like that:
controllers
- cars_controller.rb
- make_scope (folder)
- cars_controller.rb
The path make_cars_path(#make) would hit the index action in the make_scope/cars_controller, so you would not have to worry about the presence of a params[:make_id], you would just know you're working with the cars of a make.
Otherwise, the get params are fine. I don't think it's bad to define a new route to get prettier urls though, depending on the complexity of your filters.

REST Rails 2 nested routes without resource names?

I'm using Rails 2.
I have resources nested like this:
- university_categories
- universities
- studies
- professors
- comments
I wish to use RESTful routes, but I don't want all that clutter in my URL. For example instead of:
/universities/:university_id/studies/:study_id/professors/:professor_id
I want:
/professors/:university_id/:study_id/:professor_id
(I don't map professors seperately so there shouldn't be a confusion between this and /professors/:professor_id since that route shouldn't exist).
Again, I want to use RESTful resources/routes...
Also note, I am using slugs instead of IDs. Slugs for studies are NOT unique, while other are. Also, there are no many-to-many relationships (so if I know the slug of a professor, which is unique, I also know which study and university and category it belongs to, however I still wish this information to be in the URI if possible for SEO, and also it is necessary when adding a new professor). I do however want to use shallow nesting for "administrator" URIs like edit, destroy (note the problem here with Study since it's slug is not unique, though)...
I would also like some tips on how to use the url helpers so that I don't have too much to fix if I change the routes in the future...
Thank you.
It doesn't seem like map.resources will provide you with this functionality, but you could use something like (untested)
map.show_professor "/professors/:university_id/:study_id/:professor_id", :controller => "professors", :action => "show"
and then similar routes for the other actions.
There might be a better solution, but this is the only way I can find that would work, since it seems map.resources assumes it is in the form of /resources/(:resource_id)
You can use REST this way, you just have to do all the actions yourself instead of using the shortcut.
As an example of an edit, you can just use
map.edit_professor "/professors/:id/edit", :controller => "professors", :action => "edit"

Rails 2 Trailing Route Parameter

I'm building an article/blog website where any article may show up within a series of URL paths.
/section/article
/section/page/article
/section/page/page2/article
This works, but what if I wanted to have the page number for that article (/page/123) be bound to those URLs
/section/article/page/123/
/section/page/article/page/123
/section/page/page2/article/page/123
This would mean that I would have to create a specific route for each different url?
/:section/:page/:sub_page/:article/page/:page
This would mean that I would create dozens of URL routing paramters.
Is there anyway in rails to say that all urls may have a /page/NUMBER suffix at the end of the URL and still route normally (that is assign the NUMBER to a parameter and continue to goto the page normally)?
Route globbing, described at http://guides.rubyonrails.org/routing.html#route-globbing, might work in this situation. For example, your route might read map.connect '/:section/*page_subpage_path/page/:number', :controller => 'articles', :action => 'show'
This exact code might not work as intended, but this method might be a good direction to try. Good luck :)
If you want to create routes that are as customized as that you normally need to create a large number of routes to accommodate them. The general format is /resource/:id when using map.resource, anything other than that is left to you to specify.
Given that the Rails routes.rb file is executable ruby you can often define your routes programmatically by repeating patterns or doing combinations on arrays if required.

Recursive routes in Rails

Is is possible to create a recursive route in Rails?
I have an application, which allows a admin to create pages. The page model is a nested set and so each page has a parent_id hence the pages are structured in trees. The page model also uses the Friendly ID plugin to provide slugs for each page.
When a user browses the site I would like them to see the nesting structure in the urls - its better for search engine purposes as well as any users who might like to browse the site by chopping the urls.
I want something along the lines of:
http://example.com/page/page/page/page ...etc
Now obviously I can create a nested map with say 10 nests and hope that no site exceeds that limit, but I'm curious if there is another way...
You can map the initial route (/page) to the controller, setting up "globbing" for all the trailing parameters.
map.connect '/:page/*pages', :controller => 'pages', :action => 'show'
params[:pages] will now contain an array of the page parameters (matching as many trailing params as you specify in the URL).

Routing Question in Rails

How would i go about routing www.homepage.com/site/about to www.homepage.com/about for example? I still would like to use "site" as a controller, I just want to remove it from the route.
On a separate note, ss this how you usually setup routes for pages like about and contact that appear right after the site name?
For the first question, you could put something like this in your routes.rb:
map.about '/about', :controller => 'site', :action => 'about'
As for the second question, i don't quite undestand it, can you be a little more specific?
On a separate note, ss this how you usually setup routes for pages like about and contact that appear right after the site name?
If you follow the principles of REST (and you probably should), then by and large every URL should be terminating in a "resource" (ie: a noun, a "thing"). Each resource in turn has a controller, and these controllers have a standardized and limited set of actions.
This is a bit different from the "classic" routing scheme, where a controller would have more varied actions (and thus you could lump more functionality into a single, larger controller)
So, for example, in a RESTful system:
/contact would map to a ContactController, and not a "contact" action in some other controller (as it could in classic routing)
/about would similarly map to an AboutController.
/site/about would map to an AboutController, but this would be a "nested" route under the Site namespace. If you want to get hardcore about the nesting (and I typically do), I would:
Put AboutController in the Site module (thus Sites::AboutController), which in turn is stored in the file /app/controllers/site/about_controller.rb
Create a SiteController
Map /site to SiteController
If you had a preexisting Site:AboutController mapped to /site/about and you wanted to remap it to /about, you could do so using #rbaezam79's method. However, I'd seriously consider relocating and renaming the class itself just for consistency.
All of the resources I listed above would probably be considered "singleton" resources. This means:
you use map.resource instead of map.resources to map your routes
the default action is "show" (and not "index" as it is with non-singleton resources). Typically this will be the only action you need. (You wouldn't ever want to create or delete your about page, would you?)
the names are typically singular instead of plural. (Although I've run into some stumbling blocks with this)
When setting up routes like this, be sure to run the command "rake routes" regularly; this will show you exactly what you're getting at any given time.

Resources