load_resource get a nil resource - ruby-on-rails

In my controller I have:
class UsersController < ApplicationController
load_and_authorize_resource
def create
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render json: #user, status: :created, location: #user }
else
format.html { render action: "new" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
end
But I get undefined method 'save' for nil:NilClass
Failures:
1) UsersController if the user passes all the authorizations POST #create should create a new User with some parameters
Failure/Error: post :create, :user => { :email => 'puppa#puppa.pup' }
NoMethodError:
undefined method `save' for nil:NilClass
# ./app/controllers/users_controller.rb:47:in `block in create'
# ./app/controllers/users_controller.rb:46:in `create'
# ./spec/controllers/users_controller_spec.rb:66:in `block (4 levels) in <top (required)>'
Finished in 0.10714 seconds
1 example, 1 failure
I was expecting load_resources to populate #user = User.new(params[:user])
I was looking at CanCan internals following the entire flow and I discovered that in controller_resource.rb when we reach #build_resource we have:
def build_resource
resource = resource_base.new(resource_params || {})
assign_attributes(resource)
end
But resource here is nil... it's normal? what I'm missing? It's related with my problem with create action?
EDIT
here is my ability.rb
class Ability
include CanCan::Ability
def initialize(user)
# Define abilities for the passed in user here. For example:
# check if the user is registered or a guest user (not logged in)
if user.present?
if user.any_role? :super_admin
can :manage, :all
end
if user.any_role? :admin
can :manage, [User, Institution, Project, Order]
end
if user.any_role? :user
can :show, Project
can [:add, :change], :cart
can [:create, :show], Order, :user_id => user.id
can :download, UrlConnector
end
end
end
end
EDIT 2
While calling POST create I have:
in RSpec environment:
resource_base: User
resource_class: User
#params: {"user"=>{"email"=>"puppa#puppa.pup"}, "controller"=>"users", "action"=>"create"}
in browser as superadmin or admin, it's the same:
resource_base: User
resource_class: User
#params: {"utf8"=>"✓", "authenticity_token"=>"95qQ4H/+CLU96jCIO6U/YtgIQ5zWxE7pg0BedVMPSGk=", "user"=>{"email"=>"estanost#alumnes.ub.edu", "password"=>"264763", "password_confirmation"=>"264763", "ragionesociale"=>"fff", "partitaiva"=>"12345678901", "address"=>"via plutarco, 36", "city"=>"Manduria", "cap"=>"74024", "phone"=>"099979456", "role_ids"=>["3"]}, "commit"=>"Create User", "action"=>"create", "controller"=>"users"}

Try placing cancan as the last gem in the Gemfile

I think you should get rid of the if user.present? block, add user ||= User.new where that if block starts. This way, you will always have a user object. Of course, it won't pass any of your cancans, so the rest should fall through gracefully.

Related

How do I let users edit the account their logged into in ruby on different pages?

I get the following error, Couldn't find User with 'id'=
I have this in my Users_Controller,
def edit
#user = #signed_in_user
end
This is in my routes.rb,
root 'welcome#welcome'
get 'login' => 'sessions#login', :as => :login
get 'profile' => 'users#profile', :as => :profile
post 'logging/user' => 'sessions#create'
get 'logout' => 'sessions#destroy', :as => :logout
get 'about' => 'about'
resources :users
get 'register' => 'users#new', :as => :register
get 'edit' => 'users#edit', :as => :edit
This is in my application_controller.rb,
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :set_user
protected
def set_user
unless session[:user_id] == nil
#signed_in_user = User.find(session[:user_id])
end
end
end
This is in my Users_Controller
Here is my code from my User_Controller on creating the account
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
# GET /users
# GET /users.json
def index
#users = User.all
end
def profile
#user = User.find(session[:user_id]) unless session[:user_id] == ""
redirect_to login_path, notice: "You're not logged in" unless #user
end
# GET /users/1
# GET /users/1.json
def show
end
# GET /users/new
def new
#user = User.new
end
# GET /users/1/edit
def edit
#user = #signed_in_user
end
# POST /users
# POST /users.json
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /users/1
# PATCH/PUT /users/1.json
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# DELETE /users/1
# DELETE /users/1.json
def destroy
#user.destroy
respond_to do |format|
format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:name, :password, :password_confirmation, :email, :age)
end
end
And this is the link that I use for my HTML,
<li role="presentation"><%= link_to 'Edit', edit_path %></li>
So, to start, a good practice when you get an 'Couldn't find' message is to check what instance variables are in your view.
So in a view, just type: <%= #user_id %> and see if anything shows up on your page, thus indicating if any user is even present! The other problem is that your instance variable might be <%= user.id %> but I am not sure as I can't see your code and how the user is stored in the database.
Second if you run rake routes, you generally find that the edit path will have a URI pattern like: "/edit(.:format)", meaning the route need "edit_path(#user.id)" rather than just "edit_path".
Let me know if this leads you anywhere or you have further questions and I hope I can answer them!
===========
Additional info:
Well without more code to look at, I would provide a few more suggestions...The goal is to have the <%= #user.id %> (or user_id) show up on the page somehow, thus telling you it is available.
The set_user method is an instance method, not a class method. To make it a class method, try def self.set_user. This invokes the method of the instance on the controller, thus making it a class method.
Make sure you have a session object to use. In the routes, it looks like post logging/user might be creating the session, but I am not sure.
Keep the edit_path(#user.id) or however the id is stored for the user as the route rather than just edit_path. I am pretty sure if you run 'rake routes', it will tell you that an additional variable needs to be passed for the link to work
Use the gem byebug Here is the link: https://github.com/deivid-rodriguez/byebug. You get this error while the edit page or where ever you are getting the error write in the action byebug. As you have mentioned in the console it shows a arrow pointing at a specific line in the application, the last line should appear as this (byebug), here write the variable in which you are getting the user id. If we take an example of your application controller in the set_user method:
def set_user
byebug
unless session[:user_id] == nil
#signed_in_user = User.find(session[:user_id])
end
end
In the console after (byebug) write session[:user_id] so this will give you the value of the session[:user_id]. So if this is null then you have a problem here or just follow the same procedure to check anywhere else.
Also there is one more thing you can do to learn is just create a new project or use the existing one and generate a scaffold which will give you options of show, edit, index. It will generate all the views, controller code, migration and everything. You can do that like this:
rails generate scaffold User email:string password:string
You can add more fields if you want. And then in your application just visit http://localhost:[port_no]/users which will by default take you to index page where you can add new users, edit existing ones. This will teach you about everything. It would be like a reference code for you. Read more at: http://guides.rubyonrails.org/command_line.html#rails-generate
And being more specific to users only there is a gem named Devise which will give you all the required things like sign_in, sign_up, session_management for users. Hope these things help you with your issue.
Edit:
Here is a very good tutorial link which will help you: https://www.railstutorial.org/book/updating_and_deleting_users#sec-updating_users

Implementing scopes in Pundit

I am using the Pundit gem (with Devise and Rolify) to restrict access to information based on logged-in user roles.
At this time I have three roles for my User model defined: Admin, Client Admin, and Customer Admin.
A User belongs_to a Customer.
Customer has_many Users.
I have successfully implemented a Pundit policy when indexing the Customer model. Admins and Client Admins can see all Customers. Customer Admin can only see their OWN record.
The problem lies when I am trying to restrict the show method of the Customer controller. Admins and Client Admins can see all Customers. However, the Customer Admin should only be able to see his own record. But as it stands the Customer Admin can input any id in the URL and see any Customer record.
I'm fuzzy on the scoping. It's my understanding that the Policy methods (i.e. index? and show?) are to restrict WHO can perform these actions and the Scoping methods restrict WHICH RECORDS can be obtained. I'm having trouble composing the correct scope for the above scenario.
Here's the Customer controller:
class CustomersController < ApplicationController
before_action :set_customer, only: [:show, :edit, :update, :destroy]
after_action :verify_authorized
# GET /customers
# GET /customers.json
def index
#customers = policy_scope(Customer)
authorize Customer
end
# GET /customers/1
# GET /customers/1.json
def show
authorize #customer
end
# GET /customers/new
def new
#customer = Customer.new
authorize #customer
end
# GET /customers/1/edit
def edit
authorize #customer
end
# POST /customers
# POST /customers.json
def create
#customer = Customer.new(customer_params)
authorize #customer
respond_to do |format|
if #customer.save
format.html { redirect_to #customer, notice: 'Customer was successfully created.' }
format.json { render :show, status: :created, location: #customer }
else
format.html { render :new }
format.json { render json: #customer.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /customers/1
# PATCH/PUT /customers/1.json
def update
authorize #customer
respond_to do |format|
if #customer.update(customer_params)
format.html { redirect_to #customer, notice: 'Customer was successfully updated.' }
format.json { render :show, status: :ok, location: #customer }
else
format.html { render :edit }
format.json { render json: #customer.errors, status: :unprocessable_entity }
end
end
end
# DELETE /customers/1
# DELETE /customers/1.json
def destroy
authorize #customer
#customer.destroy
respond_to do |format|
format.html { redirect_to customers_url, notice: 'Customer was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_customer
#customer = Customer.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def customer_params
params.require(:customer).permit(:name, :parent_customer_id, :customer_type, :active, :currency)
end
end
And here is the Customer policy:
class CustomerPolicy < ApplicationPolicy
def index?
# Admins, ClientAdmins, and CustomerAdmins can index customers (see Scope class for filters)
#user.has_role? :admin or #user.has_role? :client_admin or #user.has_role? :customer_admin
end
def show?
# Admins, ClientAdmins, and CustomerAdmins can see any customer details
#user.has_role? :admin or #user.has_role? :client_admin or #user.has_role? :customer_admin
end
def update?
# Only Admins and ClientAdmins can update customer details
#user.has_role? :admin or #user.has_role? :client_admin
end
def destroy?
#user.has_role? :admin or #user.has_role? :client_admin
end
class Scope < Struct.new(:user, :scope)
def resolve
if (user.has_role? :admin or user.has_role? :client_admin)
# Admins and ClientAdmins can see all Customers
scope.where(:parent_id => nil)
elsif user.has_role? :customer_admin
# Customer Admins can only see their own Customer
scope.where(:id => user.customer) # THIS DOES NOT APPEAR TO GET INVOKED BY THE SHOW METHOD OF THE CONTROLLER
end
end
def show?
# NOT SURE WHAT TO PUT IN HERE
end
end
end
Success!! Thanks to the headstart given to me by railscard, the trick was to modify the show? method in the Customer policy file like the following:
def show?
# Admins, ClientAdmins, and CustomerAdmins can see any customer details
# Students cannot see customer details
return true if user.has_role?(:admin) || user.has_role?(:client_admin)
return true if user.customer_id == #record.id && user.has_role?(:customer_admin)
false
end
Note that I had to use the #record instance variable, as that's what the Application policy class uses to refer to the record being passed in by the authorize method.
Thanks!!
To get Pundit's scoping working for the show action, Pundit's policy_scope helper (or policy_scope!) could be used, or you could just inherit show? from the generated ApplicationPolicy.
The index action is already using policy_scope correctly, we just need to do something similar for the show action. Here are some options:
Option 1: Modify the show action to
def show
# Also remove :show from the :only option where
# before_action :set_customer, only: ... is called.
#customer = policy_scope(Customer).find(params[:id])
authorize #customer
end
OR
Option 2: Modify set_customer to
def set_customer
#customer = policy_scope(Customer).find(params[:id])
end
OR
Option 3: Modify CustomerPolicy#show? to
def show?
# scope call here will return the
# result of CustomerPolicy::Scope#resolve
# This is the same implementation generated
# in the default ApplicationPolicy so you could
# just delete this method here and inherit instead.
scope.where(:id => record.id).exists?
end
Here's the code that generates the default ApplicationPolicy#show? method.
See Pundit's README section on Scopes for additional details.
I think you can safely delete the empty show? method you have in CustomerPolicy::Scope, I don't believe it will be called.
I think you don't need scope to restrict access for show action.
def show?
return true if user.has_role? :admin || user.has_role? :client_admin
return true if user.customer_id == customer.id && user.has_role? :customer_admin
false
end
Pundit scopes usually used to fetch a list of records which user have access to. In case of show method (or any other method in controller, where you call authorize) Pundit instantiates policy class with current user and given customer and then simply calls show? method to check user permissions, i.e. CustomerPolicy.new(current_user, #customer).show?

Undefined method role for Cancan in rails 3

Currently I'm implementing a simple demo for a school management system. After I login I get the following exception:
undefined method `role' for nil:NilClass
app/models/ability.rb:5:in `initialize'
app/controllers/application_controller.rb:10:in `new'
app/controllers/application_controller.rb:10:in `current_ability'
Here's ability.rb:
class Ability
include CanCan::Ability
def initialize(user)
if user.role.name=='admin'
can :manage, :all
end
if user.role.name=='teacher'
can :read, Course
end
end
end
Here's application_controller.rb:
def set_current_user(user)
#current_user=user
end
def current_ability
#current_ability||= Ability.new(current_user)
end
def current_user
#current_user
end
I authenticate the user in the users_controller.rb as:
def authenticate
#user=User.find_by_name_and_password(params[:name],params[:password])
if #user
set_current_user(#user)
respond_to do |format|
format.html { redirect_to courses_path }
format.json { head :no_content }
end
end
end
finally here you are the method index in courses_controllers.rb, it's very simple as you can see
class CoursesController < ApplicationController
load_and_authorize_resource
def index
#courses = Course.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #courses }
end
end
end
Any help would be greatly appreciated!
The error tells you that "current_user" is nil, and this is the case.
I assume that you authenticate users and then redirect them to courses_path, and it's all fine because you actually assign an instance of User to current_user. However, after the redirect it doesn't know about user and seems like session is not created.
I'd recommend you to assign session[:user_id] in authenticate, therefore you will be able to allow current_user return an instance of user during the session.
Also, you might just use Devise to handle all this logic for you.

Rspec user controller - NoMethodError: undefined method 'user_url'

I'm now making Rspec test for users_controller.rb. However I'm in trouble the error NoMethodError: undefined method 'user_url' as follow.
FF
Failures:
1) UsersController PUT update user update does not succeed
Failure/Error: put :update, {:id => user.to_param}, valid_session, :user_route => user
NoMethodError:
undefined method `user_url' for #<UsersController:0x52e40e0>
# ./app/controllers/users_controller.rb:21:in `block (2 levels) in update'
# ./app/controllers/users_controller.rb:18:in `update'
# ./spec/controllers/users_controller_spec.rb:64:in `block (3 levels) in <top (required)>'
2) UsersController PUT update user update succeeds
Failure/Error: put :update, {:id => user.to_param}, valid_session, :user_route => user
NoMethodError:
undefined method `user_url' for #<UsersController:0x53bc560>
# ./app/controllers/users_controller.rb:21:in `block (2 levels) in update'
# ./app/controllers/users_controller.rb:18:in `update'
# ./spec/controllers/users_controller_spec.rb:58:in `block (3 levels) in <top (required)>'
Finished in 0.679 seconds
2 examples, 2 failures
Failed examples:
rspec ./spec/controllers/users_controller_spec.rb:61 # UsersController PUT update user update does not succeed
rspec ./spec/controllers/users_controller_spec.rb:56 # UsersController PUT update user update succeeds
Randomized with seed 33412
users_controller.rb
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #user }
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "user#edit" }
format.json { render json: #idea.errors, status: :unprocessable_entity }
end
end
end
end
Also here is my Rspec users_controller_spec.rb. I made two tests about "POST update". One is for being updated successfully. Another is for not being updated. (About the latter, I put the stub User.stub(:update_attribute).and_return(false) which I expect that "update_attribute" returns "false" so that process proceeds to "else".)
require 'spec_helper'
describe UsersController do
let(:valid_attributes) { {
"email" => "hoge#hogehoge.com",
"password" => "12345678"
} }
def valid_session
{}
end
describe "PUT update" do
it "user update succeeds" do
user = User.create! valid_attributes
put :update, {:id => user.to_param}, valid_session
assigns(:user).should eq(user)
end
it "user update does not succeed" do
user = User.create! valid_attributes
User.stub(:update_attribute).and_return(false)
put :update, {:id => user.to_param}, valid_session
assigns(:user).should eq(user)
response.should render_template("edit")
end
end
end
I have no idea to solve this, because I cannot understand where user_url did come. So I would like to have your help.
When you use redirect_to #user, rails sends that request to UsersController#show, but it does so by calling user_url(#user). If I had to guess, you probably don't have the line that defines user_url:
resources :users
in your routes.rb file. This would automatically create the named route user_url that your controller is referencing with redirect_to #user
Alternatively, you could define the route yourself in your routes.rb file like so:
get "/users/show" => "users#show", as: :user
But that's not really the 'Rails-y' way to do it. At any time, you can run the command rake routes in the terminal to see all the named routes you have defined in your routes.rb file. If user isn't there, then you need to define it like I mentioned above.
More info on named routes here: http://guides.rubyonrails.org/routing.html#singular-resources
If you are using devise then check if the following method returns anything.
def after_sign_in_path_for(resource)
in application_controller.rb
If the method returns nothing you will receive the error:
undefined method `user_url' for #
I also ended up removing
stored_location_for(resource)
in after_sign_in_path_for(resource) because it was causing an endless loop. Refer to this answer for details.
rails:3 Devise signup Filter chain halted as :require_no_authentication rendered or redirected

Cannot test with rspec controller POST create action( devise and cancan)

I am having difficulty getting a rspec test for a controller to pass. I would like to test that the POST create action works. I am using rails (3.0.3), cancan (1.4.1), devise (1.1.5), rspec (2.3.0)
The model is dead simple
class Account < ActiveRecord::Base
attr_accessible :name
end
The controller is standard as well (straight out of scaffolding)
class AccountsController < ApplicationController
before_filter :authenticate_user!, :except => [:show, :index]
load_and_authorize_resource
...
def create
#account = Account.new(params[:account])
respond_to do |format|
if #account.save
format.html { redirect_to(#account, :notice => 'Account was successfully created.') }
format.xml { render :xml => #account, :status => :created, :location => #account }
else
format.html { render :action => "new" }
format.xml { render :xml => #account.errors, :status => :unprocessable_entity }
end
end
end
and the rspec test I would like to pass is (excuse the title, perhaps not the most appropriate one)
it "should call create on account when POST create is called" do
#user = Factory.create(:user)
#user.admin = true
#user.save
sign_in #user #this is an admin
post :create, :account => {"name" => "Jimmy Johnes"}
response.should be_success
sign_out #user
end
Yet all I get is
AccountsController get index should call create on account when POST create is called
Failure/Error: response.should be_success
expected success? to return true, got false
# ./spec/controllers/accounts_controller_spec.rb:46
Other actions can be tested and do pass (i.e. GET new)
here is the test for GET new
it "should allow logged in admin to call new on account controller" do
#user = Factory.create(:user)
#user.admin=true
#user.save
sign_in #user #this is an admin
get :new
response.should be_success
sign_out #user
end
and for completion here is the ability file
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.admin?
can :manage, :all
else
can :read, :all
end
end
end
Any ideas? My guess is that I am using the wrong rspec expectation, since the code does work (it is just that the test does not perform as desired!)
response.should be_success returns true if the response code is in the range 200-299. But the create action redirects, so the response code gets set to 302, thus the failure.
You can test this by using response.should redirect_to. Check the output of the standard RSpec controller generator for an example, which might look like this:
it "redirects to the created account" do
Account.stub(:new) { mock_account(:save => true) }
post :create, :account => {}
response.should redirect_to(account_url(mock_account))
end
The rspec test that got the test to pass was (thanks to zetetic's advice):
it "should call create on account when POST create is called" do
#user = Factory.create(:user)
#user.admin = true
#user.save
sign_in #user #this is an admin
account = mock_model(Account, :attributes= => true, :save => true)
Account.stub(:new) { account }
post :create, :account => {}
response.should redirect_to(account_path(account))
sign_out #user
end

Resources