Rails 3 routing : customize a resourceful route - ruby-on-rails

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.

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

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.

How to write routing when resource model name does not match path or controller

I've got a model called ImplicitTest. It's called this as having a Ruby object called Test just breaks a lot of things in Rails.
However, I still want to expose it as a RESTful resource as test (e.g. /tests, /test/1/edit and so forth). Furthermore, it would be great to keep the controller as TestsController, although that's less important.
I was doing this by having simple resources :tests line in my routes.rb file, but this fails for the RESTful forms (e.g. <%= form_for #test ... > - this picks up that the #test object is of type ImplciitTest, and tries to lookup implicit_test_path which does not exist.
I tried adding form_for options, but came to the conclusion that to have the form work for both new and edit actions, there was no one single, unified way of asking form_for() to use a different prefix for the path name lookup.
So I've been trying to approach the problem from the routing side. Is there a line I can add to the routes file that will allow me to:
Have a model called ImplicitTest
Have the path as /test
Use the <%= form_for #test ... %> tag still
Keep the controller as TestsController (optional)
I know I am departing the Golden Path to do this, but Rails isn't letting me use Test as a model name, but this is the name users will expect to see in the URL for this resource, so I'm hoping there is are simple routing option(s) that enable this.
All you need to do is set the :path option on your route:
resources :implicit_tests, :path => '/test'
You would still use the standard implicit_tests_path helper this way, too, so your code doesn't have to diverge to alter the URL scheme.
Whilst looking at coreyward's answer, I stumbled across a shorter, but less intuitive method of getting what I need:
resources :tests, :as => "implicit_tests"
Are these essentially doing the same thing (given the extra :controller switch I added in the comments) ? Or is one preferred ?

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.

Ruby on Rails - differentiating plural vs singular resource in a REST API

I'm working on building the URLs for my REST API before I begin writing any code. Rails REST magic is fantastic, but I'm slightly bothered the formatting of a URL such as:
http://myproject/projects/5
where Project is my resource and 5 is the project_id. I think if a user is looking to retrieve all of their projects, then a respective HTTP GET http://myproject/projects makes sense. However if they're looking to retrieve information on a singular resource, such as a project, then it makes sense to have http://myproject/project/5 vs http://myproject/projects/5. Is it best to avoid this headache, or do some of you share a similar concern and even better - have a working solution?
Rails (3) has a lot of conventions when it comes to singular vs plural. For example, model classes are always singular (Person), while the corresponding tables are always plural (people). (For example, Person.all maps to select * from people.)
For routes, there's a concept of a singular resource as well as a plural resource. So if you did resource :account then you would get paths like /account for the default path or /account/edit for a path to a form to edit the account. (Note that Rails uses /account with a PUT method to actually update the account. /account/edit is a form to edit the account, which is a separate resource from the account itself.) If you did resources :people, however, then you would get paths like /people, /people/1, and /people/1/edit. The paths themselves indicate whether there can only be one instance of a given type of resource, or whether there can be multiple instances distinguished by some type of identifier.
I agree, go with the flow. Consider how the URL forms a hierarchy.
The root of your website is where you start to access anything.
/projects/ narrows it down to only projects, not anything else. From projects you can do lots of things, /list, /index/, /export, etc... the /id limits things even further.
At each / the scope of what do becomes narrower, and I think it makes sense.
Further programming is all about arbitrary rules. Indexs starting at 1 vs 0, and so on. Anyone working with your urls will sort things out in short order.
There are cases where a singular path to a resource is helpful. If your resource ids are non-numeric user defined names then routing clashes are possible. Example:
/applications/new --> create a new application or show user's application named new?
In this situation you can choose to limit the user input to avoid the clash, or, this can be worked around by overwriting the default Rails 3 behavior:
class ActionDispatch::Routing::Mapper
module Resources
RESOURCE_OPTIONS << :singular_resource
class Resource
def member_scope
#options[:singular_resource] ? "#{singular}/:id" : "#{path}/:id"
end
def nested_scope
#options[:singular_resource] ? "#{singular}/:#{singular}_id" : "#{path}/:#{singular}_id"
end
end
end
end
Then when specifying a new resource route:
resources :applications, :singular_resource => true
Which will generate the routes:
GET /applications
GET /applications/new
POST /applications
GET /application/:id
GET /application/:id/edit
PUT /application/:id
DELETE /application/:id

Resources