Rails - From RESTFul resource to customized routes - ruby-on-rails

This is a design problem that I am trying to figure out. I will explain what I have right now, and what I would like to have:
1. Actual design
I have a defined a resources :users and by doing so I have defined different actions such as new, create and update in the Users controller. This is working as expected by following urls like users/new , users/:id, etc...
Now I want to go one step forward, and I want to be able to do the following...
2. What am I looking for
I want to be able to have a route like this:
users/overview/profile - This should be equivalent to `users/:id` (show action)
users/overview/network - This should be equivalent to users/:id/network (list of networks for that user)
3. My idea
My first idea was to define something like this:
resource :users do
namespace :overview do
resource :networks
end
end
But this would work for urls like: users/:id/overview/networks and I don't want the user id to be shown in the URL. So my questions are:
1 - How can I deal with users/overview/networks instead of users/:id/overview/networks , assuming that I can get the user id from session.
2 - How can I be able to manage URLs like this: users/overview/profile where actually a profile is just the show method of users/:id Right now I have defined all the actions in the users controller and everything is working fine (new,delete,create,update...) I just don't know how to move into that "namespace" overview/profile

I have tried same thing you tried, and it is returning your desired results only, not sure what is your problem. Posting rake routes result here.
users_overview_networks POST /users/overview/networks(.:format) {:action=>"create", :controller=>"overview/networks"}
new_users_overview_networks GET /users/overview/networks/new(.:format) {:action=>"new", :controller=>"overview/networks"}
edit_users_overview_networks GET /users/overview/networks/edit(.:format) {:action=>"edit", :controller=>"overview/networks"}
GET /users/overview/networks(.:format) {:action=>"show", :controller=>"overview/networks"}
PUT /users/overview/networks(.:format) {:action=>"update", :controller=>"overview/networks"}
DELETE /users/overview/networks(.:format) {:action=>"destroy", :controller=>"overview/networks"}
users POST /users(.:format) {:action=>"create", :controller=>"users"}
new_users GET /users/new(.:format) {:action=>"new", :controller=>"users"}
edit_users GET /users/edit(.:format) {:action=>"edit", :controller=>"users"}
GET /users(.:format) {:action=>"show", :controller=>"users"}
PUT /users(.:format) {:action=>"update", :controller=>"users"}
DELETE /users(.:format) {:action=>"destroy", :controller=>"users"}

scope :path => 'users/overview' do
match ':id/profile', :to => 'users#show'
match ':id/network', :to => 'users#network'
end

Related

Understanding rake routes output

I'm confused by my rake routes output. For an example (trimmed):
profil GET /profil/:id(.:format) {:action=>"show", :controller=>"profil"}
PUT /profil/:id(.:format) {:action=>"update", :controller=>"profil"}
login GET /login(.:format) {:action=>"new", :controller=>"sessions"}
POST /login(.:format) {:action=>"create", :controller=>"sessions"}
logout GET /logout(.:format) {:action=>"destroy", :controller=>"sessions"}
I've always thought:
Line 2: Route can be accessed using profil_path with PUT method.
Line 4: Route can be accessed using login_path with POST method.
Conclusion: Lines with the first column empty (line 2 and 4) would follow the one above it.
However, I've been experimenting with adding parameter to the url. So, I added these codes in my routes.rb:
namespace :admin do
resources :pengguna_bulk, :only => [:new, :create]
resources :pengguna do
collection do
get 'index/:page', :action => :index
end
end
end
New rake routes output (trimmed):
admin_pengguna_bulk_index POST /admin/pengguna_bulk(.:format) {:action=>"create", :controller=>"admin/pengguna_bulk"}
new_admin_pengguna_bulk GET /admin/pengguna_bulk/new(.:format) {:action=>"new", :controller=>"admin/pengguna_bulk"}
GET /admin/pengguna/index/:page(.:format) {:action=>"index", :controller=>"admin/pengguna"}
admin_pengguna_index GET /admin/pengguna(.:format) {:action=>"index", :controller=>"admin/pengguna"}
POST /admin/pengguna(.:format) {:action=>"create", :controller=>"admin/pengguna"}
new_admin_pengguna GET /admin/pengguna/new(.:format) {:action=>"new", :controller=>"admin/pengguna"}
edit_admin_pengguna GET /admin/pengguna/:id/edit(.:format) {:action=>"edit", :controller=>"admin/pengguna"}
admin_pengguna GET /admin/pengguna/:id(.:format) {:action=>"show", :controller=>"admin/pengguna"}
PUT /admin/pengguna/:id(.:format) {:action=>"update", :controller=>"admin/pengguna"}
DELETE /admin/pengguna/:id(.:format) {:action=>"destroy", :controller=>"admin/pengguna"}
My question is, why is the 3rd route looks like it's under the 2nd route? Is it empty because Rails do not know what to name it and I'd have to use get 'index/:page', :action => :index, :as => :page to name it?
So, this means, route with an empty first column doesn't always follow the above path?
I've always thought:
Line 2: Route can be accessed using profil_path with PUT method.
Line 4: Route can be accessed using login_path with POST method.
Conclusion: Lines with the first column empty (line 2 and 4) would
follow the one above it.
Everything's correct except the conclusion. profil_path expands to /profil/:id(.:format). If it is called with method GET it responds to your first route, if its called with method PUT it responds to your second route.
But same doesn't hold true for second set of routes. You don't have any named helper for /admin/pengguna/index/:page(.:format). If you want a named helper, you should define the route like:
get 'index/:page', :action => :index, :as => :what_ever_named_helper_you_want

difference between resource and controller generators

when I do
rails g model user name:string
rails g controller users index create new destroy show
and edit config/routes.rb to add:
resource :users
bundle exec rake routes gives:
users POST /users(.:format) {:action=>"create", :controller=>"users"}
new_users GET /users/new(.:format) {:action=>"new", :controller=>"users"}
edit_users GET /users/edit(.:format) {:action=>"edit", :controller=>"users"}
GET /users(.:format) {:action=>"show", :controller=>"users"}
PUT /users(.:format) {:action=>"update", :controller=>"users"}
DELETE /users(.:format) {:action=>"destroy", :controller=>"users"}
however, when I do
rails g resource users name:string
(which automatically adds resources :users to config/routes.rb)
bundle exec rake routes
I get
users GET /users(.:format) {:action=>"index", :controller=>"users"}
POST /users(.:format) {:action=>"create", :controller=>"users"}
new_user GET /users/new(.:format) {:action=>"new", :controller=>"users"}
edit_user GET /users/:id/edit(.:format) {:action=>"edit", :controller=>"users"}
user GET /users/:id(.:format) {:action=>"show", :controller=>"users"}
PUT /users/:id(.:format) {:action=>"update", :controller=>"users"}
DELETE /users/:id(.:format) {:action=>"destroy", :controller=>"users"}
So my question is,
when I generate a controller how can I get the correct helper methods to make
link_to 'Destroy', instance, :method=> :delete
work?
Because currently it gives an error user_path is not defined.
You should call
rails g controller user index create new destroy show
instead of
rails g controller users index create new destroy show
in order to get resources :users to give you the helpers you want.
The latter causes Rails to assume that users is a singular object, and that resources :users should create what is called a singular resource:
http://guides.rubyonrails.org/routing.html#singular-resources
as a result, user_path is undefined, whereas users_path is defined.
When you use rails g controller and specify the method names, the generator only maps specific routes to the routes file. rails g resource assumes that you want the whole resource functionality and will map resources.
In order to fix this, just go into your routes file and replace the specific mappings with a resources call.
resources :users
What I really wanted was a way of creating a working (with correct delete/show paths) controller for an existing model (as described in the question) but just adding "resource :x" and generating the controller wasn't enough.
I ended up using the scaffold_controller generator. It doesn't create any migrations or models, but it does generate a resource with views and rake paths command shows the correct paths for delete and show to work.
You can run following commands in the console:
$rails g model user name:string
$rails g scaffold_controller User
And add this code line to the file routes.rb:
resources :users

Routing redirection

I am using Ruby on Rails 3 and I would like to redirect all requests made to a resource (URL) to another resource.
I have the following resources:
<My_app_name>::Application.routes.draw do
resources :users
namespace :users do
resources :user_admins
end
end
What I would like to do is to redirect all requests made to <my_app_name>/users/user_admins/<id> to <my_web_site_name>/users/<id>. How can I do that?
Note: I am using a Single Table Inheritance approach, so that the <id> value will don't change behaviours. That is, the <id> value will be automatically handled from the RoR framework and it will refer to the same resource for both when the URL is <my_app_name>/users/user_admins/<id> or <my_web_site_name>/users/<id>.
You may want to avoid the use of a namespace, and play it like that :
scope "users" do
resources "user_admins", :controller => "users"
end
You'll get those routes :
user_admins GET /users/user_admins(.:format) {:action=>"index", :controller=>"users"}
POST /users/user_admins(.:format) {:action=>"create", :controller=>"users"}
new_user_admin GET /users/user_admins/new(.:format) {:action=>"new", :controller=>"users"}
edit_user_admin GET /users/user_admins/:id/edit(.:format) {:action=>"edit", :controller=>"users"}
user_admin GET /users/user_admins/:id(.:format) {:action=>"show", :controller=>"users"}
PUT /users/user_admins/:id(.:format) {:action=>"update", :controller=>"users"}
DELETE /users/user_admins/:id(.:format) {:action=>"destroy", :controller=>"users"}
Try this in your routes.rb:
match 'users/user_admins/:id' => 'users#show'

Rails 3 Routing Resources with Variable Namespace

Is it possible to have a variable namespace? I have restful resources like the following:
resources :articles
resources :persons
But I need to scope these inside a variable namespace, such that it responds to URLs of the form:
':edition/:controller/:action/:id'
for example:
/foobar/article/edit/123 or /bazbam/person/edit/345
for each of the resources. Is this possible with the resources method, or must I hand-craft these? I will not know the possible values for :edition ahead of time; these get looked up in a before_filter in my ApplicationController.
Is this all I need to do?
scope ':edition' do
resources :articles
resources :matches
resources :teams
end
UPDATE: When using the scope directive above, I get routes like I want:
articles GET /:edition/articles(.:format) {:action=>"index", :controller=>"articles"}
POST /:edition/articles(.:format) {:action=>"create", :controller=>"articles"}
new_article GET /:edition/articles/new(.:format) {:action=>"new", :controller=>"articles"}
edit_article GET /:edition/articles/:id/edit(.:format) {:action=>"edit", :controller=>"articles"}
article GET /:edition/articles/:id(.:format) {:action=>"show", :controller=>"articles"}
PUT /:edition/articles/:id(.:format) {:action=>"update", :controller=>"articles"}
DELETE /:edition/articles/:id(.:format) {:action=>"destroy", :controller=>"articles"}
matches GET /:edition/matches(.:format) {:action=>"index", :controller=>"matches"}
POST /:edition/matches(.:format) {:action=>"create", :controller=>"matches"}
new_match GET /:edition/matches/new(.:format) {:action=>"new", :controller=>"matches"}
edit_match GET /:edition/matches/:id/edit(.:format) {:action=>"edit", :controller=>"matches"}
match GET /:edition/matches/:id(.:format) {:action=>"show", :controller=>"matches"}
PUT /:edition/matches/:id(.:format) {:action=>"update", :controller=>"matches"}
DELETE /:edition/matches/:id(.:format) {:action=>"destroy", :controller=>"matches"}
teams GET /:edition/teams(.:format) {:action=>"index", :controller=>"teams"}
POST /:edition/teams(.:format) {:action=>"create", :controller=>"teams"}
new_team GET /:edition/teams/new(.:format) {:action=>"new", :controller=>"teams"}
edit_team GET /:edition/teams/:id/edit(.:format) {:action=>"edit", :controller=>"teams"}
team GET /:edition/teams/:id(.:format) {:action=>"show", :controller=>"teams"}
PUT /:edition/teams/:id(.:format) {:action=>"update", :controller=>"teams"}
DELETE /:edition/teams/:id(.:format) {:action=>"destroy", :controller=>"teams"}
I'm now able to reference :edition in my ApplicationController:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :authenticate_user!
before_filter :get_edition
def get_edition
#edition = Edition.first(:conditions => { :FriendlyName => params[:edition] } )
end
end
Now I just want to make sure this is the best way to accomplish this.
Actually, you can just do the following :
my_var = "persons"
resources my_var.to_sym
The to_sym method on a string changes it to a symbol
If you don't know the possible values for edition - then you can't use namespaces which seem like they might have solved this issue.
That said, I'd just handcraft them - your case here seems like the ideal case foregoing resources and going straight to a handcrafted path.

Weird routing error in Rails 3.0

I've been converting my Rails 2.3.9 application to Rails 3.0 and everything has been going well. I've created a lot of routes with the new system so I feel like I know how to do this. However, I recently got a routing error I can't solve.
I have a users resource defined like this:
resources :users do
member do
get 'activate'
get 'edit_password'
post 'update_password'
end
collection do
get 'forgot_password'
post 'send_reset_instructions'
end
end
The failing route is update_password. If I change it to a get method it works. But not when used as a post, with data coming from a form in "edit_password".
This is the error message:
Routing Error
No route matches "/users/31/update_password"
This is the (relevant) output of rake routes:
activate_user GET /users/:id/activate(.:format) {:action=>"activate", :controller=>"users"}
edit_password_user GET /users/:id/edit_password(.:format) {:action=>"edit_password", :controller=>"users"}
update_password_user POST /users/:id/update_password(.:format) {:action=>"update_password", :controller=>"users"}
forgot_password_users GET /users/forgot_password(.:format) {:action=>"forgot_password", :controller=>"users"}
send_reset_instructions_users POST /users/send_reset_instructions(.:format) {:action=>"send_reset_instructions", :controller=>"users"}
users GET /users(.:format) {:action=>"index", :controller=>"users"}
users POST /users(.:format) {:action=>"create", :controller=>"users"}
new_user GET /users/new(.:format) {:action=>"new", :controller=>"users"}
edit_user GET /users/:id/edit(.:format) {:action=>"edit", :controller=>"users"}
user GET /users/:id(.:format) {:action=>"show", :controller=>"users"}
user PUT /users/:id(.:format) {:action=>"update", :controller=>"users"}
user DELETE /users/:id(.:format) {:action=>"destroy", :controller=>"users"}
Finally, this is the output of the web server for that request (webrick):
Started POST "/users/31/update_password" for 127.0.0.1 at 2010-10-14 23:30:59 +0200
SQL (1.5ms) SELECT name
FROM sqlite_master
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
SQL (0.7ms) SELECT name
FROM sqlite_master
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
ActionController::RoutingError (No route matches "/users/31/update_password"):
Rendered /home/jonatan/.rvm/gems/ruby-1.9.2-p0/gems/actionpack-3.0.0/lib/action_dispatch/middleware/templates/rescues/routing_error.erb within rescues/layout (1.0ms)
Can you find any logic in this?
I'm using the latest Rails available through gem (3.0.1). At first I did override *to_param* in the User-model to use the username, but the problem is the same no matter what.
I've checked for misspellings in the update_password method of the model, and also made sure it is not private.
I have noticed this as well, only with the POST method and only when using "member", so same as your example, ie:
resources :forum_posts do
member do
post :preview
end
end
will trigger this, but changing it to GET or using collection both work fine, so seems to be a bug with rails routes; I get around this for now by adding the following just above my resources :forum_posts do line:
match '/forum_posts/:id/preview(.:format)', :to => "forum_posts#preview", :as => :preview_forum_post
then everything continues working; easy temporary fix.

Resources