I'm working on a group project building a chess application & trying to understand why I am getting a routing error when running this test.
I am trying to test the update action in my pieces controller, to make sure that it redirects once a piece is moved. Bare in mind I am new to testing and am guessing that there is a problem with how I've written this one. Really stuck and could use an explanation of what I am missing.
controller:
class PiecesController < ApplicationController
before_action :authenticate_user!, only: [:show, :update]
def show
#show the board again
#game = Game.find(params[:id])
#pieces = #game.pieces
end
def update
#piece = Piece.find(params[:id])
#game = #piece.game
if #piece.update_attributes(piece_params)
redirect_to game_path(#game)
end
end
private
def piece_params
params.require(:piece).permit(:x_position, :y_position)
end
end
spec:
require 'rails_helper'
RSpec.describe PiecesController, type: :controller do
describe 'update action' do
it 'should redirect to game path when piece is moved' do
user1 = FactoryGirl.create(:user)
game = FactoryGirl.create(:game, white_user_id: user1.id )
piece = FactoryGirl.create(:piece, game: game, user_id: user1.id, x_position: 0, y_position: 0)
put :update, params: {x_position: 1, y_position: 1}
expect(response).to redirect_to game_path(game)
end
end
end
routes:
Rails.application.routes.draw do
devise_for :users
root 'static_pages#index'
resources :games do
member do
patch :join
end
resources :pieces, only: [:show, :update]
end
end
You're missing id on put :update, params: {x_position: 1, y_position: 1}?
You probably want something like:
put :update, id: piece.id, game_id: game.id, piece: {x_position: 1, y_position: 1}
Related
I am getting a routing error when I attempt to create a new db entry or update a current one.
ERROR: No route matches [POST] "/pubs"
Routes.rb:
resources :people, except: [:show] do
resources :pubs, except: [:create, :new, :edit, :destroy]
end
resources :articles
resources :pubs, except: [:create, :new, :edit, :destroy]
namespace :sekret do
resources :people do
resources :pubs
end
end
sekret/pubs_controller
class Sekret::PubsController < SekretController
def index
#pubs = Pub.all
end
def show
#pub = Pub.find(params[:id])
end
def new
#person = Person.find(params[:person_id])
#pub = #person.pubs.new
end
def create
#pub = Pub.new(pub_params)
if #pub.save
flash[:notice] = "Article created successfully!"
redirect_to sekret_person_pub_path(#pub)
else
render :new, status: :unprocessable_entity
end
end
def edit
#pub = Pub.find(params[:id])
end
def update
#pub = Pub.find(params[:id])
if #pub.update(pub_params)
redirect_to sekret_person_pub_path(#pub)
else
render :edit, status: :unprocessable_entity
end
end
def destroy
pub = Pub.find(params[:id])
pub.destroy
redirect_to sekret_people_path
end
private
def pub_params
params.require(:pub).permit(
:pubmed_id, :journal, :pages, :date, :type, :link, :authors,
:title, :notes, :auth_id, :person_id)
end
end
After going through all of this setup, when I allow the non-namespace pubs to resolve edit, update, etc, the update process goes through without a hitch. Once I limit these functions to within the password protected namespace I get the routing error. After parsing through the routes I can see that sekret_person_pub_path is listed there. I think I am missing something somewhere.
Rake Routes:
pubs#index
pub GET /pubs/:id(.:format) pubs#show
PATCH /pubs/:id(.:format) pubs#update
PUT /pubs/:id(.:format) pubs#update
sekret_person_pubs GET /sekret/people/:person_id/pubs(.:format) sekret/pubs#index
POST /sekret/people/:person_id/pubs(.:format) sekret/pubs#create
new_sekret_person_pub GET /sekret/people/:person_id/pubs/new(.:format) sekret/pubs#new
edit_sekret_person_pub GET /sekret/people/:person_id/pubs/:id/edit(.:format) sekret/pubs#edit
sekret_person_pub GET /sekret/people/:person_id/pubs/:id(.:format) sekret/pubs#show
PATCH /sekret/people/:person_id/pubs/:id(.:format) sekret/pubs#update
PUT /sekret/people/:person_id/pubs/:id(.:format) sekret/pubs#update
DELETE /sekret/people/:person_id/pubs/:id(.:format) sekret/pubs#destroy
sekret_people GET /sekret/people(.:format)
By using resources :pubs, except: [:create, :new, :edit, :destroy], you are preventing the route generation from providing POST /pubs.
The namespace and nested resources will generate a URL POST sekret/people/:person_id/pubs.
In your controller, you should create the Pub as an associated object.
def create
person = Person.find(params[:person_id])
#pub = person.pubs.new(pub_params)
if #pub.save
flash[:notice] = "Article created successfully!"
redirect_to sekret_person_pub_path(#pub)
else
render :new, status: :unprocessable_entity
end
end
If you want to restrict access the create method, you could use an authorization library such as Pundit in which case you would setup a policy to restrict who can do what.
https://github.com/elabs/pundit
You are missing out on the routes because rails form don't use the correct routes when namespacing so you'll have to specify them manually
<%= form for #pub, url: sekret_person_pubs_path do |f| %>
to let the form knows which route to post, if you do not specify the url, rails will use url: person_pubs_path behind the scenes
Edit: forgot to add _path
error
uninitialized constant YourSpace::UsersController::User
controller
class YourSpace::UsersController < ApplicationController
def new
#title = YourSpace
end
def edit
#title = YourSpace
#user = User.find(params[:id])
end
def update
name = params[:user][:name]
if name.blank?
flash[:notice] = "Name can not be blank dawg!"
redirect_to :back
else
User.find(params[:id]).update_attributes(params[:user])
# redirect_to :action 'show'
redirect_to :action => :show
# render :action => 'show'
end
end
def index
#title = YourSpace
#users = User.limit(100).order('created_at DESC')
end
def show
#title = YourSpace
#user = User.find(params[:id])
end
end
routes
Rails.application.routes.draw do
root 'site#home'
get '/about', to: 'site#about', as: :about
namespace :your_space do
resources :users
end
namespace :word_cloud do
resources :words, :only => [:index, :create]
end
namespace :word_clock do
resources :page, :only => [:index]
end
namespace :wish do
resources :page, :only => [:index]
end
end
when firing up the rails server, I get error in the YourSpace UsersController where this line of code #users = User.limit(100).order('created_at DESC') is apparently all jacked up. Please know I'm trying to duplicate 180 websites in 365 days which is mostly built using ruby and rails. I'm following closely along the repo to build muscle memory. very much new to learning how to think, and programming.
First thought was wrong.. to solve the problem change User to ::User
My first thought is that you need to put this YourSpace::UsersController in app/controllers/your_space/users_controller.rb. Rails forces directory structure.
I am writing a rails application with devise and testing in rspec. I have an issue where my rspec fails the user_authenticate when the user is not logged in. All of my specs pass except for the last one- the error it gives is
"Failure/Error: get :show, id: course NoMethodError:undefined method `authenticate' for nil:NilClass"
I suspect I am having this issue because I have a before_action :authenticate_user! call and for someone not logged in, it tries to authenticate nil. Is there a way to make it fail gracefully and redirect to user_session? I tried to create an inherited version of authenticate_user to do the redirect, but it does not appear to work. I know this is probably a noob question but I have extensively searched around without any solution. thanks!
This is my controller:
class CoursesController < ApplicationController
before_action :authenticate_user!, except: [:index]
before_action :set_course, only: [:show]
def index
#course = Course.order('name')
end
def show
end
private
def set_course
#course = Course.find(params[:id])
end
def course_params
params.require(:course).permit(:name,:description,:department,:hidden,
:lecture_attributes => [:name,:description,:level])
end
def authenticate_user!
if user_signed_in?
super
else
redirect_to user_session
end
end
end
This is my spec:
require 'rails_helper'
RSpec.describe CoursesController, :type => :controller do
describe "user access " do
before(:each) do
#user = create(:user)
#request.env['devise.mapping'] = Devise.mappings[:user]
sign_in :user, #user
end
describe 'GET #index' do
it 'renders the :index view' do
get :index
expect(response).to render_template :index
end
end
describe 'GET #show' do
it 'assigns the requested course to #course' do
course = create(:course)
get :show, id: course
expect(assigns(:course)).to eq course
end
it 'renders the :show template' do
course = create(:course)
get :show, id: course
expect(response).to render_template :show
end
end
end
describe "guest access " do
describe 'GET #index' do
it 'renders the :index view' do
get :index
expect(response).to render_template :index
end
end
describe 'GET #show' do
it 'redirects to the login url' do
course = create(:course)
get :show, id: course
expect(response).to redirect_to 'user_session'
end
end
end
end
It seems that devise does the redirect to "users#sessions" itself when you add :authenticate_user! to the show action for a guest or a user that is not signed in.
Try removing your custom :authenticate_user! method and add "only: [:show]" to your before_action
class CoursesController < ApplicationController
before_action :authenticate_user!, only: [:show], except: [:index]
before_action :set_course, only: [:show]
def index
#course = Course.order('name')
end
def show
end
private
def set_course
#course = Course.find(params[:id])
end
def course_params
params.require(:course).permit(:name,:description,:department,:hidden,
:lecture_attributes => [:name,:description,:level])
end
end
Update
class CoursesController < ApplicationController
before_action :authenticate_user!, except: [:index]
before_action :set_course, only: [:show]
def index
#course = Course.order('name')
end
def show
if user_signed_in?
render :show
else
redirect_to user_session
end
end
private
def set_course
#course = Course.find(params[:id])
end
def course_params
params.require(:course).permit(:name,:description,:department,:hidden,
:lecture_attributes => [:name,:description,:level])
end
end
This isn't a super satisfying result but it appears as if authenticate_user! does not properly work with rspec. When I load the page directly, it correctly redirects to the login page, I am still interested to know what the proper work around is.
Can I have some sort of OR statement where I can first check if a user exists? There must be a standard way to deal with this problem so I can ensure my app is properly redirecting.
My routes file looks like this:
resources :people do
resources :text_messages, only: [:index, :new, :create]
end
And my /app/controllers/text_messages_controllers.rb looks like:
class TextMessagesController < ApplicationController
def index
puts 'You have reached the index controller'
#messages = Message.all
end
end
Here's the /spec/controllers/text_messages_controller_spec.rb
describe TextMessagesController do
describe 'GET #index' do
it 'shows all the messages to that user' do
person = create(:person)
message = create(:message, person: person)
get :index, person_id: person.id
expect(assigns(:messages)).to include(message)
end
end
end
When I run the test however, it fails saying #messages is nil and does not print out the text. Why is this test not reaching the controller action? It works great in development when I visit the url.
On RSpec, the destroy action fails in ActionController::InvalidAuthenticityToken.
I don't think this is a problem as skip_before_filter :verify_authenticity_token, :only => [:index, :show] in my Controller is enough to fix the problem, but it still bothers me that this has to happen and it seems like it could be a bad security practice.
-
Here's the would-be-failing RSpec test:
require 'spec_helper'
describe "Products" do
subject { page }
...
describe "Destroy" do
before {
FactoryGirl.create(:product)
visit products_path
}
it "should have a destroy link" do
expect { should have_link('Destroy') }
end
it "link should destroy" do
expect do
click_link('Destroy', match: :first)
end.to change(Product, :count).by(-1)
end
end
end
-
And here's the controller:
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
skip_before_filter :verify_authenticity_token, :only => [:destroy] #destroy wasn't working without this
def index
#products = Product.all
end
def show
end
def new
#product = Product.new
end
def edit
end
def create
...
end
def update
...
end
def destroy
#product.destroy
flash[:notice] = "Product was successfully destroyed."
redirect_to products_path
end
private
# Use callbacks to share common setup or constraints between actions.
def set_product
#product = Product.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def product_params
params.require(:product).permit(:title, :description, :image_url, :price)
end
end
-
Ideas?
You are correct, skipping your authenticity token check is a bad idea. At the very least, ensure you are only doing that for your test environment:
skip_before_filter :verify_authenticity_token, :only => [:destroy] if Rails.env.test?
The issue with invalid tokens when you run tests may be the result of running your tests in your development environment.
Ensure your test_helper.rb file is correctly setting the environment:
Rails.env = 'test'
This works better than the ENV['RAILS_ENV'] = 'test' style approach.
Best of luck! Did you happen to find any other resolution?