I am trying to add a user role according to Rolify gem in my devise User model.
What I basically want to achieve is that, if the user has selected that he is a student or a teacher in the registration page itself, after the creation of the user, it should add the required roles to the user.
Please note that I am not storing the 'role' in the User table. I am just using attr_accessor to send me an initial value to compare.
This is my User model code :
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# email :string(255) default(""), not null
# encrypted_password :string(255) default(""), not null
# reset_password_token :string(255)
# reset_password_sent_at :datetime
# remember_created_at :datetime
# sign_in_count :integer default(0), not null
# current_sign_in_at :datetime
# last_sign_in_at :datetime
# current_sign_in_ip :string(255)
# last_sign_in_ip :string(255)
# created_at :datetime not null
# updated_at :datetime not null
# avatar :string(255)
# username :string(255)
#
class User < ActiveRecord::Base
# This is for the user roles.
rolify
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessor :role
# Adding the carrierwave uploader
mount_uploader :avatar, AvatarUploader
before_create :add_specified_role
def add_specified_role
if self.role == 0
# I am sure I am messing it up here and this is not the way.. :/
after_create :add_student_role
elsif self.role == 1
after_create :add_teacher_role
end
end
def add_student_role
self.add_role(:student) if self.roles.blank?
end
def add_teacher_role
self.add_role(:teacher) if self.roles.blank?
end
end
However, it does not seem to be working as when I check the roles, the role has not been added and I am sure I am doing something wrong.
What is the correct way to achieve the above task?
callback should be
after_create :add_specified_role , if: proc { |a| a.roles.blank? }
instead of
before_create :add_specified_role
And method should
def add_specified_role
if role == 0
add_role(:student)
elsif role == 1
add_role(:teacher)
end
end
Related
I want to skip the default image that I assign to paperclip, only when remote_avatar is not null, that is, if the user starts a session with facebook that uses remote_avatar and in the opposite case that paperclip assigns a default image to my attachment :avatar
user.rb
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# email :string default(""), not null
# encrypted_password :string default(""), not null
# reset_password_token :string
# reset_password_sent_at :datetime
# remember_created_at :datetime
# sign_in_count :integer default(0), not null
# current_sign_in_at :datetime
# last_sign_in_at :datetime
# current_sign_in_ip :string
# last_sign_in_ip :string
# confirmation_token :string
# confirmed_at :datetime
# confirmation_sent_at :datetime
# username :string
# avatar_file_name :string
# avatar_content_type :string
# avatar_file_size :integer
# avatar_updated_at :datetime
# bio :text
# birthdate :date
# nationality :string
# gender :string
# created_at :datetime not null
# updated_at :datetime not null
# roles_mask :integer
# uid :string
# provider :string
# doctor_id :integer
# facebook :string
# remote_avatar :string
#
# Indexes
#
# index_users_on_confirmation_token (confirmation_token) UNIQUE
# index_users_on_doctor_id (doctor_id)
# index_users_on_email (email) UNIQUE
# index_users_on_reset_password_token (reset_password_token) UNIQUE
# index_users_on_username (username) UNIQUE
#
class User < ApplicationRecord
...
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable, :omniauth_providers => [:facebook]
validates :username, uniqueness: {message: "The username has already been taken."}
has_attached_file :avatar, styles:{thumb:"42x42",medium:"300x300"}, default_url:"/images/:style/user.png"
validates_attachment_content_type :avatar, content_type: /\Aimage\/.*\Z/
def self.from_omniauth(auth)
where(provider: auth[:provider], uid: auth[:uid]).first_or_create do |user|
if auth[:info]
...
user.facebook = auth[:extra][:raw_info][:link]
if auth[:info][:image]
user.remote_avatar = auth[:info][:image]
user.avatar = nil
end
...
end
end
end
end
Looks like you can make it easier:
<%= current_user.avatar? ? current_user.avatar.url(:medium) : '/images/medium/user.png' %>
Or you can to use helper, like:
def self.fetch_avatar(user)
return '/images/medium/user.png' unless user.avatar?
user.avatar.url(:medium)
end
ERB:
<%= SomethingHelper.fetch_avatar(current_user) %>
In a Rails app I'm testing user confirmations
scenario "unconfirmed user cannot login" do
user = create(:user, confirmation_sent_at: 10.days.ago ) #FactoryGirl factory
login user
expect(current_path).to eq(#unconfirmed_user_path)
end
This fails, and the user is signed in.
However if I change the first line as follows
user = create(:user)
user.confirmation_sent_at = 10.days.ago
user.save
login user .....
The test passes.
What am I overlooking? These two pieces of code are equivalent aren't they?
EDIT
#factories/user_factory.rb
factory :user do
email { Faker::Internet.email }
password Faker::Internet.password(10, 20)
end
# models/user.rb
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# email :string(255) default(""), not null
# encrypted_password :string(255) default(""), not null
# confirmation_token :string(255)
# confirmed_at :datetime
# confirmation_sent_at :datetime
# ...
class User < ActiveRecord::Base
devise :invitable, :database_authenticatable, :registerable, :confirmable, :lockable, :recoverable, :rememberable, :trackable, :validatable, :omniauthable
...
end
I am trying to make a User able to edit their own Dashboard.
I am using CanCan and Devise to try to make this happen (Rolify is also installed to help with Admin features, but I don't feel it's applicable to this situation).
When I have a user logged in and they visit their :show dashboard page at root/users/id/dashboard.id they fail authentication although they have the same user.id as listed on the dashboard.user_id (as dictated in the ability.rb file.
How do I ensure that a user can view their own dashboard?
Here is the associated code:
dashboards_controller.rb:
class DashboardsController < ApplicationController
before_filter :authenticate_user!
def show
#dashboard = Dashboard.find(params[:format])
authorize! :read, current_user, :message => 'Not authorized to view this dashboard.'
end
end
ability.rb:
user ||= User.new
if user.has_role? :default # All of my users have this role
can :read, Dashboard, :user_id => user.id
end
from routes.rb
resources :users do
resource :dashboard, only: [:show, :edit, :update, :destroy]
end
User.rb:
# id :integer not null, primary key
# email :string(255) default(""), not null
# encrypted_password :string(255) default(""), not null
# reset_password_token :string(255)
# reset_password_sent_at :datetime
# remember_created_at :datetime
# sign_in_count :integer default(0)
# current_sign_in_at :datetime
# last_sign_in_at :datetime
# current_sign_in_ip :string(255)
# last_sign_in_ip :string(255)
# created_at :datetime
# updated_at :datetime
#
class User < ActiveRecord::Base
rolify
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :dashboard
before_create :build_dashboard
end
Dashboard.rb:
# Table name: dashboards
#
# id :integer not null, primary key
# user_id :integer
# has_selected_account_type :boolean default(FALSE)
# created_at :datetime
# updated_at :datetime
#
class Dashboard < ActiveRecord::Base
belongs_to :user
end
Please take a look here
Basically, you want to use the load_resource helper method to fetch only current_user's dashboard. Behind the scene it is doing something like this in your controller:
current_user.dashboard.find(params[:dashboard_id])
I find my models getting more and more clumped and messy. below is my actual user model, any suggestions on cleaning things up and arranging things for better readability?
Im getting frustrated by the unreadability of this all, so any general thoughts on this code are welcome. I really like code that does the same to be positioned together but like I have now is just one big bulk unreadable mess.
# == Schema Information
#
# Table name: users
#
# id :integer(4) not null, primary key
# email :string(255) default(""), not null
# encrypted_password :string(255) default(""), not null
# reset_password_token :string(255)
# reset_password_sent_at :datetime
# remember_created_at :datetime
# sign_in_count :integer(4) default(0)
# current_sign_in_at :datetime
# last_sign_in_at :datetime
# current_sign_in_ip :string(255)
# last_sign_in_ip :string(255)
# password_salt :string(255)
# confirmation_token :string(255)
# confirmed_at :datetime
# confirmation_sent_at :datetime
# unconfirmed_email :string(255)
# failed_attempts :integer(4) default(0)
# unlock_token :string(255)
# locked_at :datetime
# authentication_token :string(255)
# username :string(255)
# is_blocked :boolean(1)
# is_deleted :boolean(1)
# role :string(255)
# slug :string(255)
# created_at :datetime not null
# updated_at :datetime not null
# last_seen :datetime
# credits :integer(4)
#
class User < ActiveRecord::Base
devise :database_authenticatable,
:registerable,
:recoverable,
:rememberable,
:trackable,
:validatable,
:token_authenticatable,
:encryptable,
:confirmable,
:lockable,
:timeoutable,
:lastseenable
#:omniauthable
attr_accessible :username,
:login,
:email,
:password,
:password_confirmation,
:remember_me,
:profile_attributes,
:is_moderated,
:is_blocked,
:is_deleted,
:credits,
:role,
:confirmed_at,
:last_seen,
:invite_code
attr_accessor :login
#attr_accessor :invite_code
has_one :profile
has_one :account
accepts_nested_attributes_for :profile
accepts_nested_attributes_for :account
extend FriendlyId
friendly_id :username, use: :slugged
before_create :default_values
# AFTER CREATE -------------------------------------------------------------------------------------------------------
after_create :add_account
def add_account
self.create_account
end
def default_values
self.credits = -1
self.invite_code = invite_code
#self.reset_authentication_token!
beta = Beta.where(:code => invite_code).first
beta.used = 1
beta.save
end
# ROLES --------------------------------------------------------------------------------------------------------------
before_create :setup_default_role_for_new_users
ROLES = %w[admin default vip]
# VALIDATIONS --------------------------------------------------------------------------------------------------------
before_validation { |u| u.username.downcase! }
before_validation { |u| u.email.downcase! }
validates_uniqueness_of :username,
:email,
:case_sensitive => false
validates_presence_of :email,
:username,
:invite_code
validates :username,
:exclusion => {:in => ["admin", "root", "administrator", "superuser", "myhost", "support", "contact", "chat", "boo"],
:message => "is reserved"}
validate :check_email, :on => :create
validate :check_invite_code, :on => :create
def check_invite_code
errors.add(:invite_code, "Invalid code") unless Beta.where(:code => invite_code, :used => 0).first
end
# Devise
def active_for_authentication?
super && !is_deleted
end
# Devise
def confirm!
#welcome_message
#super
end
# Devise
def soft_delete
update_attribute(:deleted_at, Time.current)
end
def is_moderated?
return self.is_moderated
end
def is_online?
if self.last_seen < 10.minutes.ago
return true
else
return false
end
end
private
def setup_default_role_for_new_users
if self.role.blank?
self.role = "default"
end
end
def welcome_message
#::Devise.mailer.welcome_instructions(self).deliver
::Devise.mailer.welcome_instructions(self).deliver
end
def check_email
host = email.split("#").last
username = email.split("#").first
reserved_word_filters = %w(admin hostmaster root support )
if /.*(#{reserved_word_filters.join("|")}).*\#/.match(email)
errors.add(:email, "Invalid username in email")
end
if email.include? 'myhost'
errors.add(:email, "Invalid email")
end
end
# DEVISE: --------------------------------------------------------------------------------------------------
protected
def self.find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
login = conditions.delete(:login)
where(conditions).where(["lower(username) = :value OR lower(email) = :value", {:value => login.strip.downcase}]).first
end
end
The only thing I would do to clean up models is to modularize them a bit. DHH himself posted a great example gist showing how to clean up a model that had gotten too large. I don't think yours is particularly too big, but if you wanted to move all the devise stuff into its own module, it certainly would make your model a bit more tidy.
You could also get in the habit of treating booleans as first class expressions. For example,
def is_online?
if self.last_seen < 10.minutes.ago
return true
else
return false
end
end
is more clearly written as:
def is_online?
last_seen < 10.minutes.ago
end
1.
You have return statements all over your code. Unless you are returning something prematurely in a method, the return of the last statement in a method is what Ruby returns automatically. Like so:
def square(x)
val = x**2
return val
end
can be shortened to:
def square(x)
x**2
end
It's a contrived example, but there it is.
2.
Many of the selfs are redundant. In an model instance's scope, when setting an attribute to a value or calling a method, you do not need to prepend self, since that method/variable is already being called from that same scope.
What I would like to happen is every single time a new file is uploaded, user.space_used is updated.
But that doesn't happen right now. In order to get an update, I have to manually run user.update_space at the console to update a specific user, or use an array to cycle through all the users and update it like that.
How do I get it to do it at the right time - also, it would be nice if I could verify that the space_used column on the User model has the total sum of the filesizes of all the files uploaded for that user on the Upload model.
My user model looks like this:
# == Schema Information
# Schema version: 20110412170916
#
# Table name: users
#
# id :integer not null, primary key
# email :string(255)
# encrypted_password :string(128)
# password_salt :string(255)
# reset_password_token :string(255)
# remember_token :string(255)
# remember_created_at :datetime
# sign_in_count :integer
# current_sign_in_at :datetime
# last_sign_in_at :datetime
# current_sign_in_ip :string(255)
# last_sign_in_ip :string(255)
# username :string(255)
# first_name :string(255)
# last_name :string(255)
# created_at :datetime
# updated_at :datetime
# invitation_token :string(60)
# invitation_sent_at :datetime
# plan_id :integer
# current_state :string(255)
# confirmation_token :string(255)
# confirmed_at :datetime
# confirmation_sent_at :datetime
# space_used :integer default(0), not null
# failed_attempts :integer default(0)
# unlock_token :string(255)
# locked_at :datetime
# trial_end_date :date
# active_subscription :boolean
#
class User < ActiveRecord::Base
acts_as_voter
devise :database_authenticatable, :confirmable, :registerable, :timeoutable,
:recoverable, :rememberable, :trackable, :validatable, :invitable, :lockable
attr_accessible :email, :password, :password_confirmation, :remember_me, :username, :first_name, :last_name, :plan_id
after_save :update_space
def role_symbols
roles.map do |role|
role.name.underscore.to_sym
end
end
def update_space
total_size = 0
if self.uploads.count > 0
self.uploads.each do |upload|
total_size += upload[:image_file_size]
end
end
self.space_used = total_size
end
def space_threshold_reached?
self.plan.storage == self.space_used
end
def space_left
(self.plan.storage * 1024 * 1024 * 1024) - self.space_used.to_f
end
end
My Upload model looks like this:
# == Schema Information
# Schema version: 20110330215959
#
# Table name: uploads
#
# id :integer not null, primary key
# name :string(255)
# description :string(255)
# image_file_name :string(255)
# image_content_type :string(255)
# image_file_size :integer
# image_updated_at :datetime
# stage_id :integer
# user_id :integer
# created_at :datetime
# updated_at :datetime
class Upload < ActiveRecord::Base
acts_as_voteable
has_attached_file :image, :styles => {
:thumb => "64x64" },
:storage => :s3,
:path => "/:style/:id/:filename"
validates_attachment_presence :image
validates_attachment_size :image, :less_than => 10.megabytes
validates_attachment_content_type :image, :content_type => ['image/jpeg', 'image/png', 'image/gif', 'image/jpg', 'image/JPG']
after_post_process :update_users_space_used
def self.total_used
total_size = 0
all.each do |upload|
total_size += upload.image_file_size
end
return total_size
end
def update_users_space_used
Authorization.current_user.space_used += self.image_file_size
end
end
Thanks.
Edit1: Btw, I am using paperclip to manage the uploads.
Edit2: In the Upload.rb model, I changed the before_save to the after_post_process callback for paperclip and it still doesn't work.
Seems like Paperclip's after_post_process callback would be what you want.