Editing the current User in Rails 3 - ruby-on-rails

Users can be edited from a normal resourceful URI like:
/users/1/edit
The issue is that in my application, the edit user page is the home page or root route.
# routes.rb
root :to => "users#edit"
So, I tried to set #user to the current user in the absence of params[:id].
# app/controllers/users_controller.rb
def edit
#user = (params[:id]) ? User.find_by_id(params[:id]) : #current_user
end
Unfortunately, I'm having trouble getting the form to point properly.
# app/views/shared/_manage_users.rb
<%= form_tag follow_user_path, :id => 'update-following-form' %>
I'm getting:
No route matches {:action=>"follow", :controller=>"users"}
follow is a member route of the user resource and has a corresponding controller method. If I access the page via the URI at the top of this question, /users/1/edit, everything works fine and no error is thrown.
I'm not sure whether I'm going about this completely the wrong way or if I'm just not using the right form helper or something silly. How can I fix this issue, or what steps can I follow to debug it?

A member route expects the member to be passed as an argument. You route is expecting a User, like so:
follow_user_path(#user)

in your routes do this
resource :user
instead of
resources :users
now the id param is notin the url. you just need to ensure the user is logged in

I think you need to actually define follow_user in your routes.rb.
Example:
post "user/follow" => "users#follow", :as => :follow_user

Related

Allowing user to edit account without the :id param in the url

Hi I'd like to accomplish having a URL that's
users/edit
instead of the current
users/7/edit
I have my own auth system built upon omniauth. Thus I store their user_id in a session. How would I go about accomplishing this task?
Assuming that you use current_user, even if you are using something else just replace current_user with your method, I am using current_user here, follow these steps,
Create an action, I would name it edit_user in your users controller
def edit_user
#user = current_user # or User.find(session[:user_id])
end
Add routes to routes.rb
get "/users/edit" => "users#edit_user"
You are done, you can use the above route anywhere in the application, you can also name the route if needed.
OR, if you don't want to define a new action and want to use the existing edit action, do this
Remove routes for edit from the default resources routes and then manually define it. In this way, you can use the existing edit action
resources :users, except: [:edit]
get "/users/edit" => "users#edit"
Hope this helped!

How to change a rails controller default router from index to other?

As we know, if we have a UsersController, when we get /users it default to /users/index. If I want to map /users to /users/show, what can I do? (This can apply to any other controllers, not just users.)
You can modified the file Global.asax.
There is a method named 'RegisterRoutes',change the 'Index' in 'action=Index' to 'show'
The only difference between URLs for the index action and the show action is the show action requires the identity of the resource. The default Rails URL for index on the users resource is /users, and the default show URL looks like /users/12, where the 12 identifies the resource. This identification part isn't optional, so you can't usefully map /users to the show action in a RESTful scheme.
For example if you want to have a profile page at localhost:3000/username you can add match '/:id' => 'users#show', :as => :user to routes.rb and <%= link_to "Profile", #user %> to your view. With a little fiddling in the controller to grab your profile info via the current user's username, this will link to localhost:3000/username.

custom action in rails 3

I'm trying to make a simple link that will toggle my "status" attribute in my model from "pending" to "active". For example, when I first create a user, I set the status to "pending". Then when I show the list of users, I add a button that should change that user's status to "active". I tried this via a custom action (is this a good approach?) but I'm having trouble with the auto-generated named route.
in my user index.html.haml:
button_to "Manually Activate", activate_user_path
in routes.rb:
resources :users do
get :activate, :on => :member
in users_controller.rb:
def activate
#user = User.find(params[:id])
#user.update_attribute(:status, 'Active')
redirect_to #user
end
this seems to work when I go to say, /users/1/activate, as the status will update. However, the /users page doesn't show and gives me error:
ActionController::RoutingError in Users#index
No route matches {:action=>"activate", :controller=>"users"}
ie, it is having a problem with the activate_user_path I specified in my view. (However if I use another named-routes-style path that I haven't specified in my routes.rb to test it out, I get
NameError in Users#index
undefined local variable or method `blahblah_user_url' for #<#<Class:0x00000102bd5d50>:0x00000102bb9588>
so it seems that it knows it's in the routes.rb but something else is wrong? I'm really new to rails and would appreciate the help!
thanks!
Your link should look like this:
button_to "Manually Activate", activate_user_path(#user)
You need to add what user you want to activate.
A number of problems, I can see.
Firstly you should NOT update the database using a GET request.
Secondly button_to will provide you with an inplace form which when clicked will POST to your app.
Thirdly, the way you have your routes setup, you need to provide the user in the path (you've tested it by forming the url in the browser already).
run
rake routes
on the command prompt to see how your routes look and the name you can use to generate those routes.
I suspect you need to use
button_to "Manually Activate", activate_user_path(user)
(user or #user or whatever is the user object). In your button_to call and change the "get" to "post" in the routes file.
resources :users do
member do
post :activate
end
end

Changing the id parameter in Rails routing

Using Ruby on Rails 3's new routing system, is it possible to change the default :id parameter
resources :users, :key => :username
come out with the following routes
/users/new
/users/:username
/users/:username/edit
...etc
I'm asking because although the above example is simple, it would be really helpful to do in a current project I'm working on.
Is it possible to change this parameter, and if not, is there a particular reason as to why not?
In your route you can use
resources :user, param: :username
If I understand you correctly, what you want is to have the username instead of id in your url, right?
You can do that by overriding the to_param method in your model. You can get more information here.
For Ruby on Rails 4.1.4 (and possibly earlier versions) you need to do what both j.. and Ujjwal suggested:
1) In config/routes.rb, add:
resources :user, param: :username
2) In app/models/user.rb, add:
def to_param
username
end
In you only do #1 then all your routes will be correct, as can be seen from rake routes:
$ rake routes
Prefix Verb URI Pattern Controller#Action
user_index GET /user(.:format) user#index
POST /user(.:format) user#create
new_user GET /user/new(.:format) user#new
edit_user GET /user/:username/edit(.:format) user#edit
user GET /user/:username(.:format) user#show
PATCH /user/:username(.:format) user#update
PUT /user/:username(.:format) user#update
DELETE /user/:username(.:format) user#destroy
However the helper methods that construct a url based on a User instance will still include the id in the url, e.g. /user/1. To get the username in the constructed urls, you need to override to_param as in #2.
Although the answer has been accepted by the asker, but there is a simple approach to do this. Write this code in your controller.
authorize_resource :find_by => :username
and in your view, where you want to show the link, write this code.
<%= link_to "Username", user_path(u.username) %>
You don't need any other changes in your routes or controller.
UPDATE: This will only work if you are using CanCan gem.
Pass the user name as the input to the path helpers.
In your view code:
user_path(u.username) #/users/john
In your controller treat the id received as username:
def show
user = User.find_by_username!(params[:id])
...
end

How do I define RESTful routes in Rails for a resource which has multiple key fields?

My User model has the usual id primary key, but it also has a unique login which can be used as an identifier. Therefore, I would like to define routes so that users can be accessed either by id or by login. Ideally, the routes would be something like this:
/users/:id (GET) => show (:id)
/users/:id (PUT) => update (:id)
...
/users/login/:login (GET) => show (:login)
/users/login/:login (PUT) => update (:login)
...
What is the best way to do this (or something similar)?
So far, the best I could come up with is this:
map.resources :users
map.resources :users_by_login,
:controller => "User",
:only => [:show, :edit, :update, :destroy],
:requirements => {:by_login => true}
The usual RESTful routes are created for users, and on top of that, the users_by_login resource adds the following routes (and only those):
GET /users_by_login/:id/edit
GET /users_by_login/:id/edit.:format
GET /users_by_login/:id
GET /users_by_login/:id.:format
PUT /users_by_login/:id
PUT /users_by_login/:id.:format
DELETE /users_by_login/:id
DELETE /users_by_login/:id.:format
These routes are actually mapped to the UserController as well (for the show/edit/update/destroy methods only). An extra by_login parameter is added (equal to true): this way, the UserController methods can tell whether the id parameter represents a login or an id.
It does the job, but I wish there was a better way.
Just check to see if the ID passed to the controller methods is an integer.
if params[:id].is_a?(Integer)
#user = User.find params[:id]
else
#user = User.find_by_login params[:id]
No need to add special routes.
Actually Kyle Boon has the correct idea here. But it is slightly off. When the params variable comes in all the values are stored as strings so his example would return false every time. What you can do is this:
if params[:id].to_i.zero?
#user = User.find_by_login params[:id]
else
#user = User.find params[:id]
end
This way if the :id is an actual string Ruby just converts it to 0. You can test this out by looking at the params hash using the ruby-debug gem.
(I would have just commented but I don't have enough experience to do that yet ;)
Not exactly sure what you are doing here but this may be of some help.
You can define actions that are outside of the automatic RESTful routes that rails provides by adding a :member or :collection option.
map.resources :users, :member => { :login => [:post, :get] }
This will generate routes that look like this:
/users/:id (GET)
...
/users/:id/login (GET)
/users/:id/login (POST)
Another thing you could do just use the login as the attribute that you look up (assuming that it is unique). Check out Ryan Bates screencast on it. In your controller you would have:
def show
#user = User.find_by_login(params[:id])
...
end
He also has another screencast that may help you. The second one talks about custom named routes.

Resources