Rails 3 Routing resources scoped to a username - ruby-on-rails

I have a basic understanding of rails routing, but nothing too advanced. So far I've gotten by using the RESTful resource based routes and a few custom named routes.
I am nearly done my app now though and I wanted to make some pretty urls.
In my app, each user has many pages. What's the best way to make the URL's look like www.sitename.com/username/page_name?

This will route to the pages controller's show action. Params hash includes :username and :page_name.
match "/:username/:page_name" => "pages#show"
Remember to put it last or it will match pretty much everything.

I'm not quite sure what you're using this for, but something like this might work in your routes file:
resources :users do
get 'page_name'
end
Which will produce: users/:id/page_name
You might want to check out the Railsguide on routing.

What you are looking for is a member route (section 2.9.1).
resources :users do
member do
get :cool_page
end
end
Will result in /users/:id/cool_page

Related

What's the "Rails way" to route a "diff" between two instances of the same model?

I'm building a "Brand personality" tool that gives you a report based on the text you share on social media.
I have a model PersonalityReport and in routes I have resources :personality_reports.
A new feature is to offer a "diff" between two reports and I'm trying to work out the most "guessable" way to model this in routes.
Ideally I'd like GET /personality_reports/:personality_report_id/diff/:id or something along those lines, and while I could simply put that into routes as a GET route, is there a more Railsy way of specifying a route using the resources / collections style so that my routes.rb file is more easy to understand?
The 'neatest' way can think of is:
resources :personality_reports, param: 'personality_report' do
member do
get 'diff/:id', to: 'personality_reports#action', as: 'diff_route'
end
end
Where obviously to: is your controller#action, and as: is the name of your route. After running rake routes you will see this generates:
diff_route_personality_report GET /personality_reports/:personality_report_id/diff/:id(.:format) personality_reports#action
I think whatever you mentioned is good enough,
resources : personality_reports do
resources :diffs, only: [:show]
end
So, routes like below,
personality_report_diff GET /personality_reports/:personality_report_id/diffs/:id(.:format) diffs#show
NOTE: You can also make diff route in singular resource :diff if you want to make it as singular resource.

Nested routes in rails

I am someone who has always liked sinatra better than rails, and has never had to do a large enough scale project that rails was required (all the sources I have read say that rails is better for larger scale projects) and now I do have to do a large scale project. I have gotten very confused with the url structure of rails. What I am trying to do is the rails equivalent of this:
get "/" do
erb :index
end
get "/home" do
erb :dashboard
end
get "/home/profile" do
erb :profile
end
get "/home/friends" do
erb :friends
end
In the first one I understand that I should put in app/routes.rb I should put root home#index and in the home controller I should put def index end.
In the second one, I understand that I should do the same except replacing index with home.
But for the third and forth ones I have no idea what to do.
Also, is the a RESTful way to do the first two?
You probably want something like this
root 'home#index'
get 'home' => 'home#dashboard'
get 'home/profile' => 'home#profile'
get 'home/friends' => 'home#friends'
remember to use the command rake routes to see all your routes, where they lead and what their names are (if they have any)
I never understood what RESTful means, so someone else will have to answer that part of your question.
K M Rakibul Islam has shown you what can be called a "resourceful" way to do routes (because it uses the keyword resources) but it looks like you're just doing the static pages at this stage, so it's not necessary.
The simplest way to do routes is with this formula:
method url => controller::action, as: route_name
where method can be get, post, patch or delete so you can have different actions linked to the same URL depending on the method the request uses.
Putting a name on the route is optional, but it gives you a clean way to use the route in your views (route_name_path)
When you start making models then you'll find that using the resources keyword comes in handy. Read about it.
You can have this:
resources :home do
collection do
get :profile
end
collection do
get :friends
end
end
end
This will give you routes like this:
profile_home_index GET /home/profile(.:format) home#profile
friends_home_index GET /home/friends(.:format) home#friends
The standard way of declaring the root path:
root 'home#index'
And for the 2nd one, you have to do:
get 'home' => 'home#dashboard'
which will give you this route:
GET /home(.:format) home#dashboard
One route can be defined in many ways that works. But, Rails has conventions that should be followed while defining routes in your Rails app.
I would highly recommend you to take a look at the Rails Routing Guide

Creating an alias (i.e. second name) for a model in Rails

I have a production Rails 2.3.5 website, and I'd like to the change the name of a model to something that will look much better in the URLs. I know the easy way to do this is in routes.rb like this:
map.resources :announcements, :as => :posts
However, I need to support the old name as well since we're production and can't have dead links. Just using ':as' isn't going to cut it.
I'm basically looking for a way to redirect so that:
http://mysite.com/announcements/23
redirects to
http://mysite.com/posts/23
It's probably possible to do this through Apache, but I can't seem to figure out the rewrite rules. I thought maybe routes.rb would be an easier method. Not having luck there either.
Thanks!
Depends on what we mean by "redirect" - it sounds like the most efficient route might be to take your posts controller and add redirects to announcements, and let the announcements controller handle the pages. So, in PostsController:
def index
redirect_to 'announcements#index'
end
and so forth. That gives you legacy support for old links for as long as you want it, and if there ever comes a time that you don't want it anymore you can just drop the whole Posts Controller.
I ended up finding a solution that works out pretty well.
I added the :as => :posts to my routes.rb, which makes all the link helpers create links to the new URLs. Then I figured out a mod_rewrite rule to handle redirects so there aren't any broken links out there on the internets. Here's what I added to my Apache config:
RewriteEngine On
RewriteRule ^/announcements(.*) /posts$1 [R=301,L]
How about adding the route two times. With :as option and without?
//routes.rb
map.resources :announcements
map.resources :announcements, :as => :posts

Shallow routing in Rails 3

Having a rough time with my routes in my rails 3 app, I want to have shallow routes like this:
/san-francisco/union-square
But my router insists on having them like so:
/cities/san-francisco/neighborhoods/union-square
I've used this for my routes.rb
shallow do
resources :cities do
resources :neighborhoods do
resources :locations
end
end
end
But still I have this:
city_neighborhood_locations GET /cities/:city_id/neighborhoods/:neighborhood_id/locations(.:format)
Shouldn't it look like:
city_neighborhood_locations GET /:city_id/:neighborhood_id/:id(.:format)
I'm not sure how to fix this, additionally I'm not sure what I'm doing wrong with my links, I want to be able to use the syntax:
link_to neighborhood.name, [:city, neighborhood]
but that seems to invert the :id, and :neighborhood_id when the request comes to the controller, any help on this would be really really helpful!
What you're looking for is not typically known as "shallow routing".
Shallow routing, as Rails defines it, would look like this:
city_neighborhoods GET /cities/:city_id/neighborhoods
new_city_neighborhood GET /cities/:city_id/neighborhoods/new
(create_city_neighborhood) POST /cities/:city_id/neighborhoods
neighborhood GET /neighborhoods/:neighborhood_id
Without shallow routing, that last route would be:
city_neighborhood GET /cities/:city_id/neighborhoods/:neighborhood_id
Shallow routing lets you nest a resource underneath another resource (neighborhood under city in this case), but gives you absolute/un-nested routes for the nested resource when the nesting isn't necessary.
This makes sense when you're referencing the nested resource with unique identifiers that are not dependent on the ID of the parent resource. In your example, that's not true (there's potentially a "Union Square" outside of San Francisco; there's definitely going to be duplicates like "Chinatown"), so you probably do not want shallow routes for this case.
What you're wanting is positionally-dependent routing, where the type of resource is assumed/fixed depending on where it appears in the URL. (For instance, you couldn't have anything other than a "neighborhood" follow a "city" under the scheme you outlined.)
I don't think the Rails resource(s) commands will support that by default, but you could probably do it with manual match commands. This is off the top of my head:
match ":city_id", :controller => "cities", :action => "show"
match ":city_id/:neighborhood_id", :controller => "neighborhoods", :action => "show"
This is still RESTful/resource-based, it's just not using the standard Rails way of naming routes.

Do you have to mess with Rails's "routes.rb" file?

I never touch routes.rb beyond calling map.root to set a default route. I've always been content to use URLs of the form...
/controller/action/perhaps_an_id
and it works fine.
Does this make me a bad person? Am I missing out on something totally awesome?
What if I try to adopt RESTful design? Would that mean I have to edit routes.rb or could I continue to pleasantly ignore it?
(I tried to read up on this topic in The Rails Way but it was unbearable.)
If you generate your resources with the default scaffolding then it will even include the restful routing for you in routes.rb.
If you're not using the scaffolding then the reason that it's working is because of the default routes at the bottom by default:
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
I've been following that it's best practice to remove these for production applications and instead make sure that only the resources that need to be exposed are exposed. With Rails 2.2 you can even limit the RESTful methods from map.resources by:
map.resources :posts, :only => [:index, :show]
map.resources :comments, :except => [:edit]
There's also tons of cool things you can do with nested resources, named routes, etc. They have a lot of examples in the docs (http://www.railsbrain.com/api/rails-2.2.2/doc/index.html?a=M000255&name=resources)
You may also want to make custom named routes for your marketing department (eg: mycoolsite.com/free-trial) that go off to specific controllers and actions, etc.
Ryan Bates has a series of screencasts that go over some of the neat things you can do with routes: http://railscasts.com/tags/14
Not having switched to RESTful design does not make you a bad person and if you feel no need to change keep writing your apps the 1.x way.
The majority of Rails developers has adopted REST and seems to be very happy about it. I don't think there is a need here to repeat all pro REST arguments.
You do need to add one line per resource to your routes file such as:
map.resources :posts
If you were to go RESTful, yes you would have to edit routes.rb and add your resources like,
map.resources :your_resource
or if you have nested resources,
map.resources :people do |person|
person.resources :ideas do |idea|
ideas.resources :bad_ones
end
end

Resources