Restricting resource routes and adding additional non-RESTful routes in Rails 3 - ruby-on-rails

I wasn't able to find anything here or elsewhere that covered both restricting a resource's routes and adding additional non-RESTful routes in Rails 3. It's probably very simple but every example or explanation I've come across addresses just one case not both at the same time.
Here's an example of what I've been doing in Rails 2:
map.resources :sessions, :only => [:new, :create, :destroy], :member => {:recovery => :get}
Pretty straightforward, we only want 3 of the 7 RESTful routes because the others don't make any sense for this resource, but we also want to add another route which is used in account recovery.
Now from what I gather doing either one of these things is very straightforward as well:
resources :sessions, :only => [:new, :create, :destroy]
Just like in Rails 2. And:
resources :sessions do
member do
get :recovery
end
end
So, how do I combine these two? Can I still use the old Rails 2 way of doing this? Is there a preferred way of doing this in Rails 3?

You can pass arguments and a block to resources:
resources :sessions, :only => [:new, :create, :destroy] do
get :recovery, :on => :member
end
And test it out with rake routes.

It should be working pretty much like this
resources :sessions, :only => [:new, :create, :destroy] do
member do
get :recovery
end
end
There is an even shorter way, as proposed by coreyward.
Check the rails guides, "Rails Routing from the Outside In".
I also can recommend "The Rails 3 Way" by Obie Fernandez, which got 2 pretty good chapters on Routing and RESt.
Cheers

Related

Rails not differentiating between two resources nested in the same namespaces

I have two types of products that are nested under the same categories. The routes I have setup are
resources :categories, path: '/', only: [:show] do
resources :subcategories, path: '/', only: [:show] do
resources :amazon_products, path: '/', only: [:show]
resources :other_products, path: '/', only: [:show]
end
end
which I was previously accessing using this link
<%= link_to "View Product Page", [product.collection, product.category, product.subcategory, product], class: 'product__link' %>
which that resulted in something like this url after friendly_id finished with it
/cleansers/face-wash-and-cleansers/blemish-remedy-acne-treatment-gelee-cleanser
The issue is that the link only resolves for amazon_products and I'm not sure how to make it differentiate between the two. I think the problem is in the way I am referencing the path since when I enter rails routes in the console, I can see the two different paths there like so
category_subcategory_amazon_product
GET :category_id/:subcategory_id/:id(.:format)
amazon_products#show
category_subcategory_other_product
GET /:collection_id/:category_id/:subcategory_id/:id(.:format)
other_products#show
I tried specifically referencing the other product path using the link
category_subcategory_other_product_path(product.category, product.subcategory, product)
but it is giving me an ActiveRecord::RecordNotFound since its still looking in the wrong controller
app/controllers/amazon_products_controller.rb:5:in `show'
How do I tell rails to differentiate between these two resources?
When you boil it down, Rails routes are relatively simple patterns that match URIs and dispatch requests to the proper controller/method.
If you had specified your nested routes in their basic form:
resources :categories, only: [:show] do
resources :subcategories, only: [:show] do
resources :amazon_products, only: [:show]
resources :other_products, only: [:show]
end
end
The resulting URI patterns for the product level would look like:
/categories/<id>/subcategories/<id>/amazon_products/<id>
/categories/<id>/subcategories/<id>/other_products/<id>
Admittedly verbose, but obviously distinct, patterns.
The issue is that you are using path: '/' on the resources to remove everything unique about the resource routes. So, your product-level route patterns are:
category_subcategory_amazon_product: /<id>/<id>/<id>
category_subcategory_other_product: /<id>/<id>/<id>
Since the two patterns are identical, Rails falls back to the time honoured practice of matching the first one defined. You can demonstrate that for yourself by swapping :other_products so it is first; its controller will then be the one always invoked.
NB: It doesn't matter that you are using friendly-id -- that just changes what the IDs look like to the user, not the basic route pattern.
The solution is to simply reintroduce some uniqueness, at least at the product level.
resources :categories, path: '/', only: [:show] do
resources :subcategories, path: '/', only: [:show] do
resources :amazon_products, path: 'amazon', only: [:show]
resources :other_products, path: 'other', only: [:show]
end
end
The result will be route patterns that Rails can actually distinguish:
category_subcategory_amazon_product: /<id>/<id>/amazon/<id>
category_subcategory_other_product: /<id>/<id>/other/<id>

How to create routes path into resource in rails 3

I understand resource and path routes in rails 3 but I do not know is there any way to have both routes ? I try this routes but it not work for me, this is the routes:
resources :roles, only: [:index, :create, :show, :update]
get '/roles/:id' => 'roles#available_users'
How can we routes to use both routes ?
thankyou very much
Routes
What you're asking for cannot be done, as you'll be using the same "route" for different controller actions:
#config/routes.rb
resources :roles, only: [:index, :create, :show, :update] #-> domain.com/roles/:id - roles#show
If you then create another route for domain.com/roles/:id, Rails will just take the first which it finds in the routes file
--
The way to fix your issue is likely to be down to the following:
#config/routes.rb
resources :roles, except: [:edit, :destroy] do
get :available_users # -> domain.com/roles/:id/available_users
end
This will take you to the roles#available_users action, providing you with the ability to load the view you wish (to display the users for a particular role)
For a more defined explanation, I'd recommend checking out the nested_resources part of the Rails routing system
If I understand you correctly you want something like this:
resources :roles, only: [:index, :create, :update] do
get '/roles/:id' => 'roles#available_users'
end
Correct?
Just add an "do" behind the closing ] and an end after the custom routes.
Edit: Apparently I got wrong. ;) What you could do is:
resources :roles, only: [:index, :create, :show, :update] do
get '/roles/:id/available' => 'roles#available_users'
end

Making a custom route

The goal is to create a URL like this for a GET, REST API:
/manager/someID/report
example: /manager/2/report
I can get it to show in rake routes if do it this way:
get 'manager/:id/report', to: 'report#show'
But in some weblogs I read, thats the way unskilled developers write their routes! and looks like the better way is to use "nested resources" so I am banging my head over desk to get nested resources working the same way...but no success
this is what I have written so far:
resources :manager, only: [:show] do
resources :report, only: [:show], controller: 'report' do
member do
## WAT ?!
end
end
end
First, you might want to consider reading different blogs if they're calling routes like that "unskilled".
What you proposed is actually fine considering it's a non-standard RESTful route, and maybe even preferable in some cases. If you want an alternative approach, you have a couple different options. I don't think any one is more right than the other, but I prefer the first because it takes up less vertical space.
resources :manager, only: [:show] do
get 'report' => 'report#show', on: :member
end
or
resources :manager, only: [:show] do
member do
get 'report' => 'report#show'
end
end

constraining a route in Rails

I have a controller named results_controller. It uses
resources :results
in the router.
One problem is that I don't want people to be able to see the results page by typing in the following
www.example.com/results
Is there a way to put a redirect on that www.example.com/results without interfering with any of the other resources :results?
The simplest way would be to not generate that specific route:
resources :results, :except => :index
// or you can use
resources :results, :only => [:show, :destroy]
Here's a good intro to rails routing: http://guides.rubyonrails.org/routing.html

Overriding a resource route to / (root) in Rails3: not changing the path helper?

I am quite new to Rails3, I basically created a subscribers scaffolding, I only want my app to respond to new and create actions.
So in config/routes.rb I defined:
resources :subscribers, :only => [:new, :create]
Which works this way
GET /subscribers => subscribers#new
POST /subscribers => subscribers#create
Now I want my app to exhibit the subscribers resources at / (root) instead of /subscribers, so here is what I did:
match '/' => "subscribers#new"
match '/' => "subscribers#create"
match '/' => "subscribers#thankyou"
resources :subscribers, :only => [:new, :create]
Which somehow works, but is probably not the DRYest thing: here are the issues I have:
When going back to the form after an issue on a create the browser displays the /subscribers URL instead of just /, the form is created using the form_for(#subscriber) helper method, so the path helper must be somehow unaffected by the route
Ideally I don't even want the app to respond to a request on /subscribers
I noticed a weird bug, when posting the form while disconnected (from /, and then doing a refresh when the connection comes back (browser ask for resubmitting => OK), the Rails app crashes (I don't have the error stack though as this was on production), why is that?
Also, I tried setting up the route this way:
resources :subscribers, :only => [:new, :create] do
collection do
post '/' => :create
get '/' => :new
end
end
Which is probably DRYer, but it doesn't fix any of these issues.
I am sure this is something quite simple, please help!
Thank you for your answers, it helped me find the exact solution to my question:
resources :subscribers, :only => [:new, :create], :path => '', :path_names => {:new => ''}
Tested and working on Rails 3 :)
You could do
resources :subscribers, :path => ''
and make sure that GET / is being served by your root template, e.g. by adding this to SubscribersController:
def index
render 'welcome/index'
end
I experimented with using a match "/" declaration to override the resource index action and map it to another controller instead but apparently a resources declaration is always fully overriding manually declared routes.
For number 2 in your list, delete this line, and rewrite any _path or _url methods in your erb:
resources :subscribers, :only => [:new, :create]

Resources