Testing a general controller action using rspec - ruby-on-rails

Here's how my routes look like:
/article/:id/:action {:root=>"article", :controller=>"article/article", :title=>"Article"}
Here's how my controller looks like:
# app/controllers/article/article_controller.rb
class ArticleController < ApplicationController
def save_tags
# code here
end
end
I want to test the save_tags action so I write my spec like this:
describe ArticleController do
context 'saving tags' do
post :save_tags, tag_id => 123, article_id => 1234
# tests here
end
end
But when I run this spec, I get the error
ActionController::RoutingError ...
No route matches {:controller=>"article/article", :action=>"save_tags"}
I think the issue is the save_tags action is a general controller action, ie. there's no /article/:id/save_tags in routes. What's the best way to test this controller action?

You're spot on. The issue is that you're looking for a route which doesn't have :id in it, but you don't have one. You'll need to pass a parameter to the post :save_tags of :id, and given the above question, I believe it is what you are calling article_id.
Therefore, try changing your test to:
describe ArticleController do
context 'saving tags' do
post :save_tags, tag_id => 123, id => 1234
# tests here
end
end
Update
Rails might be getting confused because you're using :action in your route and I believe action is either a reserved word or a word that Rails treats as special. Maybe try changing your routes to:
/article/:id/:method_name {:root=>"article", :controller=>"article/article", :title=>"Article"}
And your test to:
describe ArticleController do
context 'saving tags' do
post :save_tags, { :tag_id => 123, :article_id => 1234, :method_name => "save_tags" }
# tests here
end
end

You need a route to map to your controller actions
post '/article/:id/save_tags'
should work, or consider using resources helper to build your routes
# creates the routes new, create, edit, update, show, destroy, index
resources :articles
# you can exclude any you do not want
resources :articles, except: [:destroy]
# add additional routes that require an article in the member block
resources :articles do
member do
post 'save_tags'
end
end
# add additional routes that do NOT require an article in the collection block
resources :articles do
collection do
post 'publish_all'
end
end

Related

post to an update action with out an id in rails 4.1.x

I have a controller:
module Xaaron
class PermissionsManagementController < ApplicationController
end
end
chick has create, edit, update, new and destroy.
edit and destroy I was able to do something like:
get 'edit_group_membership' => 'permissions_management#edit', :as => 'edit_group_membership'
get 'remove_group_membership' => 'permissions_management#destroy', :as => 'remove_group_membership'
which allows me to by pass passing in a id. There is no model object for this controller so there is no need for an id. but my question is, how do i post to the update action in rspec and in the form_tag with out passing in an id? and with out it exploding stating that I am missing an id.
in the spec can I do: post :update, id: '', {}? What would I do for form_tag?
You could start with something like this:
# routes.rb
post 'permissions_management' => 'permissions_management#update', as: 'change_permissions_management'
# view
form_tag(change_permissions_management_path)
If it were my app, I would treat this as a singular resource, for which Rails provides out-of-the-box support. As you can see in the rake routes output, the :id param is not required for the show, update and destroy routes.
# routes.rb
resource :permissions_management # note the singular "resource"
# view
form_tag(permissions_management_path)
# $ rake routes | grep permissions_management
permissions_management POST /permissions_management(.:format) permissions_managements#create
new_permissions_management GET /permissions_management/new(.:format) permissions_managements#new
edit_permissions_management GET /permissions_management/edit(.:format) permissions_managements#edit
GET /permissions_management(.:format) permissions_managements#show
PATCH /permissions_management(.:format) permissions_managements#update
PUT /permissions_management(.:format) permissions_managements#update
DELETE /permissions_management(.:format) permissions_managements#destroy

Rspec fails with nested singular route

I have the following routes.rb file (part):
resource :user do
resources :orders do
post :verify, :on => :collection
end
end
My controller spec looks like follows:
require 'spec_helper'
describe OrdersController do
describe "#verify" do
it "verifies a recipe" do
post :verify
end
end
end
The spec fails with an ActionController::RoutingError No route matches {:controller=>"orders", :action=>"verify"} even though the route exists and correctly responds in a browser.
Based on your routes, the describe should look like:
describe Users::OrdersController do
...
Ah, my bad! I've completely forgot that I have the rails-translate-routes gem in place and I don't set a default locale!

rails custom rest route with parameter

I have a questions controller and an associated model and a number of rest routes. Here is how it's set up in routes.rb:
resources :questions
I want to add a custom route that has the format /questions/widget/ID (where ID is the id of the question for which I want to generate a widget). I want this to be processed by the "widget" action in my questions controller. I've tried a number of things such as:
resources :questions do
member do
get 'widget/:id'
end
end
But nothing is working. I'm sure I'm missing something simple. Any ideas? Thanks in advance.
You do not have to specify the id since you are inside resources. It should look like:
resources :questions do
member do
get 'widget'
end
end
You can get more information from the Rails Guide. Look at section 2.9.1.
Edit: I just noticed that you are trying to match get /questions/widget/:id. This will set up a route for get /questions/:id/widget. This is more in line with Rails convention. If you really want it the other way, you need to set up a custom match statement:
match "/questions/widget/:id" => "questions#widget"
However, I would stick with convention.
I know it is old, but looking to fix another routing problem I ended here, it is possible, to do what you are asking for, here is an example
resources :articles do
get 'by_tag/:tag' => :by_tag, on: :collection
get 'by_author/:author' => :by_author, on: :collection
resources :comments, except: :show
end
now you have /artices/by_tag/:tag . The trick was to use on:collection.
Obviously don't forget to add the by_tag action and by_author.
class ArticlesController < ApplicationController
.....
def by_tag
...
end
end
Check this route works with
melardev#local~$ rails routes
Why don't you use this routes:
resources :questions do
resources :widgets
end
it will create path like questions/:question_id/widgets/new for you to create new widget for question with specific id of question.
This is what ended up working for me:
resources :post do
get "author/:author", to: "posts#author", on: :collection, as: "author"
end
Which outputs the following route:
author_posts GET /posts/author/:author(.:format) posts#author
Then in your controller, you need to create the author action:
class PostsController < ApplicationController
def author
#roles = Post.where(author: params[:author])
render :index # to reuse the index view
end
end
Then in your view:
<%= link_to post.author, author_posts_path(post.author), data: { turbo_frame: "_top" } %>

Rails: Point several nested routes to one customer controller action

How do you point different nested routes to one controller action?
A user can be a member of several groups like company, project, group ect. for which It can request to join, leave or be removed by an admin.
I want to access the remove action for several models and destroy the belongs_to record in the profile model
I already have a polymorphic model that takes requests from a profile to a model( e.g. company) and upon acceptance of the request the profile will belong to the model. once the request is accepted the request recored is destroyed. I feel that the remove action that will destroy the relationship between the profile and the model should be part of the requests_controller, but I guess could be part of the profile_controller.
What I'm thinking I need to end up with is either
/_model_/:id/profile/:id/remove
/company/:id/profile/:id/remove
but how do I get this to point the remove action in my requests controller
or
/_model_/:id/requests/remove
/company/:id/request/remove
I am using the following code in my routes
resources :companies do
resource :requests do
put 'remove', :on => :member
end
resources :requests do
put 'accept', :on => :member
end
end
This is producing a double up of the routes
remove_company_requests PUT /companies/:company_id/requests/remove(.:format)
company_requests POST /companies/:company_id/requests(.:format)
new_company_requests GET /companies/:company_id/requests/new(.:format)
edit_company_requests GET /companies/:company_id/requests/edit(.:format)
GET /companies/:company_id/requests(.:format)
PUT /companies/:company_id/requests(.:format)
DELETE /companies/:company_id/requests(.:format)
accept_company_request PUT /companies/:company_id/requests/:id/accept(.:format)
GET /companies/:company_id/requests(.:format)
POST /companies/:company_id/requests(.:format)
new_company_request GET /companies/:company_id/requests/new(.:format)
edit_company_request GET /companies/:company_id/requests/:id/edit(.:format)
company_request GET /companies/:company_id/requests/:id(.:format)
PUT /companies/:company_id/requests/:id(.:format)
DELETE /companies/:company_id/requests/:id(.:format)
As
My I suggest that you create a new controller to handle this? The advantage is that you can map the route to this controller on any models you want the "remove association" on.
For example:
# RemoveController.rb
class RemoveController < ApplicationController
def destroy
# inplement the logic for deletion. You can use refection to implement
# this function only once for all the applied associations.
end
end
# routes.rb
resources :companies do
resource :requests do
resource :remove, :controller => :remove, :only => [:destroy]
end
end
The above routes would generate:
company_requests_remove DELETE /companies/:company_id/requests/remove(.:format) remove#destroy
You can nest the above line for the remove controller on any nested routes you want and they will all point back to the RemoteController's destroy object, only with different parameters to help you implement the destroy action.
Edit: to add create for specific relationship that you don't want to duplicate you can do this:
# routes.rb
resources :companies do
resource :requests do
resource :remove, :controller => :relationship, :only => [:destroy]
resource :create, :controller => :relationship, :only => [:create]
end
end
company_requests_remove DELETE /companies/:company_id/requests/remove(.:format) relationship#destroy
company_requests_create POST /companies/:company_id/requests/create(.:format) relationship#create
But I think you might need to be careful about breaking the convention of create in the respective controller. I'm not sure if there are any downside to this. The remove part since is only removing association and not the records itself, it doesn't seem to break the convention.
Try
puts 'remove', :on => :member, :controller => :requests, :action => :remove

Rails 3 add GET action to RESTful controller

I have a controller with the 7 RESTful actions plus an additional 'current' action, which returns the first active foo record:
class FooController < ApplicationController
def current
#user = User.find(params[:user_id])
#foo = #user.foos.where(:active => true).first
#use the Show View
respond_to do |format|
format.html { render :template => '/foos/show' }
end
end
#RESTful actions
...
end
The Foo Model :belongs_to the User Model and the User Model :has_many Foos.
If I structure the routes as such:
resources :users do
resources :foos do
member do
get :current
end
end
end
The resulting route is '/users/:user_id/foos/:id'. I don't want to specify the foo :id, obviously.
I've also tried:
map.current_user_foo '/users/:user_id/current_foo', :controller => 'foos', :action => 'current'
resources :users do
resources :foos
end
The resulting route is more like I would expect: '/users/:user_id/current_foo'.
When I try to use this route, I get an error that reads:
ActiveRecord::RecordNotFound in FoosController#current
Couldn't find Foo without an ID
edit
When I move the current action to the application controller, everything works as expected. The named route must be conflicting with the resource routing.
/edit
What am I missing? Is there a better approach for the routing?
I think you want to define current on the collection, not the member (the member is what is adding the :id).
try this.
resources :users do
resources :foos do
collection do
get :current
end
end
end
Which should give you a route like this:
current_user_foos GET /users/:user_id/foos/current(.:format) {:controller=>"foos", :action=>"current"}
Also map isn't used anymore in the RC, it will give you a deprecation warning.

Resources