views:

31

answers:

2

Currently users can access their "profile" through many paths.

  • localhost:3000/users/current_user
  • localhost:3000/users/current
  • localhost:3000/users/id#

How can I make it that they can only get to their "profile" through localhost:3000/users/current_user

A: 

Umm, first, remove the /users/current route that you must have in your routes.rb somewhere. (Although I prefer /users/current to /users/current_users, since the latter is rather redundant.)

As for /users/123, in your controller, you can check if the current user's ID matches 123 or whatever, and, if so, redirect.

But I really prefer the opposite effect. Pushing /users/current to /users/123 makes more sense in my brain, since it keeps the routes consistent for all users while still allowing you to cache links to /users/current.

Matchu
double checked to see if I have /users/current and I don't. Am using Authlogic and Declarative Authorization. Might one of them be adding this?
pcasa
@pcasa: I dunno, I haven't used them. I'm pretty sure Authlogic wouldn't, since they install almost nothing by default, but I've never used Dec Auth... You can run `rake routes` to see if the route is inserted somehow. Maybe `User.find('current')` returns the current user?
Matchu
A: 

One suggestion on the 'what' of your question: instead of the ideal url being localhost:3000/users/current_user I suggest localhost:3000/user or something even more descriptive such as localhost:3000/profile or localhost:3000/account.

Could you include the entries in your routes.rb? Even if Authlogic, etc. add routes to your app, they should do it in routes.rb. If you have the entry:

map.resource :users

then that's where the /users/123 route is coming from. I agree with Matchu that even if you don't use /users/123 you should keep it and route other requests to it.

An Additional Idea If you don't want to get into the (kinda complicated, and not pretty) business of preserving model validation errors across redirects, here's another way. I'm assuming from here that you already have map.resource :users, so that you have the 7 default actions on your UsersController (index, new, create, show, edit, update, destroy).

In your routes.rb:

map.profile 'profile', :controller => 'users', :action => 'show'
map.edit_profile 'profile/edit', :controller => 'users', :action => 'edit', :conditions => { :method => :get }
map.update_profile 'profile/edit', :controller => 'users', :action => 'update', :conditions => { :method => :put }

You will need to update your form_for tag slightly:

<% form_for @user, :url => update_profile_path do |f| %> ...

Now, assuming you start on /profile, and click an edit link that takes you to /profile/edit (should show the form), if you fill out the form such that it fails validation then you should end up back on /profile/edit with the correct errors in the f.error_messages output.

Your controller code for edit should stay the same, and your code for update should be:

def update
   @user = current_user || User.find(params[:id])
   if @user.update_attributes(params[:user])
     flash[:notice] = "Successfully updated user."
     redirect_to @user
   else
     render :action => 'edit'
   end
end

The render (rather than a redirect) preserves the state of the @user model (including errors) and just renders the edit template again. Since you directed it at the update_profile_path the url viewed by the user is still /profile/edit.

whazzmaster
OK, that makes sense. Did map.profile. If a validation now kicks in it will still go to /user/123 with error(s). Is there a way that if a validation kicks in that it goes to /profile with error message(s)?
pcasa
Assuming you defined a named route (map.profile) then, in your controller, redirect to profile_path. I should note that this URL design assumes you have a current_user/session (which you do if you're using Authlogic) so that you don't need to pull the id of the user in from the actual URL.
whazzmaster
but doesn't errors get lost in redirects?
pcasa
You are correct, sir. From here there's two options: work to preserve the errors through a redirect (see http://stackoverflow.com/questions/1536428/rails-validation-over-redirect) or rethink the solution so we don't have to worry about that. I'll put the latter idea into a different answer...
whazzmaster
This works great. Any idea how to lockout current_user paths
pcasa
It would involve removing map.resource :users and custom mapping the remainder of the calls to specific urls in routes.rb. IMO, resources work really well for things in your app that are actually... resources (books, accounts, orders, etc.) but not so well for users, if you want pretty, compact urls. I'm working on an app right now where I bootstrapped the user-framework with map.resource :users and then once I had forms written and validation working I removed the map.resource and custom mapped nice urls to the existing actions.
whazzmaster
Sounds great. would definitely help. As is too many ways to get to user. Not worried so much about everyday users but more from outside attacks. BTW - This did work and your form_tag was the key for returning to the right URL.
pcasa