Assigns usage in Rspec - ruby-on-rails

I'd like to ask why i'm always getting nil value when running rspec controller test ?
I already read in this site and most of answers because plurals word using inside assigns
but in my case thats not working and i still got the same value
This is my Controller
class ContactsController < ApplicationController
load_and_authorize_resource
before_action :find_contact, only: [:show,:edit,:update,:destroy]
def index
authorize! :index, Contact
#contact = Contact.accessible_by(current_ability)
# #contact = Contact.all
end
def show
end
def new
#contact = Contact.new
end
def edit
end
def create
#contact = current_user.contact.new(contact_params)
if #contact.save
redirect_to #contact
else
render 'new'
end
end
def update
# #contact = Contact.find(params[:id])
if #contact.update(contact_params)
redirect_to #contact
else
render 'edit'
end
end
def destroy
#contact.destroy
redirect_to contacts_path
end
private
def contact_params
params.require(:contact).permit(:firstname,
:lastname,
:alamat,
details_attributes: [:id, :number, :_destroy])
end
def find_contact
#contact = Contact.find(params[:id])
end
end
And this is my simple controller test
require 'rails_helper'
RSpec.describe ContactsController do
describe "Contact" do
it "succesfully create the contact" do
contact = FactoryGirl.create(:contact)
get :index
# byebug
expect(assigns(:contacts)).to eq([contact])
end
end
end
Even i change assigns(:contacts) to assigns(:contact) i still got the same value. So where is that i am do wrong ?
Please kindly answer this, big thanks

Even i change assigns(:contacts) to assigns(:contact) i still got the
same value. So where is that i am do wrong ?
assigns and assert_template have been remove and extracted to a gem in Rails 5.
Source

You have an authorization check
authorize! :index, Contact
before the assignment to #contact.
But your test has no setup in order to grant permissions to the requesting user in any way.
It probably makes sense to have an additional test alongside the one you show in order to spot errors like this. E.g:
it "returns 200 (OK)" do
get :index
expect(response.response_code).to eq(200)
end

Related

the AbstractController::DoubleRenderError cannot be fixed with "redirect_to and return"

I got this error today when I tried to use some helper methods for the users controller:
AbstractController::DoubleRenderError (Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and
at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need
to do something like "redirect_to(...) and return".)
I put this following helpers in application_controller.rb :
class ApplicationController < ActionController::Base
def current_user
User.find_by :id=>session[:user_id]
end
def log_in?
!!session[:user_id]
end
def log_in_first
if !log_in?
session[:error]="You have to log in first to continue your operation"
redirect_to("/login") and return
end
end
def correct_user?
if !(current_user.id.to_s==params[:id])
session[:error]="You have no right to do this operation."
redirect_to "/"
return
end
end
end
and here is the user_controller.rb:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id]=#user.id
redirect_to user_path(#user)
else
render 'new'
end
end
def show
log_in_first
#user = User.find_by id: params[:id]
correct_user?
if #user
render 'show'
else
redirect_to '/login'
end
end
private
def user_params
params.require(:user).permit(:name,:password,:email,:email_confirmation)
end
end
As you can see I tried to use both return and and return in log_in_first and correct_user?to fix the problem but it still doesn't work. Does anyone have any ideas?
The problem is in the show action, log_in_first redirects then the show action does whatever it wants, which is redirect or render. This is causing the error.
A better solution is to use before_action for your authentication and authorization and just let the user controller actions do their thing. Something like the below.
class ApplicationController < ActionController::Base
def current_user
User.find_by :id=>session[:user_id]
end
def log_in?
!!session[:user_id]
end
def authenticate_user!
if !log_in?
session[:error]="You have to log in first to continue your operation"
redirect_to("/login")
end
end
def authorize_user!
unless current_user&.id.to_s==params[:id]
session[:error]="You have no right to do this operation."
redirect_to "/"
end
end
end
class UsersController < ApplicationController
before_action :authenticate_user!, only: [:show]
before_action :authorize_user!, only: [:show]
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id]=#user.id
redirect_to user_path(#user)
else
render 'new'
end
end
def show
#user = User.find_by id: params[:id]
render 'show'
end
private
def user_params
params.require(:user).permit(:name,:password,:email,:email_confirmation)
end
end

Rails beginner - How to make this API accept JSON?

I am new to rails and am trying to connect my rails api for an article (consists of two fields - title and description) to an iOS application. I am trying to send JSON data in a POST request and then save that data in a database. I am wondering how to make my articles_controller accept and save json data.
Here is my code so far:
class ArticlesController < ApplicationController
before_action :set_article, only: [:edit, :update, :show, :destroy]
def index
#all_articles = Article.all
end
def new
#article = Article.new
end
def edit
end
def create
#article = Article.new(article_params)
if #article.save
flash[:notice] = "Article was sucessfully created"
redirect_to article_path(#article)
else
render 'new'
end
end
def show
end
def update
if #article.update(article_params)
flash[:notice] = "Article was successfully updated."
redirect_to article_path(#article)
else
render 'edit'
end
end
def destroy
#article.destroy
flash[:notice] = "Article was successfully deleted."
redirect_to(articles_path)
end
private
def set_article
#article = Article.find(params[:id])
end
def article_params
params.require(:article).permit(:title, :description)
end
end
The above code is able to submit a form on the webpage and save it to my database. I would like to alter it to accept json data from an iOS application and save to the database.
Thank you very much for any help
Make a route for POST method:
In config/routes.rb
match 'accept_post' => 'articles#accept_post', :via => :post
In Articles Controller
def accept_post
// get parameters as usual (like params[:title] etc.)
// do the saving to db
end
Important Note: Make the url publicly accessible. But do your own authentication, so others cannot POST to it.
You can use your existing action in controller (like create) if you want, just wanna give you an idea about it.
You may need to parse the JSON in controller:
require 'json'
JSON.parse(<json object>)
JSON.parse(response.body)

Updating TopicsController to allow a moderator to update topics, but not create or delete

I'm in the process of creating a website similar to Reddit. I would like to allow a moderator to be able to update a topic, but not be able to create or delete topic. I'm aware that I need to update TopicsController but I'm not sure how. My main problem is that I'm not sure how to make the code specific enough to ensure that a moderator can only update; not delete or create a topic, as an admin can.
My current code looks like this:
class PostsController < ApplicationController
before_action :require_sign_in, except: :show
before_action :authorize_user, except: [:show, :new, :create]
def show
#post = Post.find(params[:id])
end
def new
#topic = Topic.find(params[:topic_id])
#post = Post.new
end
def create
#post.body = params[:post][:body]
#topic = Topic.find(params[:topic_id])
#post = #topic.posts.build(post_params)
#post.user= current_user
if #post.save
flash[:notice] = "Post was saved"
redirect_to [#topic, #post]
else
flash[:error] = "There was an error saving the post. Please try again."
render :new
end
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
#post.assign_attributes(post_params)
if #post.save
flash[:notice] = "Post was updated."
redirect_to [#post.topic, #post]
else
flash[:error] = "There was an error saving the post. Please try again."
render :edit
end
end
def destroy
#post = Post.find(params[:id])
if #post.destroy
flash[:notice] = "\"#{#post.title}\" was deleted successfully."
redirect_to #post.topic
else
flash[:error] = "There was an error deleting the post."
render :show
end
end
private
def post_params
params.require(:post).permit(:title, :body)
end
def authorize_user
post = Post.find(params[:id])
unless current_user == post.user || current_user.admin?
flash[:error] = "You must be an admin to do that."
redirect_to [post.topic, post]
end
end
end
I've already added a moderator role to the enum role.
I apologise if this seems really basic...but it has got me stumped!
Thanks in advance!
I could answer with some custom solution, but it's better to use a more structured and community-reviewed approach: authorization with cancan.
As tompave noticed you can use cancan gem for this.
Personally I prefer pundit.
In old days I used to define permissions directly in code everywhere: in controllers, in views and even models. But it's really bad practice. When your app grows, you are lost: you update a view, but you should make the same change in controller and sometimes in model too. It soon becomes absolutely unmanageable and you have no idea what your users can and cannot do.
Pundit, on the other hand, offers central place -- policy -- for defining what user can do. Views and controllers can then use those policies.
For example, if you need to define Post's policy you simply create app/policies/post_policy.rb file:
class PostPolicy
attr_reader :user
attr_reader :post
def initialize(user, post)
#user = user
#post = post
end
def author?
post.user == user
end
def update?
author? || user.admin? || user.moderator?
end
def create?
author? || user.admin?
end
def destroy?
author? || user.admin?
end
# etc.
end
Now whenever you need to check user's ability to perform action, you can simply invoke:
# in controller
def update
#post = Post.find(params[:id])
authorize #post
# do whatever required
end
# in view
<% if policy(post).update? %>
<%= link_to 'Edit Post', post_edit_path(post) %>
<% end %>
As you can see Pundit is very easy to comprehend and it uses the same "convention over configuration" approach as Rails. At the same time it's very flexible and allows you to test virtually anything.
You will definitely need Pundit or any similar gem to manage permission in your ambitious app.

Keep Receiving an "Unknown attribute=user_id" error

First time poster, long time lurker here. I have a Users model and controller for a little video game application for Rails that I'm currently making. So I've read a couple of answers on here regarding this issue, but none of the answers really seem to have helped me. People have suggested adding a "user_id" column to my Users table, but my point of contention is, I thought the "user_id" was automatically made in Rails? Even if I use a user.inspect, I still see a user_id=7show up on the page. However, I still get the unknown attribute error when attempting to create a game and assign to the current user. Any help would be most appreciated in pinpointing the cause and solution to this. Thanks!
app/controllers/users_controller.rb:
class UsersController < ApplicationController
skip_before_filter :require_authentication, only: [:new, :create]
def index
#users = User.all
end
def show
end
def new
#user = User.new
end
def edit
#user = current_user
end
def create
#user = User.create!(user_params)
session[:user_id] = #user.id
redirect_to users_path, notice: "Hi #{#user.username}! Welcome to DuckGoose!"
end
def update
current_user.update_attributes!(user_params)
redirect_to users_path, notice: "Successfully updated profile."
end
def destroy
#user = User.find(params[:id])
#user.destroy
redirect_to users_url, notice: 'User was successfully destroyed.'
end
private
def user_params
params.require(:user).permit(:username, :firstname, :lastname, :email, :password, :password_confirmation)
end
end
app/config/routes.rb:
NkuProject::Application.routes.draw do
resources :users do
resources :games
end
resources :sessions
resources :games
get "sign_out", to: "sessions#destroy"
get "profile", to: "users#edit"
root to: "sessions#new"
end
app/controllers/games_controller.rb
class GamesController < ApplicationController
def new
#game = Game.new
end
def index
#games = Game.all
end
def destroy
#game = Game.find(params[:id])
#game.destroy
redirect_to games_url, notice: 'Game was successfully deleted.'
end
def create
#game = current_user.games.build(game_params)
if #game.save
redirect_to #game, notice: "Game successfully added"
else
render :new
end
end
def show
#game = Game.find(params[:id])
end
private
def game_params
params.require(:game).permit!
end
end
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_filter :require_authentication
def current_user
#current_user ||= User.find_by(id: session[:user_id]) if session[:user_id].present?
end
helper_method :current_user
def require_authentication
if current_user
true
else
redirect_to new_session_path
end
end
end
I'm sure I'm missing some code to put in for reference, but if I need anything else please let me know.
Looking at the way your controller actions are defined, I can safely say that User and Game have a 1-M relationship, i.e.,
class User < ActiveRecord::Base
has_many :games
end
class Game < ActiveRecord::Base
belongs_to :user
end
Now, based on that games table must have a field named user_id. Rails is not going to create it for you unless you specify it. You need to add field user_id in games table by creating a migration for the same. Right now, it doesn't seem like you have user_id foreign_key field in games table. Hence, the error while saving games record.

Testing Rails controllers with RSpec–How to test: current_account.projects

I'm using Rspec and Rails 3 for testing. I've tested my models and helpers but I'm lost on how to begin testing controllers. Almost all of my data in my controller actions is pulled using something like these examples:
#services = current_account.services
#projects = current_person.projects
#projects = current_account.projects.active
# this is really #projects = current_person.account.projects.active)
I can't seem to find any examples of how to test data that's pulled this way. All of the examples I've found aren't scoped to an account or person. Can anyone point me to an article on how to mock or stub this type of arrangement? Is this a sign that this entire approach isn't correct? Below, I've included an entire sample controller.
Any help would be greatly appreciated.
Thanks,
David
class ServicesController < ApplicationController
# Run authorizations
filter_resource_access
# Respond to ...
respond_to :html, :xml, :json
respond_to :js, :only => [:new, :create, :edit, :update, :destroy]
# GET /services
# GET /services.xml
def index
#services = current_account.services.order("name").paginate(:page => params[:page])
respond_with(#services)
end
# GET /services/1
# GET /services/1.xml
def show
#service = current_account.services.find(params[:id])
respond_with(#service)
end
# GET /services/new
# GET /services/new.xml
def new
#service = current_account.services.new
respond_with(#service)
end
# GET /services/1/edit
def edit
#service = current_account.services.find(params[:id])
respond_with(#service)
end
# POST /services
# POST /services.xml
def create
#service = current_account.services.new(params[:service])
if #service.save
# flash[:notice] = 'A service was successfully created.'
end
respond_with(#service, :location => services_url)
end
# PUT /services/1
# PUT /services/1.xml
def update
#service = current_account.services.find(params[:id])
if #service.update_attributes(params[:service])
# flash[:notice] = 'The service was successfully updated.'
end
respond_with(#service, :location => services_url)
end
# DELETE /services/1
# DELETE /services/1.xml
def destroy
#service = current_account.services.find(params[:id])
if #service.destroy
flash[:notice] = "The service was successfully deleted."
else
flash[:warning] = #service.errors.full_messages.inject("") { |acc, message| acc += message }
end
respond_with(#service)
end
end
–––––– UPDATE
Thanks to Xaid's solution I was able to get a solution:
context "logged_in" do
before(:each) do
#current_account = Factory.create(:account)
controller.stub!(:current_account).and_return(#current_account)
#services = FactoryGirl.create_list(:service, 10, :account => #current_account)
#services << #current_account.services.first
#current_account.services.stub!(:all).and_return(#services)
end
# INDEX
describe "GET services" do
before(:each) do
get :index
end
it "should set #services when accessing GET /index" do
assigns[:services].should == #services
end
it "should respond with success" do
response.should be_success
end
end
end
Can't you use something like this to test your 'index' action
describe "GET 'index'" do
before(:each) do
#user = FactoryGirl.create(:user)
controller.stub!(:current_user).and_return(#user)
#services = FactoryGirl.create_list(:service, 10, :user => #user)
#user.services.stub!(:all).and_return(#services)
end
it "should return a list of services" do
get :index
assigns(:services).should == #services
end
end
If I understood your question correctly, you should be able to stub current_user.services(or projects) and make it return some known value (generated by FactoryGirl in my example) and check that against the value thats stored in your action (for example, #services in your 'index' action).

Resources