Rails 2 Trailing Route Parameter - ruby-on-rails

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.

Related

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.

Rails 4 routing: using namespaces with dynamic segments

When discussing Routing using Dynamic Segments, the Ruby on Rails Guides (v4.0.1) say "You can't use :namespace or :module with a :controller path segment." They go on to suggest: "if you need to do this then use a constraint on :controller that matches the namespace you require."
I've got a lot of controllers under quite a few namespaces. If I could get dynamic segments to work in my situation then I could handle all of that routing with one line in routes.rb. Perhaps something like this:
get '/:namespace/:controller/:action' #note: this isn't valid
But that won't work (see above). I'd rather keep the namespaces, so I could just write out a fresh line in my routes.rb file for each namespace and then add a constraint to check that the URL is asking for that namespace. That wouldn't be the end of the world. It just seems so un-DRY and a shame because all the routing information is neatly presented right there in the URL.
I'm just wondering if there are any sneaky ways round what seems (to me) like a bit of a gap in the capabilities of Dynamic Segment Routing. Can one get the Routing DSL to run a block that (unlike Constraints) does something more complex than a Boolean yes/no on whether the route matches? Or perhaps I'm missing something in the way the standard Rails routing capabilities work. Any suggestions appreciated.
Update: Just to spell it out a little bit more. My understanding of the Rails Guides is that I'll need to have lines like these:
post ':controller/:action' , controller: /user_details\/[^\/]+/
post ':controller/:action' , controller: /preferences\/[^\/]+/
...and so on. If I want 20 namespaces then I'll need 20 of these lines. I'm just wondering if there isn't a more concise way that approaches my 1-line ideal given above.
If what you want is a dynamic route that matches all routes like
post 'user_details/name/edit' => 'user_details/name#edit'
post 'user_details/address/edit' => 'user_details/address#edit'
post 'user_details/password/edit' => 'user_details/password#edit'
post 'preferences/privacy/edit' => 'preferences/privacy#edit'
post 'preferences/colors/edit' => 'preferences/colors#edit'
then you could just use
post ':controller/:action'
since the :controller part will match namespaced controllers, too.
Verified for Rails 4.0.1 and 4.2.0.

Intricate Rails 3 Routing Configuration - Can routes.rb Access Initializers' Code?

I'm converting a bunch of landing pages written in php in order to add them to my RoR-based site (that's been live for more than 2 years now). These landing pages are divided into several versions, but unfortunately there is no consistency as far as URL names go. My problem is that the php pages I'm converting already have a high page rank, therefore I'd like to keep their URLs exactly the way it was.
I'm not sure how to set my routes.rb so that example.com/* will always go to my homepage; however, when (* == 'name-of-one-of-the-landing-pages') Rails will route to a separate controller, where a specific action will determine which page to render, based on an Initializer and the params hash, all this while the URL is, as mentioned, identical to what it was prior to the php-to-RoR conversion, namely www.example.com/name_of_landing_page, rather than www.example.com/*controller_name*/name_of_landing_page.
I know of the :path property that enables one to exclude the controller name from the path if passed an empty string (i.e. resources :examples, :path => ''), but that doesn't quite solve the entire problem.
I was thinking about writing an initializer that would hold a hash of all relevant landing pages, and using constraints in routes.rb to check against it, but I'm not sure if this kind of implementation is possible and how to go about it. A code example would be much appreciated.
Is there some kind of syntax for routes.rb that would enable me to do so, or perhaps a better solution?
To answer the first question: in routes.rb, inside the do/end block you will actually be in the context of ActionDispatch::Routing::Mapper, so no you won't. But, right after that block, you are back to the top level of your application and will have access to whatever variables you initialized inside your initializers, however, that code might be better suited to go in application.rb.
The only thing you should be doing in routes.rb is defining routes.
You could also handle the request for the legacy pages in rack
def call(env)
request = Rack::Request.new(env)
return [200, {"Location" => request.url("http://www.example.com")} if request.host == "www.oldpage.com"
end
More info here: http://railscasts.com/episodes/222-rack-in-rails-3
I don't understand why complex things involved. Setting such should be very simple in route.
Suppose your have a controller to handle static pages named "PagesController"
get 'name-of-one-of-the-landing-pages-a', to: 'pages#a'
get 'name-of-one-of-the-landing-pages-b', to: 'pages#b'
There is no need to add controller names in the path. You can control all of them.

How to handle unpredictable routes?

Assume the following paths to be legitimate and resolving:
http://test.local/wizards/home
http://test.local/wizards/wizardfest2012/dates
http://test.local/dragons/
http://test.local/dragons/blog/stop-slaying-us
http://test.local/
This is (if you couldn't tell) for a CMS that includes a blog, so the slugs would be generated by the user. I have some routes to process first for reserved namespaces (admin, for example).
I assume that the user generated routes need to be routed to a Page controller - but, I don't think pragmatically adding a line to routes.rb is efficient. My question then, is how do I process the first part of the params (in this case, wizards and dragons) to get the correct information from the model?
Here's one of my ideas - split (somehow) the first part of the slug (again, wizards and dragons and pass the rest of the slug (for example, /wizardfest2012/dates) to the model to fetch the associated content.
Any thoughts on the most efficient way to do this?
I am not sure whether I understand what you want to achieve, but maybe this is what you want:
constraints :camp => /wizards|dragons/ do
match ':camp/home' => "pages#home"
match ':camp/blog/:title' => "pages#blog"
# ...and all the routes with known components
match ':camp/*other' => "pages#other"
end
You may create a before_filter which will recognize the params[:camp] and prepare the necessary models or whatever is needed.
The other action will receive the string "wizardfest2012/dates" as params[:other]. I hope that it was what you needed.
The "Rails Routing from the Outside In" guide may be worth reading, unless you have already read it.

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