Rails 4 routing: using namespaces with dynamic segments - ruby-on-rails

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.

Related

Wildcards in default resource routes in Rails 3

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).

How to make URL routing like "web.com/user-name"

I have a basic app in Rails 3.2 - two main controllers: users and articles.
The basic routing is like web.com/users/john and web.com/articles/articles-name.
With the match rule I can make the URLs above shorter:
match "u/:permalink" => "users#my_action_name"
match "a/:permalink" => "articles#my_action_name"
allow me to do
web.com/u/john and web.com/a/articles-name
But in the best way, I would like to make the URL yet shorter and more readable, like this (this way use for instance Twitter - twitter.com/username)
web.com/john and web.com/articles-name
But I can't imagine, how to realize this way of routing. In the app are used also another controllers, like for example HomeController with actions index, about, contact.
Take a look at the "Route Globbing" section of the official Rails guides. You need to add something like
match '*users_or_articles' => 'agnostic#show'
at the end of your config/routes.rb file. This will catch all request that found no matchers in your routes file so far. Then your controller has to identify what to do with the request.
I was wondering the same thing some time ago and found an answer in the Routing Guide.
Your question can be answered in 2 steps:
1) First step would be to override the to_param method in your models in order to have prettier URLs. Take a look at this question for an example. You could also use a gem like FriendlyId (take a look at this screencast).
2) Then you would have to use the :path option in your routes.rb file. So for example if you wanted to have URLs like "example.com/3214-a-blog-post" you should declare your resource like this:
resources :posts, :path => "/"

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 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.

Can someone please explain to me in clear, layman's terms what the deal is with mapped resources and named routes in Ruby on Rails?

I've been using Ruby for the first time on a project at my work, so I am still somewhat learning the ropes (and loving every minute of it).
While I understand the point of the map.connect functions in the route.rb file, I don't understand the "resources" and "named route" features of Rails. I have my Rails book here and read it over several times, but I still don't get it. The named routes I kinda get - I think that they are either rules, either explicitly defined, or calculated by a code block, but the resources are a complete mystery to me; the only thing I've gleamed rom them is that you just NEED them if you want some of the cool stuff to work, such as being able to call 'resource_path' (and its awesome related family of methods).
My current project has:
map.resources :application_forms
map.resources :sections
map.resources :questions
map.resources :seed_answers
map.resources :question_types
map.resources :form_questions
map.resources :rules
map.resources :form_rules
..but my Rails book has this awesome kinda "has_many" and "only" type hashes and parameters hanging off them and I can't work out exactly when I am supposed to use them, nor what the benefit is.
Can anyone set me straight?
Named routes are just that; a route with a name attached, so that you can easily refer to it when you want to generate a URL. Among other things, it can eliminate ambiguity.
A resource is basically a 'thing' that you want to have routes to manipulate. When you define that 'sections' is a resource, what you're doing is saying "I want a route to get all the sections. I want a route to add a new section. I want a route to edit an existing section. I want a route to delete a section." That sort of thing. These routes point to standardized method names like index, new, edit, and so on. Each of these routes will have a name assigned based on what it is; so there is now a route named 'edit_section'.
The :has_many parameter lets you say that a certain kind of thing has sub-things. For example, you can say map.resources :sections, :has_many => [:questions]. This means that a question belongs to a section, and this will be reflected in the url and the route. You'd get urls like '/sections/27/questions/12' and named routes like 'section_questions'.
The :only parameter says "only make routes for these actions"; you could use it if you only want to allow listing, viewing, and adding items, not editing or deleting.
Honestly the Rails Routing Guide will give you a good explanation in about as plain wording as you can get. Just know that a resource route == RESTful route and you're good to go.
We all struggled with understanding resources and REST when DHH introduced it to the Rails community at the first RailsConf in 2006, so it is not wonder you have trouble grasping the concept.
I admit there is much better and more up-to-date explanations of the concepts today, but back then, right after David's keynote, I wrote a blog post in which I, from discussion with other conference attendees, tried to understand and explain it. It might help you, as it doesn't take for granted that you know everything about REST as more recent articles do.

Resources