Things to change when using username slug/short URL in rails - ruby-on-rails

I'm pretty new at rails, so forgive me if I'm overlooking simple things or the rails way. My objective is to completely replace URLs of the form
/users/1
with
/username
for all purposes. (I think exposing IDs scaffolding publicly is like walking around with a bone sticking out of your arm.) But implementing seems a little more complicated than I expected. This seems to change the entire way rails indexes and looks up users, rather than just substituting a lookup method.
Although I've kind of gotten this to function using the to_param override in my user.rb file, I've read this means I'll have indexing problems down the road when using params([:username]), and I'm not sure how it will impact my
(a) session model at new user creation, and
(b) #User usage in the user/show.html.erb file.
So, I've either consulted the following pages (or asked the questions):
Ruby on rails routing matching username
customize rails url with username
Routing in Rails making the Username an URL:
routing error with :username in url
Correct routing for short url by username in Rails
rails3, clean url, friendly_id, sessions
The major issues I'd like to understand from this question:
What functionality do I lose by transitioning to this? That is, what things currently "just work" in rails that I'll have to address and rewrite if I pursue this replacement?
As a practice, is this something better to replace with friendly_id? My concern here is that creating a slug column in my DB identical to the username seems a little non-DRY and makes me uncomfortable, and I'd rather avoid dependencies on external gems where possible.
What does my users#show need to look like?

You should check out Friendly ID. Makes doing what you're trying to do incredibly easy.
https://github.com/norman/friendly_id
There's a Railscast for it, too.
http://railscasts.com/episodes/314-pretty-urls-with-friendlyid?view=asciicast

If your username contains a special characters like #, -, . and got an error that says "No route matches" then you need to filter its route. See below:
match "/user/:username" => 'users#show', :as => :profile, :username => /[\.a-zA-Z0-9_#-]+/

After working around this for a couple weeks, I'd say the best answer as of Aug 2, 2012 is that if you do this, you violate many rails conventions and rip apart the very fabric of time and space itself.
Ugly scaffolding in the URLs is a necessary part of rails' approach to RESTfulness.

Related

Ruby on Rails database driven router to support fully customizable urls

I'm planning to port our current cms (written in PHP) to Rails. All parts do well, except for one: routing.
Like most cms systems, the routing in our cms based on a database with pages, which are linked to modules, controllers and actions. On this approach a user can fully customize or specify it's own urls.
I know that Rails (and most (application) frameworks have the approach of defining routes in a file, but I hope this is possible.
The approach our users should have is:
add new page
select type (news, form, product, ...)
select an item (select which form, blog or product should be displayed)
enter a url for that page
Special the last point (4) is important. A user should be able to add form A to /contact-us, and form B to /clients/register-as-new-client e.g.
On a request the router needs to do a database query with the page url, to find out which controller, task and parameters should be dispatched.
Question has been updated, and i don't think this is a valid answer anymore
we have a similar paging system. we use a routing glob. in routes.rb:
get 'pages/*lookup_path', to: 'pages#show', defaults: { format: 'html' }, as: 'page'
Just parse params[:lookup_path] in PagesController to suit your needs
'http://localhost/pages/users/'
params[:lookup_path] #=> users/
'http://localhost/pages/users/23'
params[:lookup_path] #=> users/23
'http://localhost/pages/people/1'
params[:lookup_path] #=> people/1
Although this solution isn't ReSTful, I think this should solve the issue.
Regardless, Rails uses routes in a file. You cannot change this since the framework heralds "convention over configiuration". All I can do is point you in a direction to minimize this.
There is a catchall route in Rails (on RailsCasts, and on StackOverflow) which you can use to direct all routing to one controller action. You may further customize the routing behaviour in that method
You could also make a route like…
:controller/:action => Controller::Action
…as is done in CodeIgniter, but now your methods have to have names like contact-us and register-as-a-new-client.

Ruby on Rails: Implementing search. How should I format my route for many parameters?

I'm building my first Ruby on Rails application, and I want to do it the "Rails" way, making my application as RESTful as possible.
The application revolves around a search function, in which a user fills out a form defining what they are looking for, submits it, then pages through their results, viewing items they are interested in. From my understanding of REST, it sounds like I should be encoding my parameters into the URL for my results page(?)
If my base route is:
match "search" => "search#index"
And the base route for my search results are:
match "search/results" => "search#results"
What would be the best way of implementing routes/URLs/parameters for my search? The solution must:
Support many parameters (30+), of which, any could be optional.
Support pagination: there could be alot of results.
Adhere to the "Rails way"!
Any suggestions/tips from you Rails pros would be very helpful, thanks!
For pagination: you can use a gem like will_paginate or kaminari
Concerning search parameters: Rails will put anything in the request's query string into a params variable accessible within your controllers and views.
What you're trying to do (naming the urls search & search/results) doesn't really align with "restful routes". Still, this is what you've asked for.
scope :search do
get 'search' => 'search#index'
get 'search/results' => 'search#show'
end
will produce
search GET /search(.:format) search#index
search_results GET /search/results(.:format) search#show
To remain restful, try discerning what you're searching for... for products, the results would be displayed using Products#index.
If you want to keep the search broad, I'd suggest Search#new and Results#index. These latter options seem more RESTful.
As you are probably anticipated, passing those 30 params around as url parameters is probably not the best approach.
I would consider creating a Configuration model (and table) for them and focus on the form being able to update this model. This will make storing the searches, doing multiple searches, validating the searches much easier in IMHO. Use this model in conjunction with the actual model, to do the searches.
For the pagination, as rthbound says use will_paginate or kaminari.

Friendly URLs in Github

How has Github managed to get friendly URLs for representing repos of users? For a project called abc by username foo, how do they work around with a URL like: http://github.com/foo/abc. Are they fetching the abc model for the DB from the title in the URL (which sounds unreasonable as they are modifying the titles). How are they transferring the unique ID of the abc repo which they can fetch and show in the view?
The reason I ask is that I am facing a similar problem of creating friendlier URLs to view a resource. MongoDB's object IDs are quite long and make the URL look horrific. Is there a workaround? All the tutorials that demonstrate CRUD (or REST) URLs for a resource always include the object's unique ID(e.g. http://mysite.org/post/1 or http://mysite.org/post/1/edit. Is there a better way to do it?
Not having seen their code, I couldn't tell you exactly how they do it, but if you're using Rails there are at least two Ruby gems that will give you similar results:
Take a look at Slugged and friendly_id
http://github.com/foo/abc is a unique repository identifier (for that repo's master branch). I'd assume that somewhere they have a table that looks like:
repository-id | user-id | project-id
and are just looking up based on user and project rather than repository-id.
You'd need to do some domain-specific mapping between internal and user-friendly ids, but you'd need to make sure that was a 1:1 mapping.
See this rails cast on methods, gems and solutions to common problems you might get while modifying the application to use friendly urls.
http://railscasts.com/episodes/314-pretty-urls-with-friendlyid?view=asciicast
(although Ryan Bates deserves the rep+ for this)
I mocked a structure like this using FriendlyID and Nested Resources.
Essentially, use friendly ID to get the to_param-ish slugs in your routes, then set up nested resources. Using GitHub as an example:
routes.rb
resources :users do
resources :repositories
end
Then in your controller, say, for repositories, you can check the existence of params[:user_id] and use that to determine the user from the route. The reason I check for existence is because I did something like (roughly):
/myrepositories/:repository_id
/:user_id/:repository_id
So my controller does:
def show
#user = params[:user_id] ? User.find(params[:user_id]) : current_user
end
I followed this tutorial here to get started with this same project.
This is called URL rewriting if the web server does it (such as Apache), and routing when it happens in a web application framework (such as Ruby on Rails).
http://www.sinatrarb.com/intro#Routes
http://httpd.apache.org/docs/current/mod/mod_rewrite.html

Ruby on rails routing matching username

I am sure there is a simple solution to this but I can't seem to find it.
I want to match the following dynamic username:
/users/dymanicUsername
How would this look in routes.rb
Thanks for any help.
allister,
To do that, just override to_param in your model. For instance :
#users.rb
def to_param
self.username
end
You will soon bump into some problems :
conflicts with existing routes. For instance, the username should not be 'new', got it ?
for fetching you user in the controller, you should create a method (that's what I did, mayybe not the best solution) in your model like this self.find_for_controller(username)
your username shall only contains url-enabled characters (forget accents, ponctuations etc...). A solution for this is to have a second attribute names username_urlized, that of course should be unique and not conflicting with other routes
And maybe more problems :)
Also, if you want something like twitter (yoursite.com/dynamicUserName), do the following in routes.rb :
resources :users, :path=>'' do
Here is a good explanation from 2006 which looks good for Rails 2.3.*:
http://garrickvanburen.com/archive/using-names-in-rails-routes-instead-of-ids
The main problems I see is that you would want to ensure not only all of the above that Marcel mentiones, but that the name is unique. ie put an index on name with :unique => true in the migration.
For rails3 to which I'm just now upgrading you would do the routing differently. For example map.connect is replaced by match.

URL rewriting in ROR

I am Amit. i am new to Rails. Please forgive me if ask any stupid
questions.
I have gone through this article. I am also suffering with the same
problem.
my website URL like this: locahost:3000/users/edit/30
I don't want to show the controller:users and action: edit.
I want to Re-Write (rewrite) the URL or i want to maintain the URL as
http://127.0.0.0:3000/30/ only.(30 is a user id)
I am not interested to show the controller(user) and action (edit)
I total intention is to Hiding (HIDING) and rewriting (REWRING) the URL
and mainly i want to hide the URL Extensions with controller and actions
mainly..
It's an odd requirement to want to use a route this way as it will make it difficult for you to expand this scheme to support other actions in your application. One of the advantages of Rails conventions done as they are is that you usually don't have to worry about (often trivial) application details or have to have strong opinions about them.
But it you really, really want to, you can add this to your config/routes.rb
ActionController::Routing::Routes.draw do |map|
map.connect "/:id", :controller => "users", :action => "edit"
end
Remember that this limited scheme will mean you can only route to the edit action. Not terribly useful I would suggest.
The rails routes.rb file is your answer:
Lots of information here on how to do exactly what you need:
http://guides.rails.info/routing.html#customizing-resourceful-routes
For most application I have been using the standard:
/script.cgi?arg1=foo&arg2=bar
Now obviously /user/edit/eric looks much better for a URL that involves
editing the "Eric" user. But how does this work out when you have
multiple items. For example if I want to delete a group of users. I
might have something like this in the old way:
/user/delete.cgi?id=3&id=5&id=7
or for a more readable format we could have:
/user/delete.cgi?username=eric&username=john&username=paul
This of course assumes that usernames are unique. :) So it seems that I
would want something like:
/user/delete/eric/john/paul
This makes it obvious what is happening from the url. But how do I
generate this? And if a action receives this request how does this get
parsed so I have something like:
#delete_usernames ===>>> [ 'eric', 'john', 'paul' ]
This way I can then load up those users and delete them. I know that
ActionController provides some methods for dealing with URL rewriting
but I am unsure how to best use them. Any help or pointers are greatly
appreciated.

Resources