Rails Role model default if doesn't exist - ruby-on-rails

We have a Role model that is connected to User via role_id, and there are some users that don't have roles assigned.
Would like to set a default Role on selecting users when they don't explicitly have a role set. Didn't setup a default in the migration.
Trying to use a loop:
users.each do |user|
# some users don't have roles so there are errors
end
The Role model:
class Role < ActiveRecord::Base
has_many :users
# Can I add something to set the default role
end
The user model:
class User < ActiveRecord::Base
belongs_to :role
end
Also if this is not a railsy way to approach this problem, any suggestions would be appreciated.

You could add an initialize method to the User model
after_initialize :init
def init
self.role =>'my_default_role'
end

You could also do a simple check for user.role_id before you do something role-related in your loop.
users.each do |user|
user.role = Role.find_by(name: 'user') unless user.role
end
Alternatively, you could modify the role method in your user class to add a default one if it's undefined.
class User < ActiveRecord::Base
belongs_to :role
def role
return :role if :role
Role.find_by(name: 'user')
end
end
Finally, you could just create a migration to add a role to all users who don't have one.

Related

Rails 4 + Devise: After create user, create XYZ

I have a User model that gets created through Devise, and after it's creation, I would like to automatically create a new Client (another model in my app). The new Client's atrribute, :user_id, should be equal to the :id of the User that was just created. I believe I need to use something like:
class Users::RegistrationsController < Devise::RegistrationsController
after_create :create_client
def create_client
Client.create(:user_id, :id) # Not sure what should go here
end
end
Is this the correct way to accomplish this? Also, if associations are important Client belongs_to :user and User has_one :client
You can add an after_create callback in User model(user.rb), check here for more information on how to create has_one associations.
class User < ActiveRecord::Base
after_save :add_client
def add_client
self.create_client(client_attribute1: value, client_attribute2: value)
end
end

Rails Devise - How to add more data to current_user

Suppose I have a User model
user.rb
class User < ActiveRecord::Base
...
end
with attributes like: name, username, access
access is an enum that tells me if the user is "staff" or "customer"
To get the name and username of the logged in user, I can do:
current_user.name
current_user.username
And suppose I have a Staff model
staff.rb
class Staff < ActiveRecord::Base
belongs_to :user
end
with attributes like: salary, phone_number
And I also have a Customer model
customer.rb
class Customer < ActiveRecord::Base
belongs_to :user
end
with attributes like: address, phone_number
I want to be able to call this on my staff's controller:
current_user.staff.salary
And this on my customer's controller:
current_user.customer.address
WHAT I TRIED SO FAR
I overwrote sessions_controller.rb
def create
super
model_name = current_user.access.capitalize.constantize
spec = model_name.where(user_id: current_user.id).take
session[:spec] = spec
end
So I'm able to access it via session[:spec], but not via current_user. Any ideas?
Well to begin with, your User model should reference the staff or customer, even if they are to stay blank
class User
has_one :staff
has_one :address
Just by doing this, you should be able to use current_user.customer.address. However...
I suggest you add some convenient methods in ApplicationController or a module that you include
def staff_signed_in?
#staff_signed_in ||= (user_signed_in? and current_user.access == :staff)
end
def current_staff
#current_staff ||= (current_user.staff if staff_logged_in?)
end
# same for customer
# Note that I use instance variables so any database queries are executed only once !
Then you can simply call
<% if customer_signed_in? %>
<h2>Logged in as customer</h2>
<p>Address : <%= current_customer.address %>
<% end %>
EDIT : about your concerns concerning database hits
You gave the example of current_user.customer.cart.products
This is indeed quite a nested association. My suggestion above already reduces it by one level (ie current_customer == current_user.customer). Then you have to go through carts to reach products... it isn't so bad in my opinion.
If you need to call that often (current_customercustomer.cart) you can override the current_customer for a given controller and eager load the resources you know you will use use.
def UserShopController < ApplicationController
# Let's assume current_customer is defined in ApplicationController like I showed above
# UserShopController always uses the customer cart, so let's load it right at the beginning
...
private
# Override with eager loading
def current_customer
#current_customer ||= (current_user.customer.includes(:cart) if customer_logged_in?)
end
add has_one :customer to your user.rb
Your user model should be like below to accessing related model.
class User < ActiveRecord::Base
has_one :customer
end

Cancancan default role

I'm building rails app that has some role\abilities separation. I decided to use cancancan + devise, but i can't figure out how to set standard user role?
class User < ActiveRecord::Base
ROLES = %i[admin moderator author banned]
end
You can do a callback on your User model:
class User < ActiveRecord::Base
after_create :assign_default_role
def assign_default_role
add_role(:default_role) if self.roles.blank?
end
end
If after_create isn't suitable, try another callback, more info
here
When defining abilities, we use an ability called 'user' for default user permissions. In other words, a user with no other roles gets the default set of abilities.
We also use a set of 'guest' permissions for visitors that are not signed in.
You can use following pattern to simplify Ability class. Notice, that defining rules for "default" role here is very simple, because it's just signed in user without roles.
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
# this is abitilites for anonymous user
can :read, Post
return unless user.persisted?
# ok, now we know that this user is logged in and can define common abilities
can :create, Post
# and after it we can define abilities for different roles
# user.roles here should return name of roles for user,
# like [:admin, :moderator]
user.roles.each { |role| self.public_send(role, user) if respond_to?(role) }
end
def admin(user)
# abitlites for admin here
end
def moderator(user)
# abilities for moderator here
end
end
Instead of callback I would set default value on field or in enumeration.
class User
include Mongoid::Document
...
field :roles, type: Array # , default: [:am]
extend Enumerize
enumerize :roles, in: [:superadmin, :am, :salesrep], multiple: true #, default: :am
end

Creating a one-to-one relation

class User
has_one :super_admin
end
class SuperAdmin
belongs_to :user
end
How would I create a SuperAdmin instance associating a certain user, from withing the User model?
I'm looking for something like this (in the User model), but it's not working:
def promote_to_super
self.super_admin.create!
end
You can use create_association(attributes = {}):
def promote_to_super
self.create_super_admin
end
See more here.

Rails, devise, acl

I have followed this tut http://railsapps.github.com/tutorial-rails-bootstrap-devise-cancan.html I want to do something like this:
before_filter :authenticate_user!
before_filter :authenticate_VIP!
before_filter :authenticate_admin!
before_filter :authenticate_somerole!
I have tables: roles, users, user_roles and I don't want to create another table (rails g devise VIP create another table).
I want to have methods authenticate_ROLE. How to do this ?
I have three table, Users, Roles, and RoleRelationships (or role_users, it's up to you)
This is my Role table:
class Role < ActiveRecord::Base
attr_accessible :name
has_many :role_relationships
has_many :users, through: :role_relationships
end
Role table will have name column for roles, like: "admin", "teacher", "vip" (as you want).
And this is User table:
class User < ActiveRecord::Base
devise ...
has_many :role_relationships
has_many :roles, through: :role_relationships
end
and my RoleRelationship table:
class RoleRelationship < ActiveRecord::Base
attr_protected :role_id, :user_id
belongs_to :user
belongs_to :role
end
I set up my app one user can have many roles, you can set up your way. So, i have a role?(role) method in my user.rb, like this:
def role?(role)
return role == RoleRelationship.find_by_user_id(self.id).role.name
end
Then in my abilities files, i define abilities of users:
def initialize(user)
user ||= User.new # guest user
if user.role? "teacher"
can :read, Course
can :manage, Topic, user_id: user.id
can :create, Topic
else user.role? "admin"
can :manage, Course
end
So, teacher will only read Course, and admin can CRUD Course. To do that, i use method load_and_authorize_resource in my CoursesController:
class CoursesController < ApplicationController
load_and_authorize_resource
before_filter :authenticate_user!
...
end
Finally, in my views, i used code like this:
<% if can? manage, #course %>
Only admin can work, see what happen here.
<% end %>
So, as you see, teacher only can read Course so they can't see or do what admin can do, in this case, is create course or edit course.
This is what i built in my online test app, you can reference and do the same for your app.

Resources