Mysterious AbstractController::ActionNotFound (Route is there) - ruby-on-rails

(Rails 3.0.7)
My routes.rb has this:
namespace :admin do
namespace :campus_hub do
resources :billing_subscriptions, {
:except => [:destroy, :new, :create]
} do
member do
post :add_addon
end
end
end
end
rake routes shows this route:
add_addon_admin_campus_hub_billing_subscription POST /admin/campus_hub/billing_subscriptions/:id/add_addon(.:format) {:action=>"add_addon", :controller=>"admin/campus_hub/billing_subscriptions"}
My controller (Admin::CampusHub::BillingSubscriptionsController) has the method add_addon.
I do a POST that looks like this in the logs:
Started POST "/admin/campus_hub/billing_subscriptions/50059f5be628f83b13000012/add_addon" for 33.33.33.1 at Tue Jul 17 20:21:17 +0200 2012
And I get this error:
AbstractController::ActionNotFound (The action '50059f5be628f83b13000012' could not be found for Admin::CampusHub::BillingSubscriptionsController)
I'm totally baffled. The POST request I make matches the route exactly. Why does it think that the ID is the action? Hope I'm just missing something obvious!

I'm guessing from the rails version that you're having a similar problem to this: Routing error with Rails 3 with members which is a bug in rails 3, note comment: Routing error with Rails 3 with members
You'd need to replace:
member do
post :add_addon
end
With something like this:
match "add_addon" => "billing_subscriptions#add_addon", :as => :add_addon, :via => :post
You'd get a slightly swapped path like this: admin_campus_hub_billing_subscription_add_addon_path but it should work in rails 3 and 4.
All together it's like this:
namespace :admin do
namespace :campus_hub do
resources :billing_subscriptions, {
:except => [:destroy, :new, :create]
} do
match "add_addon" => "billing_subscriptions#add_addon", :as => :add_addon, :via => :post
end
end
end
Note that the full rake routes looks like this:
admin_campus_hub_billing_subscription_add_addon POST /admin/campus_hub/billing_subscriptions/:billing_subscription_id/add_addon(.:format) admin/campus_hub/billing_subscriptions#add_addon

Related

Cleaning up Rails routes with non-restful actions

I have a subscriptions controller with routes that look like this:
get 'user/:user_id/subscription_requests' => 'subscriptions#index', as: :subscription_requests
post 'user/:user_id/subscribe' => 'subscriptions#subscribe', as: :user_subscribe
post 'user/:user_id/unsubscribe' => 'subscriptions#unsubscribe', as: :user_unsubscribe
post 'user/:user_id/request_subscription' => 'subscriptions#request_subscription', as: :request_user_subscription
post 'user/:user_id/accept_subscription' => 'subscriptions#accept', as: :accept_subscription_request
post 'user/:user_id/reject_subscription' => 'subscriptions#reject', as: :reject_subscription_request
All of them except index are non-restful actions. How do you make this cleaner in the routes file using something like resources while keeping the user/:user_id/ in the path?
Update for clarity:
I'm trying to avoid listing the routes line by line and instead do something like what rails provides with the restful actions. Something like:
resources :subscription_requests, :only => [:subscribe, :unsubscribe, :reject, :accept, etc]
You can use the member function of routing.
resources :user, to: 'subscriptions', :only => [] do
member do
get 'subscription_requests'
post 'subscribe'
etc
end
end
And this will produce routes such as:
subscription_requests_user GET /user/:id/subscription_requests(.:format) subscriptions#subscription_requests
subscribe_user POST /user/:id/subscribe(.:format) subscriptions#subscribe
Docs: http://guides.rubyonrails.org/routing.html#adding-more-restful-actions

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

nested namespace route going to wrong controller

Using Rails 3.0.7, I'm creating an API for our app, and I have this setup:
routes.rb
namespace :api do
namespace :v1 do
match "connect" => "users#login", :via => :post
match "disconnect" => "users#logout", :via => :post
resources :users
match "users/:id/foos" => "foos#list", :via => :get
match "users/:id" => "users#update", :via => :put
match "foos/:id/bars" => "bars#list_by_foo", :via => :get
match "foos/:id" => "foos#show", :via => :get, :constraints => { :id => /\d+/ }
match "bars/:id" => "bars#show", :via => :get
end
end
# other routes here e.g.
match "users/find" => "users#find_by_name", :via => :get
match "users" => "users#create", :via => :post
And then I have my regular app/controllers/application_controller.rb and app/controllers/users_controller.rb files as well as my app/controllers/api/v1/application_controller.rb and app/controllers/api/v1/users_controller.rb files that are defined like the following:
class Api::V1::ApplicationController < ApplicationController
before_filter :verify_access
def verify_access
# some code here
end
end
class Api::V1::UsersController < Api::V1::ApplicationController
skip_before_filter, :except => [:show, :update, :delete]
end
And before everything seemed to be working right until I overrode a method that is shared by both UsersController and Api::V1::UsersController -- and now it seems like everything is pointing to UsersController even though I'm accessing through the api/v1/users route.
I'm at my wit's end trying to figure it out. Any suggestions? Thanks. PS - feel free to comment with whatever conventions I'm ignoring that I shouldn't be or other things I might have messed up :)
skip_before_filter also general takes a symbol parameter for the before filter than you wish to skip. Controller names should not have to be unique as long as the proper scoping/namespacing is applied.
example
api/users_controller
admin/users_controller
users_controller
then the code per controller
class Api::V1::UsersController < Api::V1::BaseController
end
class Admin:UsersController < Admin::BaseController
end
class UsersController < ApplicationController
end
Then the routes
MyApp::Application.routes.draw do
scope :module => "api" do
namespace :v1 do
resources :users
end
end
namespace :admin do
resources :users
end
resources :users
end
Rails is a bit confusing, but I had a similar problem. Here's some steps you can take to make sure you're not missing any small code issues. (this eventually led me to discover a syntax bug in the namespaced controller).
run bundle exec rake routes to generate a list of what route links to what controller and action. If this is good, then move to step 2. If not, fix your routes file and try again. (many good tutorials on this, so I won't go into detail)
Go into the rails console, and just load the controller class. If it doesn't work, you may have discovered a bug in syntax. Here's what happened on console when I tried to load the Api::V2::CampaignsController.
irb> Api::V2::CampaignsController
=> CampaignsController
Note: Rails is directing all requests to the wrong controller (based on Rails' fancy logic to load controller classes). It should goto Api::V2::CampaignsController, but instead it is loading CampaignsController.
You can also verify it in the console with:
> app.get '/api/v2/campaigns.json'
> app.controller.class
=> CampaignsController
# This is not the expected controller.
This ended up being a syntax problem in a class I was extending from the Api::V2::CampaignsController.
It was a bit mind-boggling, but hope this helps someone else.

Rails Routing: POST action to destroy content

I want to have an endpoint to destroy an instance of a model through a POST action like the Twitter API does:
statuses/destroy/:id
How would I define this route in the route file? I'm at a loss.
it may look better if you have other actions in the same resource in rails 3
resources :statuses, :only => [:index, :create]
collection do
post 'destroy/:id', :action => :destroy
end
end
I'm not sure if this is what you're trying to do but add this to your config/routes.rb should do the trick:
match 'statuses/destroy/:id' => 'statuses#destroy', :via => :post
(I've found it in the Rails Routing Guide)

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