Recursive routes in Rails - ruby-on-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).

Related

Ruby on Rails page control

I'm sorry, my English is bad. Question: I have model Pages with columns title, description etc. I can create, change, destroy these pages. I can see the contents of the link mydomain/pages/1. I need for each page has been template and route, so I can see the content on the link, for example maydomain/contacts. How to do it? Help me please.
One way to implement your own solution is to add this to your routes file:
get '/mydomain/:slug, to: 'pages#show'
This is a pretty general matcher, so add it to the bottom of your routes so it doesn't override others.
Then your controller show action will look something like:
def show
#page = Page.find_by_slug(params[:slug])
end
This of course assumes you have a slug column on your Pages table.
I'm assuming by "mydomain" you mean the root url of your site (e.g. myapp.example.com)
I'd suggest that you separate the problem into two parts:
Use an attribute other than id to identify an item in the url
Reduce the route so that the controller does not need to be specified.
For 1, have a look at this: Rails routes with :name instead of :id url parameters Note, that as #spickermann suggests friendly_id could be a good solution for you.
For 2, you will need to create a route without the controller name, and then specify the controller in the route definition. (See the Rails Routing Guide):
get ':param', to: :show, controller: 'pages'
For that to work, you will need to put it after (lower in routes.rb) so that it doesn't intefer with other routes. I'd also recommend adding a constraint to the route - to limit the wrong urls that could be routed to that rout.

Self nested infinite routes

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.

Ruby on Rails database driven router to support fully customizable urls

I'm planning to port our current cms (written in PHP) to Rails. All parts do well, except for one: routing.
Like most cms systems, the routing in our cms based on a database with pages, which are linked to modules, controllers and actions. On this approach a user can fully customize or specify it's own urls.
I know that Rails (and most (application) frameworks have the approach of defining routes in a file, but I hope this is possible.
The approach our users should have is:
add new page
select type (news, form, product, ...)
select an item (select which form, blog or product should be displayed)
enter a url for that page
Special the last point (4) is important. A user should be able to add form A to /contact-us, and form B to /clients/register-as-new-client e.g.
On a request the router needs to do a database query with the page url, to find out which controller, task and parameters should be dispatched.
Question has been updated, and i don't think this is a valid answer anymore
we have a similar paging system. we use a routing glob. in routes.rb:
get 'pages/*lookup_path', to: 'pages#show', defaults: { format: 'html' }, as: 'page'
Just parse params[:lookup_path] in PagesController to suit your needs
'http://localhost/pages/users/'
params[:lookup_path] #=> users/
'http://localhost/pages/users/23'
params[:lookup_path] #=> users/23
'http://localhost/pages/people/1'
params[:lookup_path] #=> people/1
Although this solution isn't ReSTful, I think this should solve the issue.
Regardless, Rails uses routes in a file. You cannot change this since the framework heralds "convention over configiuration". All I can do is point you in a direction to minimize this.
There is a catchall route in Rails (on RailsCasts, and on StackOverflow) which you can use to direct all routing to one controller action. You may further customize the routing behaviour in that method
You could also make a route like…
:controller/:action => Controller::Action
…as is done in CodeIgniter, but now your methods have to have names like contact-us and register-as-a-new-client.

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.

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