How do I write a route that is SEO friendly in rails? - ruby-on-rails

I am changing my site over from Google App Engine to rails and I would like to keep my place in google search. Currently my site uses a URL /page?pid=microsoft-interview-questions to access the Microsoft subsection of interview questions. How would I create a route that can send this to '/tags/:id' where :id would be microsoft in this case?

In addition to josh's answer I'll put this here for formatting:
# your controller
def show
#subject = Subject.find my_stripped_id
private
def my_stripped_id
params[:id].sub(/-interview-questions/, '')
end

something like this should work (in routes.rb):
map.connect '/page?pid=:number',
:controller => 'tags', :action =>
'show'
see routes reference

Related

How to change will_paginate URLs?

I'm trying to change the will_paginate links from the format of /list?page=1 to /list/page/1. I have the routes setup right for this, but will_paginate is using the query string in links rather than my pretty URL style. How to tell it otherwise?
I'm using will_paginate 2.3.16 and Rails 2.3.14.
will_paginate uses the url_for helper. If you define routes containing the parameters it uses, it should generate pretty urls.
map.connect '/lists/page/:page', :controller => 'lists', :action => 'index'
Please make sure, you define this route above any routes that could also match your controller action. Even better: exclude the index action like this:
map.resources :lists, :except => [:index]
You have several options here:
1st option: monkey patch WillPaginate::ActionView::LinkRenderer#url, which currently has the following url logic:
def url(page)
#base_url_params ||= begin
url_params = merge_get_params(default_url_params)
merge_optional_params(url_params)
end
url_params = #base_url_params.dup
add_current_page_param(url_params, page)
#template.url_for(url_params)
end
So, I imagine that you can do something like "/list/page/#{page}" instead.
The other way is to fully implement the renderer (by subclassing WillPaginate::ViewHelpers::LinkRenderer), and then providing it as :renderer => MyRendererClass when calling will_paginate.

Correct method for custom Rails routing

In my routes I currently have resources :users and so I get the routes such as /users/id/ and /users/id/edit and so on...however, I am wanting to have the 'default' URLs for my pages begin with /name where name is the user's unique login name.
So, right now my routes file looks like this.
resources :users
match '/:name' => 'users#show_by_name'
Then in my users_controller.rb file, I have methods defined as such...
def show_by_name
#user = User.find_by_name(params[:name])
render 'show'
end
So, basically it is doing the same thing as def show but instead of an id being passed in the URL, it's a name.
In my views I am linking like this...
<li><%= link_to "My Profile", "/#{current_user.name}" %></li>
As opposed to using <li><%= link_to "My Profile", current_user %></li>
I am wondering if I am going about this the correct way. I feel like I am doing something unnecessary by using extra methods in my users_controller.
Would I be better off just removing the resources :users line and creating my own routes that are more suited towards the type of URLs I want on my application?
Thanks for your time.
You might be better off overriding the to_param method in your User model. Rails has in built function for search friendly URL's
class User < ActiveRecord::Base
def to_param
"#{user.name}"
end
end
Url's will generate as
user_url(#user)
#http://0.0.0.0:3000/users/andrew
# Controller
#user = User.find_by_name(params[:id])
I would advice you to use FriendlyID, it's a neat gem that translates the :id to a value based on one of the table's columns. This could be a title for instance or name in your case.
I found it fairly easy to start using.
Ryan Bates talks about it in this screencast: http://railscasts.com/episodes/314-pretty-urls-with-friendlyid
For installation look here: https://github.com/norman/friendly_id
Both Andrew and Martin are right (the FriendlyID gem actually uses the to_param override method), but you're asking two questions :
can I use another attribute instead of the default id as the id in the route ?
can I use a non-resourceful route ?
In both cases, the answer is yes. You may override the to_param method AND use a non-REST route such as :
match '/:id' => 'users#show'

Static pages in Ruby on Rails

What are the standard way of making a Ruby on Rails application that will have pages such as
Home
About
Contact
I would appricate if someone had links or an answers rather than just say use a gem because I want to learn how to make simple webapps with such behavior.
Depends on how you want to handle the content in those pages.
Approach #1 - store content in views
If you just want to put all your content in ERB views, then a very simple approach is to create a PagesController whose purpose is to deal with static pages. Each page is represented by one action in the controller.
pages_controller.rb:
class PagesController < ApplicationController
def home
end
def about
end
def contact
end
end
routes.rb:
match '/home' => 'pages#home'
match '/about' => 'pages#about'
match '/contact' => 'pages#contact'
Then create home.html.erb, about.html.erb, and contact.html.erb views under app/views/pages. These views contain whatever content you want on your static pages. They'll by default use your app's application.html.erb layout.
You'll also want to look into page caching to give yourself a boost in performance.
Approach #2 - store content in database
Another approach I've used is to make a very basic CMS for static pages. In this case, pages are represented in the model. It uses the friendly_id gem to handle slugs for each page so that they can be retrieved by a pretty name in the URL (e.g., /about) rather than by ID.
page.rb:
class Page < ActiveRecord::Base
attr_accessible :title, :content
validates_presence_of :title, :content
has_friendly_id :title, :use_slug => true, :approximate_ascii => true
end
pages_controller.rb:
class PagesController < ApplicationController
def show
#page = Page.find(params[:id])
render 'shared/404', :status => 404 if #page.nil?
end
end
show.html.erb:
<%= raw #page.content %>
routes.rb:
match '/:id' => 'pages#show'
Note: put this entry at the end of routes.rb since it matches everything.
Then how you want to create, edit and update pages are up to you - you can have an admin interface, or build it in to your public interface somehow. This approach can benefit from page caching too.
Another option is the high_voltage gem: https://github.com/thoughtbot/high_voltage
This makes it super easy to create static pages where the content is stored in views.
Jeff's approach #1 (storing content in views and having a route and controller action for each static page) is a good one. The only thing I would add is to use the controller macro in your routes.
So, instead of this:
match '/home' => 'pages#home'
match '/about' => 'pages#about'
match '/contact' => 'pages#contact'
You can do this:
controller :pages do
get :home
get :about
get :contact
end
It's two extra lines, yet much so much more elegant, since it eliminates repetition and groups your static page routes together visually.
It also uses the get http verb method instead of match, which is a better practice for Rails routes (and more concise, now that Rails 4 requires the http verb to be specified when using match.
Jeff's Approach #1 works great for me. Here is a trick to make the controller dynamically look up pages. With this, you don't need to touch the controller nor the routes.rb for adding pages. Just drop the pages under app/views/pages and the controller will find it.
class PagesController < ApplicationController
def show
render params[:id]
end
end
Check out Michael Hartl's http://railstutorial.org
which comes in a 2.3.8 and 3.0.x version. It covers this with great examples and leads you through building them very early on and you will also have the opportunity to learn a lot more than this example.
I highly recommend it.
config/routes.rb
get ':id', to: 'pages#show'
app/controllers/pages_controller.rb
class PagesController < ApplicationController
def show
begin
render params[:id]
rescue ActionView::MissingTemplate
render :file => "#{Rails.root}/public/404", :layout => false, :status => :not_found
end
end
end
Then place your static pages in app/views/pages/{name}.html.erb (or whatever template format.)
An adequate answer to your question would basically look like an introduction to the Rails framework: the MVC structure, templating, and routing DSL at least. Jeff has given a good stab, but his answer still assumes a lot of basic Rails knowledge on your part.
I'd suggest though, that if your webapp is really that simple, Rails might be overkill. I'd look into something lighter, like Sinatra, which has a much lower learning curve than Rails and does a great job of this kind of thing without having to deal with complex routing, magic MVC action/template mapping, etc.
I'd suggest adding your pages in the public folder so as to be served directly without having to pass through rails at all. I'm not an expert though so I'm not sure if this could have any cons if the page is static.
For more you can create static pages using Jekyll bootstrap or also Jekyll using Danger blog
Refer it is very helpful.

Rails: Difference between List and Index

I appreciate this is an incredibly noob question, but having Googled multiple combinations of search terms I regretfully am still in the dark. These things can be difficult when one doesn't know and so obvious when one does.
I have semi-home page where incoming customers can choose to see a queried list or do a handful of other things. This isn't a home page but a sort of mini 'switchboard' within the site.
The seven standard RESTful Rails controller methods are (as I understand them):
List # shows a list of records generated with .find(:all)
Show # shows details on one record
New # initiates new record
Create # saves and renders/redirects
Edit # finds and opens existing record for edit
Update # updates attributes
Delete # deletes record
What to use when some users need to see a selected 'list' of records that isn't literally .find(:all)? How would this work given I still need a list function that gives me .find(:all) for other purposes?
I've heard of 'index' being used in Rails controllers, but I don't know the difference between index and list.
For best practice and best design, what controller methods would you use for a mini-switchboard (and other intermediate pages such as 'About Us')?
Any specific answers would be a bit more useful than links to http://guides.rubyonrails.org/action_controller_overview.html etc. :) Thanks very much.
First, I think it's important to note that the "standard methods" are neither standard nor methods in a sense. These are considered actions, and are only standard in that they are the conventions used with scaffolding. You can create any number of actions and group them logically with a controller.
If you open up [Project]/config/routes.rb and read through the comments, I think you'll understand a little better how controllers and actions map to a specific route. For instance, you can create a named route to the login controller's login action and call it authenticate by adding to the top of your routes.rb:
# ex: http://localhost/authenticate
map.authenticate 'authenticate', :controller => 'login', :action => 'login'
# map.[route] '[pattern]', :controller => '[controller]', :action => '[action]'
# ex: http://localhost/category/1
map.category 'category/:id', :controller => 'categories', :action => 'list'
# ex: http://localhost/product_group/electronics
map.search 'product_group/:search', :controller => 'products', :action => 'list'
To partially answer your question, you may want to consider adding a category model and associating all products to a category. then, you can add a named route to view items by category as in the code block above.
The major benefit to using named routes is that you can call them in your views as category_url or category_path. Most people don't want to do this and rely on the default route mappings (at the end of the routes.rb):
# ex: http://localhost/category/view/1
map.connect ':controller/:action/:id'
# ex: http://localhost/category/view/1.xml
# ex: http://localhost/category/view/1.json
map.connect ':controller/:action/:id.:format'
The key thing to mention here is that when a URI matches a route, the parameter that matches against a symbol (:id, or :search) is passed into the params hash. For instance, the search named route above would match a search term into params[:search], so if your products have a string column called 'type' that you plan to search against, your products controller might look like:
class Products < ApplicationController
def list
search_term = params[:search]
#products = Product.find(:all, :conditions => ["type = ?", search_term])
end
end
Then, the view [Project]/app/views/products/list.html.erb will have direct access to #products
If you'd really like an in-depth view into Ruby on Rails (one that is probably 10 times easier to follow than the guide in the link that you posted) you should check out
Agile Web Development with Rails: Second Edition, 2nd Edition

renaming routes (map, link_to, to_param) in rails

I'm having a little issue...I setup a rails application that is to serve a german website. To make use of Rails' internal pluralization features, I kept all my models in english (e.g. the model "JobDescription").
Now, if I call "http://mysite.com/job_descriptions/", I get all my job_descriptions....so far, so good. Because I didn't want the english term "job_descriptions" in my url, I put the following into my routes.rb
map.german_term '/german_term', :controller => 'job_descriptions', :action => 'index'
map.german_term '/german_term/:id', :controller => 'job_descriptions', :action => 'show'
If I call "http://mysite.com/german_term/" or "http://mysite.com/german_term/283" I get all my job_descriptions, which is fine.
However, to make the URL more SEO friendly, I'd like to exchange the id for a more userfriendly slug in the URL. Thus, I put the following in my job_description.rb:
def to_param
"#{id}-#{name.gsub(/[^a-z0-9]+/i, '-')}"
end
which, whenever I use "job_description_path" in any link_to method, renders my URLs out to something like "http://mysite/job_descriptions/13-my-job-description-title".
However, and this is where I'm stuck, I'd like to get "http://mysite/german_term/13-my-job-description-title". I already tried to exchange the "job_description_path" with "german_term_path" in the link_to code, but that only generates "http://mysite/german_term/13". Obviously, to_param isn't called.
One workaround I found is to build the link with:
<%= link_to job_description.name, german_term_path(job_description.to_param) %>
But that's rather tedious to change all the link_to calls in my code. What I want is to replace "job_description" by "german_term" whenever it occurs in a URL.
Any thoughts?!?
Regards,
Sebastian
I think you're going to need to use the restful route helpers to get what you want.
In that case, it wouldn't take much re-factoring (assuming you've mapped JobDescriptions as a resource). Leave your to_param as is and change your JobDescriptions route to something like the following:
map.resources :job_descriptions, :as => 'german_term'
Hope this helps!
Rails only utilizes the
def to_params
end
URL builder when you are using a restful route/link helper. The only way I am aware of is to do it similar to how you did, unless you are willing to just scrap your english language links and do it all in German. In that case, just get rid of the named route lines and change the to_params to use the correct name field from the database. At that point, the REST routes should behave correctly.

Resources