before_validation method not working - ruby-on-rails

I am a newB in rails and I am working on a project and wanted to remove trailing and leading white spaces from username and email. So I created a method in user model
class User < ActiveRecord::Base
include CarrierWave::MiniMagick
#removes the white spaces before validating the data
before_validation :strip_whitespace, :only => [:name,:email]
#data validations
validates :email, :presence =>true, :uniqueness => {case_sensitive: false}, :format => { :with=> /([a-zA-Z0-9_\-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)/, :message => "please enter a valid e-mail" }
validates :name, :presence=>true
validates :password ,:presence =>true, :confirmation=> true #, :length =>{ :minimum=>6, :maximum=>30}, :format=>{:with=>/(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,30}/}
#for the image
mount_uploader :image, ImageUploader
#for the password
has_secure_password
#associations
has_many :issues
has_many :comments
end
def strip_whitespace
self.email = self.email.squish
self.name = self.name.squish
end
when I enter user information in Users.create action, the leading and trailing spaces are removed, But when I login from my sessions controller, the leading and trailing spaces are not removed and hence it shows me an error.
Please help
Code for the users controller
class UsersController < ApplicationController
before_action :not_logged_in?, :only => [:new]
def new
#user=User.new
end
def create
#user = User.new(user_params)
if #user.save
sign_in #user
RegistrationsMailer.welcome_email(#user).deliver
flash[:success] = 'you are successfully registered'
redirect_to :controller => 'users' , :action => 'show', :id => #user.id
else
render 'new'
end
end
def show
#user=User.find(params[:id])
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
redirect_to #user
else
render 'edit'
end
end
protected
def user_params
params.require(:user).permit(:name,:email,:password,:image)
end
end
code for sessions controller
class SessionsController < ApplicationController
def new
end
def create
#user = User.find_by_email(params[:sessions][:email])
if #user && #user.authenticate(params[:sessions][:password])
sign_in #user
redirect_to #user
else
flash.now[:error] = 'Invalid email or password'
render 'new'
end
end
def destroy
end
end
Please help

According to When does Validation happen?
The following methods trigger validations, and will save the object to the database only if the object is valid:
create
create!
save
save!
update
update!
When you are creating or destroying a new session, you don't actually call any of those methods on the User model so validations won't be called.
But since your database has the squished values, you need to modify your create action in the SessionsController to squish the passed in params.
def create
#user = User.find_by_email(params[:sessions][:email].squish)
if #user && #user.authenticate(params[:sessions][:password].squish)
sign_in #user
redirect_to #user
else
flash.now[:error] = 'Invalid email or password'
render 'new'
end
end

Related

NoMethodError: undefined method `projects' for nil:NilClass

This is the error I'm getting:
Error:
ProjectsControllerTest#test_should_redirect_destroy_when_not_logged_in:
NoMethodError: undefined method `projects' for nil:NilClass
app/controllers/projects_controller.rb:39:in `project_owner'
test/controllers/projects_controller_test.rb:19:in `block (2 levels) in <class:ProjectsControllerTest>'
test/controllers/projects_controller_test.rb:18:in `block in <class:ProjectsControllerTest>'
What I think is happening and correct if I am wrong is that when the code goes and search for the projects it finds that the user is not logged in, therefore there is not such a project_owner. Even then the project_owner object should pick it and check that is nil, however that might not be happening.
Projects.Controller
class ProjectsController < ApplicationController
before_action :logged_in_user, only: [:index, :show, :create]
before_action :project_owner, only: :destroy
def index
end
def show
#project = Project.find(params[:id])
end
def new
#project = Project.new
end
def create
#project = current_user.projects.build(project_params)
if #project.save
flash[:success] = "Project Created"
redirect_to #project
else
render 'new'
end
end
def destroy
#project.destroy
flash[:success] = "Project Deleted"
redirect_to request.referrer || root_url
end
private
def project_params
params.require(:project).permit(:name,:category,:picture)
end
def project_owner
#project = current_user.projects.find_by(id: params[:id])
redirect_to root_url if #project.nil?
end
end
Model for Project
class Project < ApplicationRecord
before_save {name.downcase!}
belongs_to :user
default_scope -> { order(created_at: :desc) }
mount_uploader :picture, PictureUploader
validates :user_id, presence: true
validates :name && :category, presence: true
validates :name, presence: true,
uniqueness: { case_sensitive: false }
Test suit
require 'test_helper'
class ProjectsControllerTest < ActionDispatch::IntegrationTest
def setup
#project = projects(:Flyingcar)
end
test "should redirect destroy when not logged in" do
assert_no_difference 'Project.count' do
delete project_path(#project)
end
assert_redirected_to login_url
end
Any ideas of what it might be?
Thanks!
This current_user error is due to nil value of the current_user in the project_owner method.
Change this method to
def project_owner
if current_user.nil?
redirect_to root_url
else
#project = current_user.projects.find_by(id: params[:id])
redirect_to root_url if #project.nil?
end
end
Neither in create nor in project_owner the current_user is defined or are you doing that in your filter? Then you have to do that also for project_owner

Setting an id as a default foreign key in rails

I am having challenges assigning a current user a role in a team the user is creating. I want to assign the user that creates the team the role of the captain which could be changed later.
I'm currently using the create_asociation method that comes with has_one relationship, as this instantiates the values of the associated model, which i want to be instantiated with the current user but get the error Can't mass assign protected attribute: captain. Captain is a self join model with user as i will like to use captain.teammates and team.captain.
Below are the models involved.
User and Captain Model
class User < ActiveRecord::Base
has_one :profile
has_many :teammates, :class_name => "User", :foreign_key => "captain_id"
belongs_to :captain, :class_name => "User"
belongs_to :team
# before_create :build_profile
after_create :build_default_profile
accepts_nested_attributes_for :profile
attr_accessible :email, :password, :password_confirmation, :profile_attributes, :captain_id
def build_default_profile
Profile.create(user_id: self.id)
end
has_secure_password
before_save { email.downcase! }
before_save :create_remember_token
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 6 }
validates :password_confirmation, presence: true
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
Team Model
class Team < ActiveRecord::Base
has_many :profiles, through: :users
has_one :captain, :class_name => "User", foreign_key: :captain_id
has_one :result, as: :result_table
attr_accessible :teamname, :color, :result_attributes, :captain_attributes
after_create :build_result_table
after_create :build_default_captain
accepts_nested_attributes_for :profiles
accepts_nested_attributes_for :captain
accepts_nested_attributes_for :result
def build_result_table
Result.create(result_table_id: self.id, result_table_type: self.class.name)
end
def build_default_captain
# Team.captain = User
# Captain.create(team_id: self.id, captain_id: user.id)
end
end
User Controller
class UsersController < ApplicationController
before_filter :signed_in_user, only: [:index, :edit, :update, :destroy]
before_filter :correct_user, only: [:edit, :update]
before_filter :admin_user, only: :destroy
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save!
sign_in #user
flash[:success] = "Welcome to the JHDC Mini Olympics Web Application; Thanks for singing Up"
redirect_to user_profile_path(#user, #profile)
else
flash[:error_messages]
render 'new'
end
end
def show
#user = User.find(params[:id])
end
def index
#users = User.paginate(page: params[:page])
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
flash[:success] = "Profile Updated"
redirect_to user_profile_path(#user, #profile)
else
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted."
redirect_to users_url
end
private
def signed_in_user
unless signed_in?
store_location
redirect_to signin_url, notice: "Please sign in."
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_path) unless current_user?(#user)
end
def admin_user
redirect_to(root_path) unless current_user.admin?
end
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
end
Team Controller
class TeamsController < ApplicationController
def new
#team = Team.new
end
def create
#team = Team.new(params[:team])
#captain = #team.create_captain(captain: current_user)
if current_user.admin?
if #team.save!
flash[:success] = "Team created."
redirect_to #team
else
flash[:error_messages]
render 'new'
end
else
flash[:error] = "Sorry, you don't have the authority to create a Team"
redirect_to current_user
end
end
def index
#teams = Team.paginate(page: params[:page])
end
def show
#team = Team.find(params[:id])
end
def edit
if current_user.admin?
#team = Team.find(params[:id])
else
flash[:error] = "Sorry you dont have the authourity to edit a Team"
redirect_to current_user
end
end
def update
#team = Team.find(params[:id])
if #team.update_attributes(params[:team])
flash[:success] = "Team Updated"
redirect_to #team
else
render 'edit'
end
end
def destroy
Team.find(params[:id]).destroy
flash[:success] = "Team is deleted."
redirect_to teams_url
end
private
def team_params
params.require(:team).permit(:teamname, :color)
end
end
The admin is currently a way i'm using to restrict the user that can create a team but i plan to use gems like declarative authorization to create role based authorization. Thanks
The error you are getting is because the attribute :captain is not declared as attr_accessible
Either set the attribute :captain in your list of attr_accessible for the User model, or change the code form
Captain.create(team_id: self.id, captain_id: user.id)
to
captain = Captain.new
captain.team_id = self.id
captain.captain_id = user.id
captain.create
in this way, the attribute won't be set by mass-assignment and won't raise the error
Edited
After checking your code twice, just realized that you don't have a Captain model, actually :captain is a relation for the user and a relation from the Team to the User.
So on Team model, take off the build_default_captain stuff and the after_create :build_default_captain, I would say to replace with something like
after_save :set_default_captain
def set_default_captain
if captain_id_changed?
profiles.each do |user|
user.captain = captain
user.save
end
end
end
so every time the captain_id change for the model, you change the captain_id of all its profiles (users)
Then on the Team controller, on the action create, instead of
#team = Team.new(params[:team])
#captain = #team.create_captain(captain: current_user)
do something like
#team = Team.new(params[:team])
#team.captain = current_user
if current_user.admin?
if #team.save!
current_user.update_attribute(:team_id, #team.id)
flash[:success] = "Team created."
redirect_to #team
else
flash[:error_messages]
render 'new'
end
else
flash[:error] = "Sorry, you don't have the authority to create a Team"
redirect_to current_user
end
so on the last part of the code, you set the captain of the team to the current user and set the user team to the current team once its saved, you can also improve the code with current_user.build_team to avoid saving current_user.update_attribute

Changing roles in rails 4

I created the app with the help of rails composer. Using devise for authentication and cancan for managing roles. So I have 3 roles by default: Admin, User and VIP. I deleted VIP, because I don't need it. Run rake db:seed to create a default admin. Then I'm coming to localhost and seeing the "First User" as admin. I logout and register(signup) a new user. Then, signing in again as admin. I see, that by deafault, this new user doesn't have any role. And also I see, that I can change it("Change role" - button). I push it and as admin can choose whether new user will be the second admin or just User. I choose, for example, User, push "change role" and have an "ArgumentError in UsersController#update wrong number of arguments (2 for 1)".
Sooo, I have two questions:
1. How to give my admin the ability to change roles without errors.
2. How to make new signing up users to have default role "User".
Thanks!
Ok, I managed to set the default role this way:
after_create :assign_reader_role
private
def assign_reader_role
self.add_role "user"
end
Here is my UserControlle:
class UsersController < ApplicationController
before_filter :authenticate_user!
def index
authorize! :index, #user, :message => 'Not authorized as an administrator.'
#users = User.all
end
def show
#user = User.find(params[:id])
end
def update
authorize! :update, #user, :message => 'Not authorized as an administrator.'
user = User.find(params[:id])
if user.update_attributes(user_params)
redirect_to users_path, :notice => "User updated."
else
redirect_to users_path, :alert => "Unable to update user."
end
end
def destroy
authorize! :destroy, #user, :message => 'Not authorized as an administrator.'
user = User.find(params[:id])
unless user == current_user
user.destroy
redirect_to users_path, :notice => "User deleted."
else
redirect_to users_path, :notice => "Can't delete yourself."
end
end
private
def user_params
params.require(:user).permit(:name, :email)
end
end
Here is models.
User:
class User < ActiveRecord::Base
after_create :assign_reader_role
rolify
devise :database_authenticatable, :registerable,#:confirmable,
:recoverable, :rememberable, :trackable, :validatable
validates_presence_of :name
private
def assign_reader_role
self.add_role "user"
end
end
Role:
class Role < ActiveRecord::Base
has_and_belongs_to_many :users, :join_table => :users_roles
belongs_to :resource, :polymorphic => true
scopify
end
UserController I've already put! And where can I take params from the form?
I think you missed role_ids in permit
def user_params
params.require(:user).permit(:name, :email, :role_ids)
end

How to skip validations as admin during update_attributes?

I want to skip validation when I am trying to edit user as admin.
Model
class User
...
attr_accessible :company_id, :first_name, :disabled, as: :admin
Controller
class Admin::UsersController
...
def update
#user = User.find(params[:id])
#user.update_attributes(params[:user], as: :admin)
redirect_to edit_admin_user_path(#user), :notice => "User Account Updated"
end
So I tried to change update action to
def update
#user = User.find(params[:id])
#user.attributes = params[:user]
#user.save(validate: false)
redirect_to edit_admin_user_path(#user), :notice => "User Account Updated"
end
But then I dont have access to set :disabled and :company_id attributes because i dont know where to set as: :admin
Try this:
#user = User.find(params[:id])
#user.assign_attributes(params[:user], as: :admin)
#user.save(validate: false)
Strong Parameters
This has been an issue with rails for a long time, in Rails 4 they are introducing "Strong Parameters"
https://github.com/rails/strong_parameters
http://railscasts.com/episodes/371-strong-parameters
You can use strong parameters gem in rails 3 applications as well
Alternative: Context Attribute
Another way to do it, introduce a context variable on the user model - *Note I am not familiar with the 'as' option for attr_accessible*
class User < ActiveRecord::Base
attr_accessor :is_admin_applying_update
validate :company_id, :presence => :true, :unless => is_admin_applying_update
validate :disabled, :presence => :true, :unless => is_admin_applying_update
validate :first_name, :presence => :true, :unless => is_admin_applying_update
# etc...
In your admin controller set the is_admin_applying_update attribute to true
class Admin::UsersController
# ...
def update
#user = User.find(params[:id])
#user.is_admin_applying_update = true
#user.update_attributes(params[:user])
NOTE: you can also group the validations and use a single conditional
http://guides.rubyonrails.org/active_record_validations.html#grouping-conditional-validations
Hack method:
def update
#user = User.find(params[:id])
#user.define_singleton_method :run_validations! do true; end
#user.update_attributes(params[:user], :as => :admin)
redirect_to edit_admin_user_path(#user), :notice => "User Account Updated"
end

Rails 3.1 Mongoid has_secure_password

I'm attempting to get has_secure_password to play nice with mongoid. I'm following Railscasts #270, however when I to signin with a username/password, I get the error:
undefined method `find_by_email' for User:Class
I see a similar post (http://stackoverflow.com/questions/6920875/mongoid-and-has-secure-password) however, having done as it suggested I still get the same error.
Here is my model:
class User
include Mongoid::Document
include ActiveModel::SecurePassword
validates_presence_of :password, :on => :create
attr_accessible :email, :password, :password_confirmation
field :email, :type => String
field :password_digest, :type => String
has_secure_password
end
Controller:
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to root_url, :notice => "Logged in!"
else
flash.now.alert = "Invalid email or password"
render "new"
end
end
def destroy
session[:user_id] = nil
redirect_to root_url, :notice => "Logged out!"
end
end
Thanks.
Option 1: In your controller, you should use
user = User.where(:email => params[:email]).first
Option 2: In your model, you should define
def self.find_by_email(email)
where(:email => email).first
end
Mongoid doesn't support the find_by_attribute methods.
You should better use where instead:
User.where(:email => email).all

Resources