I am new to rails and working on a rails application.
I have installed the Devise gem and find it helpful in many areas, however I cannot create a new user unless I do it on the Rails Console. I find this especially frustrating. Does anyone have any suggestions or solutions?
When I sign up as an employee I get the following error "CanCan::AccessDenied in JobsController#index"
Also please note that when I verified my user model in devise and I have already included "include Devise::Models::DatabaseAuthenticatable"
My jobs_controller.rb file
def index
#users = User.all
#user = current_user
#jobs = Job.all
#jobs = Job.paginate(:page => params[:page], :per_page => 3)
end
# GET /jobs/1
# GET /jobs/1.json
def show
end
# GET /jobs/new
def new
#job = Job.new(:date => Time.now, :date_of_loss => Time.now, :sign_date => Time.now, :time_called_in => Time.now)
end
# GET /jobs/1/edit
def edit
end
# POST /jobs
# POST /jobs.json
def create
#job = Job.new(job_params)
respond_to do |format|
if #job.save
format.html { redirect_to #job, notice: 'Job was successfully created.' }
format.json { render action: 'show', status: :created, location: #job }
else
format.html { render action: 'new' }
format.json { render json: #job.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /jobs/1
# PATCH/PUT /jobs/1.json
def update
respond_to do |format|
if #job.update(job_params)
format.html { redirect_to #job, notice: 'Job was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #job.errors, status: :unprocessable_entity }
end
end
end
# DELETE /jobs/1
# DELETE /jobs/1.json
def destroy
#job.destroy
respond_to do |format|
format.html { redirect_to jobs_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_job
#job = Job.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def job_params
params.require(:job).permit(:date, :time_called_in, :date_of_loss, :contact, :job_address, :city, :postal_code, :site_phone, :work_phone, :cell_phone, :email, :referred_by, :insurance_company, :policy_number, :claim_number, :broker, :insurance_address, :insurance_city, :insurance_province, :insurance_postal_code, :insurance_phone, :insurance_contact, :insurance_email, :customer_name, :customer_address, :authorized, :signed_by_name, :sign_date, :signature, :contact_first_name, :contact_last_name)
end
end
My ability model looks like this
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.role == "admin"
can :manage, :all
elsif user.role == "manager"
can [:read, :update, :create], [Job, Equipment]
#can [:read, :update, :create], [Equipment]
elsif user.role == "employee"
can [:read, :update, :create], [Equipment, Job]
cannot [:create], [Job]
#can [:read, :update], [Equipment]
end
end
If you are getting this error: CanCan::AccessDenied in JobsController#index it's because your user is not authorized to access the JobsController#index method and has nothing to do with creating users.
Can you explain what you mean when you say you "Sign up as an employee"? Do you have a roles table or a boolean on the users table to indicate that a user is an employee?
Please post your Ability.rb class (possibly in app/models/ability.rb) as that's where the access controls for cancan are defined.
Try changing this:
user ||= User.new # guest user (not logged in)
to this:
user ||= User.new # guest user (not logged in)
puts "user.role = #{user.role}"
and then check your logs to see what the value of user.role is. I'm wondering if your user does not have the role that you think it does.
Related
How do I go about making this code block dry?
I understand that dry means dont repeat yourself but I don't see any obvious opportunity for refactoring.
Index, show, edit, and create seem like basic/necessary methods. They appear to be pretty dry to me.
The methods after I am not sure about.
I haven't tried anything other than googling so far.
class UsersController < ApplicationController
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def edit
#user = User.find(params[:id])
end
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
Slack.notify_channel
end
def update
#user = User.find(params[:id])
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
Slack.notify_channel
end
def destroy
#user = User.find(params[:id])
#user.destroy
respond_to do |format|
format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
Slack.notify_channel
end
private
def user_params
params.require(:user).permit(:username, :email)
end
end
There is no rails backend attached to this code snippet. I am assuming it is just theoretical - wanting us to refactor the code to make it shorter.
IMHO you can do something like that.
class UsersController < ApplicationController
include ExceptionHandling
before_action :load_user, only: [:show, :edit, :update, :destroy]
after_action :slack_notify_channel, only: [:create, :update, :destroy]
def index
#users = User.all
end
def new
#user = User.new
end
def create
#user = User.create!(user_params)
respond_to do |format|
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
end
end
def update
#user.update!(user_params)
respond_to do |format|
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
end
end
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
def load_user
#user = User.find(params[:id])
end
def slack_notify_channel
Slack.notify_channel
end
def user_params
params.require(:user).permit(:username, :email)
end
end
I would recommend you to create a concern to manage exceptions and render each specific error by exception. Then you can avoid having two ways in each action to render a good and bad case.
I have to guess a bit about what's in your ApplicationController and User classes. But the obviously repeated code is #user = User.find(params[:id]). Your show and edit methods both just run this line. So, they do exactly the same thing, which they oughtn't to do. One method for one thing.
Also, once you resolve show and edit into a single method, your create, update and destroy methods should call it instead of repeating the line.
Next, I wouldn't use new as a method name, since it is already used by BasicObject::new. I tested a few things and it gets pretty unclear:
class Test
attr_reader :test
def initialize
#test = 'test'
end
end
class Test2
attr_reader :test2
def new
p 'test2new'
#test2 = Test.new
end
end
testx = Test2.new
p testx.new.test
p testx.test2.test
=> "test2new"
=> "test"
=> "test"
It takes extra effort to see when you're calling your own new method and when you're calling BasicObject::new. So, I'd change that to new_user or something, if you even need it at all — I don't see why you need both it and your create method. (If you don't need it, get rid of it.)
Finally, #user = User.find(params[:id]) doesn't imply either showing or editing, so understanding what you're trying to do and coming up with a name that reflects it (e.g. set_user_id) is something that ought to be done as well.
I am following a tutorial on Udemy and adding an address to my Custom Devise Admin #Show Page.
after completing the nesting and controller modifications I am getting this error in the address controller create redirect.
No route matches {:action=>"show", :admin_id=>"B1CA", :controller=>"admin"} missing required keys: [:id]
My Admin Controller:
class AdminController < ApplicationController
before_action :authenticate_admin!
def show
#admin = Admin.find_by_admin_ident(params[:id])
#addresses = #admin.addresses
end
def index
#admins = Admin.all
end
end
My Address Controller: - Provided full controller incase I have erred somewhere else thats causing this.
class Admin::AddressesController < ApplicationController
before_action :set_address, only: [:show, :edit, :update, :destroy]
# GET /addresses
# GET /addresses.json
def index
#addresses = Address.all
end
# GET /addresses/1
# GET /addresses/1.json
def show
end
# GET /addresses/new
def new
#admin = Admin.find_by_id(params[:admin_ident])
#address = Address.new
end
# GET /addresses/1/edit
def edit
end
# POST /addresses
# POST /addresses.json
def create
#admin = Admin.find_by_id(params[:admin_ident])
#address = Address.new(address_params)
#address.admin = #admin
respond_to do |format|
if #address.save
format.html { redirect_to admin_path, notice: 'Address was successfully created.' }
format.json { render :show, status: :created, location: #admin }
else
format.html { render :new }
format.json { render json: #admin.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /addresses/1
# PATCH/PUT /addresses/1.json
def update
respond_to do |format|
if #address.update(address_params)
format.html { redirect_to #address, notice: 'Address was successfully updated.' }
format.json { render :show, status: :ok, location: #address }
else
format.html { render :edit }
format.json { render json: #address.errors, status: :unprocessable_entity }
end
end
end
# DELETE /addresses/1
# DELETE /addresses/1.json
def destroy
#address.destroy
respond_to do |format|
format.html { redirect_to addresses_url, notice: 'Address was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_address
#address = Address.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def address_params
params.require(:address).permit(:admin_id, :address_ident, :number, :name, :st_type, :unit_apt, :grid, :city, :province, :postal_code)
end
end
I am using custom identifiers as opposed to the standard rails /1 ect.. for id's this is how they are implemented.
Admin: - Exact same implementation in Addresses
class Admin < ActiveRecord::Base
before_create :generate_admin_ident
# Model Relations
has_many :addresses, dependent: :destroy
# Model Validations
validates_uniqueness_of :admin_ident
# Unique Ident Generator
def generate_admin_ident
begin
self.admin_ident = SecureRandom.hex(2).upcase
other_admin = Admin.find_by(admin_ident: self.admin_ident)
end while other_admin
end
# Vanity URL
def to_param
admin_ident
end
end
My form_for: Oddly enough this is not namespaced, but loads the form as follows -
<%= form_for [:admin, #admin, #address] do |f| %>
...
<% end %>
My routes File:
devise_for :admins, controllers: { sessions: 'admins/sessions', registrations: 'admins/registrations', passwords: 'admins/passwords', confirmations: 'admins/confirmations', unlocks: 'admins/unlocks'}
#Nests Addresses to Admin
resources :admins, only: [:index, :show], controller: 'admin' do
resources :addresses, except: [:index], controller: 'admin/addresses'
end
My Controller File Layout
Error Screen Better Errors
Please let me know if you want anymore information, I will do my best to accommodate!
EDIT # 1: - Rails Server Output
Started POST "/admins/B1CA/addresses" for ::1 at 2016-05-26 22:47:52 -0600
Processing by Admin::AddressesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"D2Sq2Pbq2bLxpOxBveSFf/3ivRJcm5jbhw39mnB/C1UnDll8z3fdEJMaf9315fcIaI6o7foTrvWrH7pT2y2BtA==", "address"=>{"admin_id"=>"", "address_ident"=>"", "number"=>"1234", "name"=>"Something", "st_type"=>"St", "unit_apt"=>"101", "grid"=>"SE", "city"=>"Calgary", "province"=>"AB", "postal_code"=>"T2B 5V5"}, "commit"=>"Create Address", "admin_id"=>"B1CA"}
Admin Load (0.3ms) SELECT "admins".* FROM "admins" WHERE "admins"."id" IS NULL LIMIT 1
(0.1ms) BEGIN
Address Exists (0.2ms) SELECT 1 AS one FROM "addresses" WHERE "addresses"."address_ident" = '' LIMIT 1
Address Load (0.1ms) SELECT "addresses".* FROM "addresses" WHERE "addresses"."address_ident" = $1 LIMIT 1 [["address_ident", "9F608A04"]]
SQL (0.1ms) INSERT INTO "addresses" ("address_ident", "number", "name", "st_type", "unit_apt", "grid", "city", "province", "postal_code", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING "id" [["address_ident", "9F608A04"], ["number", "1234"], ["name", "Something"], ["st_type", "St"], ["unit_apt", "101"], ["grid", "SE"], ["city", "Calgary"], ["province", "AB"], ["postal_code", "T2B 5V5"], ["created_at", "2016-05-27 04:47:52.551915"], ["updated_at", "2016-05-27 04:47:52.551915"]]
(0.8ms) COMMIT
Redirected to
Completed 500 Internal Server Error in 7ms (ActiveRecord: 1.7ms)
ActionController::ActionControllerError - Cannot redirect to nil!:
Rake Routes Output for Admin_Addresses:
admin_addresses POST /admins/:admin_id/addresses(.:format) admin/addresses#create
new_admin_address GET /admins/:admin_id/addresses/new(.:format) admin/addresses#new
edit_admin_address GET /admins/:admin_id/addresses/:id/edit(.:format) admin/addresses#edit
admin_address GET /admins/:admin_id/addresses/:id(.:format) admin/addresses#show
PATCH /admins/:admin_id/addresses/:id(.:format) admin/addresses#update
PUT /admins/:admin_id/addresses/:id(.:format) admin/addresses#update
DELETE /admins/:admin_id/addresses/:id(.:format) admin/addresses#destroy
Edit: #2: Error When redirect to path is:** admin_addresses_path(#admin)
ActionController::UrlGenerationError at /admins/B1CA/addresses
No route matches {:action=>"create", :admin_id=>nil, :controller=>"admin/addresses"} missing required keys: [:admin_id]
Edit: 3 - Error when form for is set to below
<%= form_for [#admin, #address] do |f| %>
...
<% end %>
NoMethodError at /admins/B1CA/addresses/new
undefined method `addresses_path' for #<#<Class:0x007fc532933768>:0x007fc534b799a8>
Did you mean? admin_addresses_path
I think you May get wrong params for finding your admin. Change
#admin = Admin.find_by_id(params[:admin_ident])
format.html { redirect_to admin_path, notice: 'Address was successfully created.' }
To:
# admin_id here, not admin_ident
#admin = Admin.find_by_id(params[:admin_id])
format.html { redirect_to #admin, notice: 'Address was successfully created.' }
Or if you want to redirect to admin addresses:
# need to remove " except: [:index]" in your address routes
# routes
resources :addresses, controller: 'admin/addresses'"
# controller
format.html { redirect_to admin_addresses_path(#admin), notice: 'Address was successfully created.' }
Before I begin, let me say that I have two different devise user models.
I began removing the unnecessary methods from the scaffold_controller, because devise handles certain actions.
I'm trying to route to the current_user profile page, but I get an error every time I try to add the link in my navbar <%= 'My Profile', current_user %> doesn't work in this case. I plan to setup the associations as soon as I finish hooking up these controllers.
class ProfilesController < ApplicationController
before_action :set_profile, only: [:show, :edit, :update, :destroy]
# GET /profiles
# GET /profiles.json
def index
#profiles = Profile.all
end
# GET /profiles/1
# GET /profiles/1.json
def show
#user = User.find(params[:id])
respond_to do |format|
format.html
format.json {render :json => #user }
end
end
# GET /profiles/1/edit
def edit
end
# PATCH/PUT /profiles/1
# PATCH/PUT /profiles/1.json
def update
respond_to do |format|
if #profile.update(profile_params)
format.html { redirect_to #profile, notice: 'Profile was successfully updated.' }
format.json { render :show, status: :ok, location: #profile }
else
format.html { render :edit }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
# DELETE /profiles/1
# DELETE /profiles/1.json
def destroy
#profile.destroy
respond_to do |format|
format.html { redirect_to profiles_url, notice: 'Profile was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_profile
#profile = Profile.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def profile_params
params[:profile]
end
end
profiles GET /profiles(.:format) profiles#index
POST /profiles(.:format) profiles#create
new_profile GET /profiles/new(.:format) profiles#new
edit_profile GET /profiles/:id/edit(.:format) profiles#edit
profile GET /profiles/:id(.:format) profiles#show
PATCH /profiles/:id(.:format) profiles#update
PUT /profiles/:id(.:format) profiles#update
DELETE /profiles/:id(.:format) profiles#destroy
Assuming (such as bad thing to do), that you have already created the profile while creating the user ( since there is no create action in the controller ) and that a profile belongs_to a user, then this should work:
<%= link_to "My Profile", current_user.profile %>
Generally, I would get my associations set up properly before I even look at the controllers.
I have some code for a Rails 4 project I'm working on. It uses active_record (mysql2), and there is a has_many :through relationship that works properly when I interact through rails c (in either production or development). When I try to submit the relationship in a form (I am using simple_form), I can't seem to get it to save.
Here is how my information is currently set up (just showing snippets, I can't really show the whole source):
Model:
has_many :categorizations
has_many :resource_categories, through: :categorizations
accepts_nested_attributes_for :resource_categories
accepts_nested_attributes_for :categorizations
Form:
= simple_form_for #resource do |f|
= f.association :resource_categories
Controller:
# POST /resources
# POST /resources.json
def create
#resource = Resource.new(resource_params)
set_categories(#resource, params[:resource][:resource_category_ids])
respond_to do |format|
if #resource.save
format.html {
redirect_to #resource, notice: 'Resource was successfully created.'
}
format.json {
render action: 'show', status: :created, location: #resource
}
else
format.html {
render action: 'new'
}
format.json {
render json: #resource.errors, status: :unprocessable_entity
}
end
end
end
# PATCH/PUT /resources/1
# PATCH/PUT /resources/1.json
def update
respond_to do |format|
if #resource.update(resource_params)
set_categories(#resource, params[:resource][:resource_category_ids])
format.html {
redirect_to #resource, notice: 'Resource was successfully updated.'
}
format.json {
head :no_content
}
else
format.html {
render action: 'edit'
}
format.json {
render json: #resource.errors, status: :unprocessable_entity
}
end
end
end
# Never trust parameters from the scary internet, only allow the white list
# through.
def resource_params
params.require(:resource).permit(
:title, :slug, :ancestry, :status, :author_id, :published, :parent_id,
:resource_category_ids, :preview, :body
)
end
def set_categories(resource, categories)
# Clean out the existing categories (if there are any)
unless resource.resource_categories.blank?
resource.resource_categories.each do |category|
resource.resource_categories.delete(category)
end
end
unless categories.blank?
categories.each do |category|
unless category.blank?
resource.resource_categories << ResourceCategory.find(category)
end
end
end
end
When I issue the following commands using rails c -e production (or just plain rails c) it works (In this example, I assign all categories to all resources):
Resource.all.each do |resource|
ResourceCategory.all.each do |category|
resource.resource_categories << category
end
end
It seems like my problem is that the controller is not calling the helper function
Use this instead:
def create
#resource = Resource.new(resource_params)
#resource.set_categories(params[:resource][:resource_category_ids])
..
end
Move the method in the Resource model:
def set_categories(categories)
# Clean out the existing categories (if there are any)
unless new_record?
unless resource_categories.blank?
resource_categories.each do |category|
resource_categories.delete(category)
end
end
end
unless categories.blank?
categories.each do |category|
unless category.blank?
resource_categories << ResourceCategory.find(category)
end
end
end
end
#resource is instance variable of your Controller, you don't need to pass it to a method. Perform all your operations directly on the instance variable.
OP still had problem while saving the record, changed :resource_category_ids to :resource_category_ids => [] in resource_params method:
def resource_params
params.require(:resource).permit(
:title, :slug, :ancestry, :status, :author_id, :published, :parent_id,
:preview, :body, :resource_category_ids => []
)
end
I've just generated my User scaffold and I'm now getting an error with 'Password can't be blank' even though I've filled out all the fields. I'm confused because my rake tests are passing, so not sure where the error is?
User Model:
class User < ActiveRecord::Base
validates :name, presence: true, uniqueness: true
has_secure_password
end
And my user controller:
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
# GET /users
# GET /users.json
def index
#users = User.order(:name)
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
end
# POST /users
# POST /users.json
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to users_url, notice: "User #{#user.name} was successfully created." }
format.json { render action: 'show', status: :created, location: #user }
else
format.html { render action: '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 users_url, notice: "User #{#user.name} was successfully updated." }
format.json { head :no_content }
else
format.html { render action: '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 }
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_digest)
end
end
If you need more code submitted, just let me know. I'm working with Rails 4.
Would be nice to see the server logs when you submit the form, something tells me you don't require the right parameters, from your form there should be: password and password_confirmation but in controller you require password_digest for some reason:
def user_params
params.require(:user).permit(:name, :password_digest)
end
it should be:
def user_params
params.require(:user).permit(:name, :password, :password_confirmation)
end
and password_digest should be updated not via params.