Handling has_one nested resource in Rails 3 - ruby-on-rails

I got a User model and a About model. The about model is a page where users have more info about them that due its nature is more appropriate to have it on a separate model rather than in the user model.
I want to be able to route it to something like /:username/about and get all the verbs working on that path (GET POST, PUT, DELETE).
/:username/about
/:username/about/edit
/:username/about
This is what I already have
# routes.rb
resources :users do
resources :abouts
end
match ':username/about' => 'abouts#show', :as => :user_about
match ':username/about/add' => 'abouts#new', :as => :user_new_about
match ':username/about/edit' => 'abouts#edit', :as => :user_edit_about
And in the models I have
# about.rb
belongs_to :user
# user.rb
has_one :about
When I'm doing a post or put to /roses/about It's interpreting It as a show
Started POST "/roses/about" for 127.0.0.1 at Sun Feb 27 16:24:18 -0200 2011
Processing by AboutsController#show as HTML
I'm probably missing the declaration in the routes, but doesn't it get messy declaring each verb for a resource when it's different from the default?
What's the simplest and cleaner way to archive this?

When using a has_one, it might make sense to declare it as a singular resource in your routes. Meaning
resources :users do
resource :about # notice "resource" and not "resources"
end
And if you want to override the paths for new/edit, add a :path_names option to the resource/resources-call:
resources :about, :path_names => { :new => 'add', :edit => 'edit' }
The official documentation has lots of other tips and tricks for routing as well.

You can use scope and controller blocks to cut down on the verbiage:
scope "/:username" do
controller :abouts do
get 'about' => :show
post 'about' => :create
get 'about/add' => :new
get 'about/edit' => :edit
end
end
which produces:
about GET /:username/about(.:format) {:action=>"show", :controller=>"abouts"}
POST /:username/about(.:format) {:action=>"create", :controller=>"abouts"}
about_add GET /:username/about/add(.:format) {:controller=>"abouts", :action=>"new"}
about_edit GET /:username/about/edit(.:format) {:controller=>"abouts", :action=>"edit"}

Related

Rails 3 routes for single controller and multiple resources

I have multiple resources (:countries, :states, :schools etc.) but would like a single "Dashboard" controller to handle all the actions.
I would like to be able to do the following:
countries_path would direct me to a show_countries action in the DashboardController and be accesible by '/dashboard/countries.
Likewise for states, schools, etc.
I've read up on Rails routing and have been messing around with various options. I ended up with the following in my routes.rb file:
scope "toolbox" do
resources :countries, :controller => "toolbox", :only => :index do
get 'show_countries', :on => :collection
end
...
end
Running rake routes gives me the following for the code above:
show_countries_countries GET /toolbox/countries/show_countries(.:format) {:action=>"show_countries", :controller=>"toolbox"}
countries GET /toolbox/countries(.:format) {:action=>"index", :controller=>"toolbox"}
I've tried this:
scope "toolbox" do
resources :countries, :controller => "toolbox", :only => :index, :action => "show_countries"
end
only to get this route:
countries GET /toolbox/countries(.:format) {:action=>"index", :controller=>"toolbox"}
What I really want is this:
countries GET /toolbox/countries(.:format) {:action=>"show_countries", :controller=>"toolbox"}
Any ideas?
You just have to think outside of the 'resources' box:
scope "toolbox", :controller => :toolbox do
get 'countries' => :show_countries
get 'states' => :show_states
get 'schools' => :show_shools
end
Should output routes like this:
countries GET /toolbox/countries(.:format) toolbox#show_countries

Rails routing. Singular resource

I've got Rails routing problem. I would like to use singular resource with user controller but it doesn't work as I expected. Here is the fragment of my routes.rb file:
scope :module => "frontend" do
root :to => "home#index"
resource :user, :controller => "user"
get "/sign_up" => "user#new"
get "/sign_in" => "user#sign_in"
get "/sign_out" => "user#sign_out"
post "/authenticate" => "user#authenticate"
resources :articles
resources :article_categories
end
I thought it will work when I'll use for example "/user" or "/user/new" URL but it didn't. I get a routing error:
No route matches {:controller=>"frontend/user"}
The 'rake routes' command output is:
user POST /user(.:format) frontend/user#create
new_user GET /user/new(.:format) frontend/user#new
edit_user GET /user/edit(.:format) frontend/user#edit
GET /user(.:format) frontend/user#show
PUT /user(.:format) frontend/user#update
DELETE /user(.:format) frontend/user#destroy
sign_up GET /sign_up(.:format) frontend/user#new
sign_in GET /sign_in(.:format) frontend/user#sign_in
sign_out GET /sign_out(.:format) frontend/user#sign_out
authenticate POST /authenticate(.:format) frontend/user#authenticate
What is interesting, when I add route for index action in user controller, like this:
scope :module => "frontend" do
root :to => "home#index"
resource :user, :controller => "user"
get "/user" => "user#index"
get "/sign_up" => "user#new"
get "/sign_in" => "user#sign_in"
get "/sign_out" => "user#sign_out"
post "/authenticate" => "user#authenticate"
resources :articles
resources :article_categories
end
...it works!
But index action is not defined in user controller!
'rake routes' command returns double line for GET /user
GET /user(.:format) frontend/user#show
GET /user(.:format) frontend/user#index
so I suppose that's not the solution. Other actions assigned to '/users' URL don't work.
Is it necessary to define the route for the index action like
get "/controller_name" => "controller_name#index"
What am I doing wrong?
Defining a singular resource in your routes will not generate a route to an index action by design. The singular resource implies you're always going to lookup this resource without specifying an ID and consequently a get to index for a singular resource just doesn't make logical sense. So, a GET to your url "/user" will route to a show action for that singular resource and not an index.
EDIT: Since your issue isn't obvious, I'd simplify your routes until you can at least hit the controller you'd expect and then build from there.
config/routes.rb
scope :module=>"frontend" do
resource :user
end
#ensure you don't have any other user routes listed before this that would match "/user".
app/controllers/frontend/users_controller.rb
module Frontend
class UsersController < ApplicationController
def show
raise "in frontend/show"
end
end
end
Thanks a lot for help! I found the bug.
The routing error was caused by the following line of the layout html file
<%= auto_discovery_link_tag(:rss, {:action => "index"}, {:title => "RSS"}) %>
I was looking for errors in the erb view files but I forgot about the layout.
I must remember to check the entire view layer in such situations.

Non-RESTFul Rails XML api

I have a small API that I am writing in rails 3. This is not a restful application so I use a controller called api that has some methods such as:
def users
#users = User.all
respond_to do |format|
format.any do
render :xml => #users.to_xml
end
end
end
end
My routes (with comments etc removed) file looks like this:
resources :shows
resources :users
resources :comments
devise_scope :user do
get "/login" => "devise/sessions#new"
get "/logout" => "devise/sessions#destroy"
get "/register" => "devise/registrations#new"
end
root :to => 'home#index'
match ':controller(/:action(/:id(.:format)))'
When I call api/users I get the XML wrapped in HTML tags (in the body actually) but if I call api/users.xml I get a 406 error?
Do I need to change my routes to accommodate the XML call?
Thanks,
s
I added a new route to routes.rb and it worked:
match 'api/users.:format' => 'api#users', :constraints => {:format => /(xml)/}
j

How to route to different actions depending on the request method in Rails?

As we all know, a simple
resources :meetings
will generate 7 actions for me. Two of these are index and create. A really cool thing about these two!: The URL for both is /meetings, but when I GET /meetings I am routed to the def index action and when I POST /meetings, I am routed to the def create action. Nice.
Now I want to do this:
resources :meetings do
member do
get 'scores'
post 'scores'
end
end
And, you guessed it!, I want them to route to different actions in MeetingsController: GETting /meetings/1/scores will route to def scores and POSTing to meetings/1/scores will route to def create_scores.
Try:
resources :meetings do
member do
get 'scores' => :scores
post 'scores' => :create_scores
end
end
I suppose you will be also interested in having named routes:
resources :meetings do
member do
get 'scores' => :scores, :as => 'scores_of'
post 'scores' => :create_scores, :as => 'create_scores_of'
end
end
Then you get scores_of_meeting_path and create_scores_of_meeting_path helpers.
Above may be DRYed more with:
get :scores, :as => 'scores_of'
Define the routes such as this:
resources :meetings do
member do
get 'scores', :action => "scores"
post 'scores', :action => "post_scores"
end
end
But it sounds to me like it would be much easier to create another controller to handle this, as scores to me feels like another resource entirely, even if they don't have their own model association.
Ha! Never underestimate the ability of asking a question well to lead you to its answer.
resources :meetings do
member do
get 'scores', :to => "meetings#scores"
post 'scores', :to => "meetings#create_scores"
end
end

Setting up restful routes as a total newb

I'm getting the following error:
Unknown action
No action responded to show. Actions: activate, destroy, index, org_deals, search, and suspend
Controller:
class Admin::HomepagesController < Admin::ApplicationController
def org_deals
#organization = Organization.find(:all)
end
Routes:
map.root :controller => 'main'
map.admin '/admin', :controller => 'admin/main'
map.namespace :admin do |admin|
admin.resources :organizations, :collection => {:search => :get}, :member => {:suspend => :get, :activate => :get}
To note: This is a controller inside of a controller.
Any idea why this is defaulting to show?
Update:
I updated what the routes syntax is. Read that article, and tried quite a few variations but its still adamantly looking for a show.
Firstly, it looks like your routes file has the wrong syntax. If you are trying to establish routes for nested resources, you'd do it like so:
map.resources :admin
admin.resources :organizations
end
This would give you paths such as:
/admin/
/admin/1
/admin/1/organizations
/admin/1/organizations/1
The mapping from route to controller/action is done via a Rails convention, where HTTP verbs are assigned in ways that are useful for the typical CRUD operations. For example:
/admin/1/organizations/1
would map to several actions in the OrganizationsController, distinguished by the type of verb:
/admin/1/organizations/1 # GET -> :action => :show
/admin/1/organizations/1 # PUT -> :action => :update
/admin/1/organizations/1 # DELETE -> :action => :destroy
"Show" is one of the seven standard resourceful actions that Rails gives you by default. You can exclude "show" with the directive :except => :show, or specify only the resourceful actions you want with :only => :update, for example.
I recommend you look at Rails Routing from the Outside In, which is a great introduction to this topic.
EDIT
I see I ignored the namespacing in my answer, sorry. How about this:
map.namespace(:admin) do |admin|
admin.resources :homepages, :member => { :org_deals => :get }
end
This will generate your org_deals action as a GET with an id parameter (for the organization). You also get a show route, along with the six other resourceful routes. rake routes shows this:
org_deals_admin_homepage GET /admin/homepages/:id/org_deals(.:format) {:controller=>"admin/homepages", :action=>"org_deals"}
Of course your homepages_controller.rb has to live in app/controllers/admin/
EDIT redux
Actually, you want organizations in the path, I'll bet, in which case:
map.namespace(:admin) do |admin|
admin.resources :organizations, :controller => :homepages, :member => { :org_deals => :get }
end
which gives you:
org_deals_admin_organization GET /admin/organizations/:id/org_deals(.:format) {:controller=>"admin/homepages", :action=>"org_deals"}
By specifying admin.resources ... you are telling Rails you want the seven default different routes in your application. If you do not want them, and only want the ones you specify, do not use .resources. Show is called because that's the default route called for a GET request with a path such as /admin/id when you have the default resources.

Resources