jviotti
jviotti

Reputation: 18909

Globbed routes in Rails 3

I had my rails applications running OmniAuth-identity, saving users at "Users" model. Accesing users required pointing to:

0.0.0.0:3000/users/:id

but I wanted to get there by just typing the unique name of the user, like facebook or twitter:

0.0.0.0:3000/username

So I created a globed route:

match "/*id" => "users#show"

and modified the users controller show method to find by name instead of ID, depending on the paramter passed:

def show
    if params[:id].is_a?(String)
      @user = User.find_by_name(params[:id])
    else
      @user = User.find(params[:id])
    end
    respond_to do |format|
      format.html # show.html.erb
      format.json { render json: @user }
    end
  end

Routing works, If i go to

0.0.0.0:3000/username

It shows succesfully the user info. But loging out or finding the user like old routes are not working.. (even with the conditional I added to the controller checking if String||Int.

When doing this things I get:

NoMethodError in Users#show

Showing /home/juanchi/Dropbox/GitHub/RSandBox/app/views/users/show.html.erb where line #5 raised:

undefined method `provider' for nil:NilClass
Extracted source (around line #5):

2: 
3: <p>
4:   <b>Provider:</b>
5:   <%= @user.provider %>
6: </p>
7: 
8: <p>
Rails.root: /home/juanchi/Dropbox/GitHub/RSandBox

Application Trace | Framework Trace | Full Trace
app/views/users/show.html.erb:5:in `_app_views_users_show_html_erb___448583227_80374500'
app/controllers/users_controller.rb:22:in `show'
Request

Parameters:

{"id"=>"logout"}
Show session dump

Show env dump

Response

Headers:

None

It fails on getting the info at show method of users controller. But when using the new route it goes fine... So what is it missing?

How can I support my new routing rule and the old one at the same time?

And what does that route has to do with the user loggin out method?

When logging out it calls sessions#detroy method:

def destroy
    session[:user_id] = nil
    redirect_to root_url, :notice => "Goodbye!"
  end

Upvotes: 0

Views: 114

Answers (1)

Radek Paviensky
Radek Paviensky

Reputation: 8486

I'm not sure if I got the idea but I think that you don't need to use globbed routes.

You can use something like this:

get "users/:id" => "users#show"
get ":username" => "users#show"

And in your action you can check existence of parameter:

def show
  if params[:id].present?
    @user = User.find(params[:id])
  elsif params[:username].present?
    @user = User.find_by_name(params[:username])
  end
end

To check type of the parameter as you have in your code doesn't make sense as it's always String (or NilClass if it's nil).

And there is also one important thing - routes are ordered so you have to have your get ":username" ... somewhere at the end of the routing table to prevent conflicts with standard actions like /logout, /login, etc.

Actually it's not simple to eliminate those conflicts completely - imagine that you have user that chose the name "logout". So you have to add another logic that prevents such names for users.

Update: I forgot to explain why I think that globbing is not necessary - you need globbing to catch things like "foo/bar", "something/nice/in/rails" with one rule match "*xxx"

Upvotes: 1

Related Questions