Wildcards in default resource routes in Rails 3 - ruby-on-rails

I have the following routes in my Rails application:
resources :photos
get "/photos/*id/stats" => "photos#stats"
get "/photos/*id/visitors" => "photos#visitors"
The id parameter is a wildcard that should allow special characters like slashes. Basically, "/photos/img/trial.jpg/stats" should route to stats method in photos_controller with photo id as "img/trial.jpg".
This part is working correctly.
However, I am not sure how to use wildcard matching for id in the default routes created by "resources :tags", which are basic CRUD routes like "/photos/:id/edit" => "photos#edit".
Is there a cleaner way to use wildcard matching for default resource routes, rather than not use resource routes and specify those routes explicitly?

You should be able to do something like
resources :photos
get "/photos/:slug/stats" => "photos#stats"
get "/photos/:slug/visitors" => "photos#visitors"
I am guessing that you are using something like friendy_id

As explained in the Rails guide and the answer above, you'd usually use the colon notation. The guide calls them dynamic segments.
These do however not work as intended in your case, since they don't allow dots by default (dots are reserved for specifying the format of the query). You override the default behavior by specifying your own constraint
get "/photos/:id/stats" => "photos#stats", id: /[^\/]+/
This for example only disallows / in the id. Slashes can not be allowed, since then we would have no way to detect the trailing /stats. You could possibly escape the slashes or replace them in URLs with something. The other answer mentions the friendly id gem, which could be a good way to automate this (I'm not using it, though).

Related

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.

Remove controller_name from URL in Rails 3 and use custom string instead of id

Since the beginning I always hat this one problem with rails, short urls without the controller name.
For example, I have a blog and I don't want any dates or controller names in the url, I already have a Page model with a unique field url in my database. Rails works great with such urls:
jeena.net/pages/1
And when I modify the model I even can get it to use
jeena.net/pages/foo
But it seems not to matter what I do I can not get it to work with just:
jeena.net/foo
Of course I want the index page still to work with
jeena.net/pages
And I want creating new pages and updating old pages to work too in some was as well as the link_to()-method. All suggestions are appreciated.
To define that route, try adding the following to your routes.rb:
match '/:id' => 'your_controller#your_action'
This will pretty much match everything to the id of your model. And that's not very nice... You don't want to route youe_host/pages to the pages controller, with an id equal to 'pages'... To prevent that from happening, make sure to put that line on the end of the routes.rb file. The router uses the first route that matches the path received, so putting that line on the end of it will make sure that it will only match your route after it ran out of other meaningful options.
A better practice would be to pass regexp constraints to the router, so that it will only match ids with a specific format, like that:
match '/:id' => 'your_controller#your_action', :constraints => { :id => /your_regexp/ }
Refer to the guides if you have doubts about the rails rounting system. It is pretty well written and covers lots of important things.
Rails rounting - Official Guides
edit: to create a named route, one that you can call in your controllers and override the normal routes that you are probably creating with resource, you have to provide the :as => parameter in your routes.rb
match '/:id' => 'your_controller#your_action', :as => some_name
Then you'll be able to call it in your controller/views like this:
link_to some_name_path(#my_string_id)
Hope this helps. And take a time to read the guides, it has really lots of useful info, including more details about creating named routes.

Rails 3 routing : customize a resourceful route

I've got some Documents (and a DocumentsController), which are sorted using limited, fixed set of categories. I'd want my routes to take into account these categories, so my urls would look like :
/documents/:category/:id
/documents/:category/new
/documents/:category/:id/edit
...and so on, which should allow me to access params[:category] in order to filter the results. Is there a simple way to achieve this, that would still generate path helpers ? Or im i wrong to do this that way ?
You can provide a path to a resource (as you mentioned):
# config/routes.rb
resources :documents, :path => 'documents/:category'
This would give you the following routes:
/documents/:category
/documents/:category/new
/documents/:category/:id/edit
/documents/:category/:id
I am not sure in this case what purpose the category capturing will serve, since you can reference the document by its primary key. This key most likely does not repeat across categories.
It's not hard to customize paths in Rails 3.
match '/documents/:id', to: 'documents#show', as: :document would give you the path helper document_path(:id). This will work even for an ID that's a string rather than a number, so extending this pattern to /documents/:category/:id/edit should be no problem.

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.

Supporting URLs like /similar-to-:product in Ruby on Rails?

I have been trying to use routes.rb for creating a URL /similar-to-:product (where product is dynamic) for my website. The issue is that routes.rb readily supports URLs like /:product-similar but doesn't support the former because it requires :product to be preceded with a separator ('/' is a separator but '-' isn't). The list of separators is in ActionController::Routing::SEPARATORS.
I can't add '-' as a separator because :product can also contain a hyphen. What is the best way of supporting a URL like this?
One way that I have successfully tried is to not use routes.rb and put the URL parsing logic in the controller itself, but that isn't the cleanest way.
I would refactor your URLs so that they're simply "similar-to/product"
In fact you can add - as a separator, then use route globbing.
map.similar_product '/similar-to-*product', :controller => 'products', :action => 'similar'
then, in ProductsController#similar
#product = Product.find_by_slug params[:product].join('-')
Though refactoring does seem nicer, since with this approach you'll need to specially handle all slugs that can contain hyphens.
An easy solution is using a routing filter. See README for details.
With routing filter you can have a url /similar-to-:product, preprocess it to /similar-to/:product before it gets to routing recognition. You'll also want to post-process generated paths back from /similar-to/:product to /similat-to-:product.
I'm a little confused, but could you maybe add "to-" as a seperator?

Resources