Rails REST routing: dots in the resource item ID

11,367

Solution 1

You could replace periods with another character:

def to_param
  login.gsub(/\./,"-") # note: 'self' is not needed here
end

user = User.find_by_login("bart.simpson")
user_path(user) # => "/users/bart-simpson"

EDIT

You're right, this fails to deal with unique logins that map to the same value. Maybe a better way is to use segment constraints in the route:

  match 'users/(:id)' => 'users#show', 
    :constraints => { :id => /[0-9A-Za-z\-\.]+/ }

This should allow "/users/bart-simpson" and /users/bart.simpson" to generate :id => "bart-simpson" and :id => "bart.simpson" respectively. You'd have to alter the regex to add all the acceptable characters for the URL.

Note that this is mentioned in the Rails Routing Guide, section 3.2:

By default dynamic segments don’t accept dots – this is because the dot is used as a separator for formatted routes. If you need to use a dot within a dynamic segment add a constraint which overrides this – for example :id => /[^\/]+/ allows anything except a slash.

Solution 2

The following constrain definition permit the dot in id as well as any character except slash.

Supported formats must be explicitly defined (here .html and .json) to not to be taken by id.

resources :foobars,
  :constraints => { :id => /[^\/]+(?=\.html\z|\.json\z)|[^\/]+/ }

That constrain definition is worked with Rails 3.1

For earlier Rails versions you may need to backport look-ahead support in regin gem (it is vendored in rack-mount gem)

Solution 3

To allow the :id segment to contain any character except '/':

match 'users/(:id)' => 'users#show', :constraints => {:id => /[^\/]+/}

It's written elsewhere in one of the answers, but this is IMO the simplest way.

Share:
11,367

Related videos on Youtube

sandrew
Author by

sandrew

Updated on May 23, 2022

Comments

  • sandrew
    sandrew almost 2 years

    I have following in my routes.rb:

    resources :users, :except => [:new, :create] do
        get 'friends', :as => :friends, :on => :member, :to => "users#friends"
    end
    

    and following in my user.rb:

    def to_param
      self.login
    end
    

    And when, for example, user with dots in login (for example 'any.thing') comes from facebook, rails gives routing error (no route found, I suppose that's because it recognises anything after dot as a format or because of route constraints). How can I come over this error?

  • sandrew
    sandrew about 13 years
    replacing dots with another character is not the solution, because 1) we use connection through many oauth providers, and "bart.simpson" and "bart-simpson" could be two different users 2) it becomes more complicated to search from users on show action (for example User.find_by_login("bart-simpson") will not find user with login "bart.simpson"). self can be omitted, but it is good technique to leave it there, because it makes method context more clear, and in our company it is a standarded coding style
  • zetetic
    zetetic about 13 years
    Good point. See my edit for an alternative method. I must say I disagree with a coding standard that requires self where it is not needed, but that's not really pertinent.
  • Shaun McDonald
    Shaun McDonald almost 13 years
    Is there a way to allow dots in the id, and also accept a limited range of formats?
  • chech
    chech over 11 years
    To use the first technique and have unique id's, first replace '-' with '$-', then replace '.' with '%-'. Haven't tested if $ and % are valid, it's just an example, but that makes both - and . correspond to a unique value and can be converted backwards.
  • Blue Smith
    Blue Smith over 10 years
    Great tip. Thank you :)
  • Carson Reinke
    Carson Reinke almost 10 years
    Or :constraints => {:id => /[^\/]+(?=#{ ActionController::Renderers::RENDERERS.map{|e| "\\.#{e}\\z"}.join("|") })|[^\/]+/}
  • Joshua Pinter
    Joshua Pinter over 5 years
    @emptywalls It was correct in the answer, but the slash was being removed when rendered. I changed it to use back ticks to retain all the characters. Thanks for pointing this out!