How to check for roles using rolify gem? - ruby-on-rails

I have a user model, where I am having methods to check for roles. I have total 5 to 6 roles. Super administrator should have access to view users with all the roles. I am using rolify gem (has_role?) for checking admin role. Can someone guide me on how to use it? As of now, I am getting undefined method include? error.
class User < ActiveRecord::Base
extend FriendlyId
self.table_name = "DIM_USER"
self.primary_key = "user_id"
self.sequence_name = 'DIM_USER_ID_SEQ'
rolify
devise :database_authenticatable,
:recoverable, :rememberable, :trackable, :validatable, :timeoutable
before_validation :strip_whitespace, :only => [:email]
default_scope {where(clean_up_flag: false)}
has_many :store_user_assignments
has_many :stores, through: :store_user_assignments
has_and_belongs_to_many :clients, join_table: :clients_users
has_many :store_user_assignments
has_many :stores, through: :store_user_assignments
belongs_to :role
before_save {self.email = email.downcase}
before_save :update_full_name
after_create :create_slug
friendly_id :slug, use: :slugged
NAME_MIN_LENGTH = 1
NAME_MAX_LENGTH = 100
EMAIL_MAX_LENGTH = 100
NAME_RANGE = NAME_MIN_LENGTH..NAME_MAX_LENGTH
validate :password_check
validates :encrypted_password, presence: true
scope :admin, -> {joins(:users_roles, :DIM_ROLE).where("users_roles.role_id = DIM_ROLE.role_id AND DIM_ROLE.name = 'super_administrator'").order(:last_name)}
scope :pmt_ptl_accnt_manager, -> {joins(:users_roles, :DIM_ROLE).where("users_roles.role_id = DIM_ROLE.role_id AND DIM_ROLE.name = 'Portal-Account-Manager-Client'").order(:last_name)}
scope :inactive_pmt_ptl_accnt_manager_with_no_stores, -> {joins(:users_roles, :DIM_ROLE).where("users.active=? AND users_roles.role_id = DIM_ROLE.role_id AND DIM_ROLE.name = ? AND users.user_id NOT IN (select user_id from stores_users)", false, 'Portal-Account-Manager-Client').order(:last_name)}
def active_for_authentication?
super && self.active?
end
def inactive_message
:invalid
end
def update_full_name
self.full_name = "#{first_name} #{last_name}"
end
def admin?
self.has_role?(:super_administrator)
end
def vt_user?
self.has_role?(:Virtual-Terminal-User)
end
def pmt_ptl_accnt_manager?
self.role.include?(Role.where(:name => 'Portal-Account-Manager-Client').first) ||
self.role.include?(Role.where(:name => 'Radial-Account-Manager').first)
end
def radial_account_manager?
self.role.include?(Role.where(:name => 'Radial-Account-Manager').first)
end
def payments_portal_readonly?
self.role.include?(Role.where(:name => 'Portal-Account-Manager-Read-Only-Client').first)
end
def search_user?
self.role.include?(Role.where(:name => 'Transaction-Search-Only').first)
end
def radial_readonly?
self.role.include?(Role.where(:name => 'Radial_ReadOnly').first)
end
end

According to the documentation, you should call has_role? method.
def pmt_ptl_accnt_manager?
self.has_role?('Portal-Account-Manager-Client') || self.has_role?('Radial-Account-Manager')
end

Related

Move method from Controller to Model in Rails

I have been told to move a method "Top" from Controller to Model, but when I try to call it, it doesn't work anymore.
I am using Rails 6
This is my Controller:
class UsersController < ApplicationController
before_action :authenticate_user!
def index
#users = User.all
end
def show
#user = User.find(params[:id])
#posts = #user.posts.ordered_by_most_recent
end
def edit
#user = User.find(params[:id])
end
def following
#title = 'Following'
#user = User.find(params[:id])
#users = #user.following.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = 'Followers'
#user = User.find(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
def top
#userst = User.joins(:followers).order('COUNT(followings.follower_id) DESC').group('users.id').limit(10)
end
end
and this would be my Model:
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, authentication_keys: [:username]
validates :fullname, presence: true, length: { maximum: 20 }
validates :username, presence: true, length: { maximum: 20 }
validates_uniqueness_of :username
has_many :posts
has_many :active_followings, class_name: 'Following',
foreign_key: 'follower_id',
dependent: :destroy
has_many :passive_followings, class_name: 'Following',
foreign_key: 'followed_id',
dependent: :destroy
has_many :following, through: :active_followings, source: :followed
has_many :followers, through: :passive_followings, source: :follower
mount_uploader :photo, FileUploader
mount_uploader :coverimage, FileUploader
# Follows a user.
def follow(other_user)
following << other_user
end
# Unfollows a user.
def unfollow(other_user)
following.delete(other_user)
end
# Returns true if the current user is following the other user.
def following?(other_user)
following.include?(other_user)
end
end
All code here makes sense to me, so I only had to create a file called top.html.erb like this to render the Top:
<article class="timeline new-initial">
<h3>Top:</h3>
<ul class="posts">
<%= render #userst %>
</ul>
</article>
Now, to be honest, I am lost, I am not sure how to move this method to the User model in the right way to read it in the view section.
This seems like a job for a scope.
Model:
scope :top, -> { joins(:followers).order('COUNT(followings.follower_id) DESC').group('users.id').limit(10) }
Controller:
def top
#userst = User.top
end

Devise-invitable : Upon inviting a user, 2 emails are being sent, an invitation mail and a mail that says "Thank you for registering with us"

I am using Devise-invitable to send invitations to users using their email addresses. When I send and invitation, one of the mails that is (correctly) sent is one with the invitation link. However, another email gets sent which tells the invitee "Thank you for registering with us.".
I cannot find why this is happening.
Ruby version : 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin15]
Rails version : 4.2.4
devise version : 3.5.10
devise_invitable version : 1.6.0
Code in invitations controller:
class InvitationsController < Devise::InvitationsController
def create
params[:user][:email].each do |email|
User.invite!({:email => email}, current_user) #If I comment out this line, both emails are not sent
end
redirect_to root_path
end
end
Code in User.rb
class User < ActiveRecord::Base
# after_create :send_welcome_email
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :invitable, :omniauthable, :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable
attr_accessor :user_profile_phone, :user_profile_city
# Relationships
has_many :identities
has_many :orders
belongs_to :role, :dependent => :destroy
belongs_to :userdatum, :dependent => :destroy
belongs_to :userprofile, :dependent => :destroy
has_many :comments
has_many :deliveries
has_many :conversations, :dependent => :destroy
# Filters
before_save :assign_role
after_create :send_welcome_mail
def send_welcome_mail
UserMailer.registration(self).deliver_now
end
def assign_role
self.role = Role.find_by name: "User" if self.role.nil?
end
def facebook
identities.where( :provider => "facebook" ).first
end
def facebook_client
#facebook_client ||= Facebook.client( access_token: facebook.accesstoken )
end
def google_oauth2
identities.where( :provider => "google_oauth2" ).first
end
def google_oauth2_client
if !#google_oauth2_client
#google_oauth2_client = Google::APIClient.new(:application_name => 'Test', :application_version => "1.0.0" )
#google_oauth2_client.authorization.update_token!({:access_token => google_oauth2.accesstoken, :refresh_token => google_oauth2.refreshtoken})
end
#google_oauth2_client
end
def admin?
self.role.name == "Admin"
end
def sytlist?
self.role.name == "Stylist"
end
def user?
self.role.name == "User"
end
end
You are sending both emails yourself.
User.invite! sends out an invitation email by default.
You need to remove the after_create :send_welcome_mail in user.rb. That's what is sending out the "welcome email".
Solution I used was to check for token here :
after_create :send_welcome_email
def send_welcome_mail
if self.invitation_token.nil?
UserMailer.registration(self).deliver_now
end
end
and add
def accept_invitation!
super
UserMailer.registration(self).deliver_now
end
Not sure if this is the best but it solved my problem.

Joint query across 2 models (has_many)

Hi I need help and all insight appreciated. I have two models Auctions and Bids and I want to retrieve the All auctions current_user won, the ones s/he has been outbid on and the ones s/he's winning
Here are the two models:
class Auction < ActiveRecord::Base
extend FriendlyId
friendly_id :guid, use: :slugged
before_save :populate_guid
mount_uploaders :images, ImageUploader
belongs_to :client
has_many :bids, dependent: :destroy
has_one :order, dependent: :destroy
validates_presence_of :title, :lien_price,
:end_time, :collateral_value,
:redemption_date, :current_interest_rate,
:additional_tax, :collateral_details,
:location, :client_id, :starting_bid
validate :end_time_in_the_future, :on => :update
validates_uniqueness_of :guid, case_sensitive: false
def end_time_in_the_future
errors.add(:end_time, "can't be in the past") if self.end_time && self.end_time < Time.now
end
def self.get_active_auctions
where("end_time > ?", Time.now)
end
def self.closed_auctions
where("end_time < ?", Time.now)
end
def highest_bid
self.bids.maximum("amount")
end
def highest_bid_object
self.bids.order(:amount => :desc).limit(1).first
end
def highest_bidder
self.highest_bid_object.user if highest_bid_object
end
def closed?
self.end_time < Time.now
end
private
def populate_guid
if new_record?
while !valid? || self.guid.nil?
self.guid = SecureRandom.random_number(1_000_000_000).to_s(36)
end
end
end
end
and
class Bid < ActiveRecord::Base
extend FriendlyId
friendly_id :guid, use: :slugged
belongs_to :auction
belongs_to :user
before_save :populate_guid
validates_presence_of :amount, :user_id,
:auction_id
#validate :higher_than_current?
validates :amount, :numericality => true
validates_uniqueness_of :guid, case_sensitive: false
def higher_than_current?
if !Bid.where("amount > ? AND auction_id = ?", amount, self.auction.id).empty?
errors.add(:amount, "is too low! It can't be lower than the current bid, sorry.")
end
end
private
def populate_guid
if new_record?
while !valid? || self.guid.nil?
self.guid = SecureRandom.random_number(1_000_000_000).to_s(36)
end
end
end
end
I thought
#auctions = Auction.closed_auctions.where(highest_bidder: current_user)
or
#auctions = Auction.closed_auctions.joins(:bids).where(highest_bidder: current_user)
would work but they both raise an error.
Edit this works
#auctions = Auction.closed_auctions.references(highest_bidder: current_user)
But there's probably a better way.
You probably can't access current_user from controller (devise?). So you need to pass the user as a parameter to the class or instance method. What you should look into are scopes and especially scopes that accept parameters. Scopes could really help you refactor your Auction model (you really don't need any methods that only return a where()), but also solve the inaccessible current_user.
Use it like this in your Auction model:
scope: :highest_bidder -> (current_user) { where(highest_bidder: current_user) }
And call it like this from your controller:
#auctions = Auction.closed_auctions.highest_bidder(current_user)

Sort users by score. Numbers being rendered instead of names

The database column for my users' names is first_name. Score is not a column in the guides database.
In my karma controller:
def hiscores
#users = User.all.map(&:guides).flatten.map(&:score).sort
end
In hiscores.html.erb
<h1>Karma Hiscores</h1>
<blockquote>Users sorted by most karma</blockquote>
<ul>
<% #users.each do |user| %>
<li><%= user %></li>
<% end %>
</ul>
EDITS
Guide model
class Guide < ActiveRecord::Base
validates :link, uniqueness: true
validates :link, presence: true
validates :title, presence: true
belongs_to :user
has_many :comments
acts_as_taggable
acts_as_votable
def to_param
"#{id} #{title}".parameterize
end
def score
upvotes.count - downvotes.count
end
end
User.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, #:recoverable,
:rememberable, :trackable, :validatable
validates :first_name, presence: true
validates :email, presence: true
validates :email, uniqueness: true
validates :runescape_username, presence: true
has_many :guides
has_many :comments
acts_as_voter
end
Here is what my karma controller looks like now:
class KarmaController < ApplicationController
def hiscores
#users = (guides.upvote.count - guides.downvote.count).desc
end
end
In your karma controller:
def hiscores
#users = User.all.sort{ |x, y| y.user_score <=> x.user_score }
end
In your User.rb:
If each user has_many: guides, then
def user_score
self.guides.inject(0) { |sum, guide| sum += guide.score }
end
Else if each user has_one: guide, then
def user_score
self.guide.score
end
How about something like this:
#users = User.find(:all, include: [:guides], order: "(guides.upvotes.count - guides.downvotes.count) desc")
I am not sure if include is required, I am typing from the top of my head :)
Edit:
You should try to use Caching from acts_as_votable.
This would give you fields such as cached_votes_up and cached_votes_down and cached_votes_score.
Then you could query users as
#users = User.find(:all, include: [:guides], order: "guides.cached_votes_score desc")

Error self.user.update_attribute undefined method nil:Class

UPDATE POST I'm noob in RoR and i start the test. I have an application and I try to use test with rspec and capybara, I want create user and test the login. But when i do my test i have some error with my models users, because in my app I create user and i call an after_create :create_order
I modify my factories.rb but i have an error in my update_attributes
See my model user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
has_many :ratings
has_many :rated_sounds, :through => :ratings, :source => :sounds
has_many :sounds ,:dependent => :destroy
has_many :orders ,:dependent => :destroy
has_many :song_ups ,:dependent => :destroy
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:registerable, :confirmable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me,
:nom, :prenom, :societe, :tel, :cgu, :sign_in_count, :trans_simu,
:trans_limit, :project, :vat_number, :adress, :zip_code, :city, :tutorial
after_create :create_order
def create_order
order = Order.create(user_id: self.id,files_left: 3)
order.subscription = Subscription.where(category: 'test').first || Subscription.create(category: 'test')
self.update_attribute(:trans_limit, 1)
#Ancien Order
# Order.create(user_id: self.id, subscription_id: Subscription.where(category: 'test').first.id, files_left: 3)
# self.update_attribute(:trans_limit, 1)
end
def test?
self.orders.trial.present? && self.orders.count==1
end
def unlimited?
!self.test? && self.orders.current.where(time_left: -1).any?
end
def allow_send?
!self.finish_order? && self.sounds.in_progress.count < self.trans_limit.to_i
end
def finish_order?
self.orders.current.empty?
end
end
For create my user in my test I use FactoryGirl. And i write this :
require 'factory_girl'
FactoryGirl.define do
factory :user do
sequence(:email) {|n| "email#{n}#factory.com" }
password "foobar"
password_confirmation "foobar"
societe "RspecTest"
prenom "John"
nom "Doe"
tel "0101010101"
confirmed_at Time.now
association :order
end
factory :order do
association :subscription
end
factory :subscription do
end
end
And one of my test is :
scenario "User login right" do
visit new_user_session_path
current_path.should == "/users/sign_in"
page.html.should include('<h2>Se connecter</h2>')
user = FactoryGirl.create(:user)
fill_in "Email", :with => user.email
fill_in "Mot de passe", :with => user.password
check "user_remember_me"
click_button "Connexion"
page.should have_content('Mon compte')
current_path.should == root_path
end
My order.rb
class Order < ActiveRecord::Base
attr_accessible :nb_files, :user_id, :period, :time_left, :subscription_id, :promo_id, :promo_end_date, :max_time_file, :files_left, :ended_at
belongs_to :user
belongs_to :subscription
scope :current, where('files_left != ? AND time_left != ? AND (ended_at >= ? OR ended_at IS ?)', 0, 0, Time.now, nil)
before_create :init_value
def self.trial
self.where(subscription_id: Subscription.where(category: 'test').first.id).first
end
def init_value
self.time_left = self.subscription.trans_seconds
self.max_time_file = self.subscription.max_time_file
if self.subscription.category != 'test'
self.user.update_attribute(:trans_limit, 1)
Order.where(user_id: self.user_id, subscription_id: Subscription.where(category: 'test')).destroy_all
else
self.files_left = 3
end
end
end
My error :
Failure/Error: user = FactoryGirl.create(:user)
NoMethodError:
undefined method `trans_seconds' for nil:NilClass
# ./app/models/order.rb:13:in `init_value'
# ./app/models/user.rb:21:in `create_order'
I hope you can help me. Thank's
You don't have Subscription with category 'test' in your database. Solution depend on how you want to handle this kind of error.
If you expect this subscription always to be in your database, use db:seed rake task for prepopulating your db. (try googling it to find out how to do this)
If you don't want to assign any subscription if given doesn't exist try:
def create_order
order = Order.create(user_id: self.id,files_left: 3)
order.subscription = Subscription.where(category: 'test').first
self.update_attribute(:trans_limit, 1)
end
And finally, if you want to create such a subscription if it doesn't exist:
def create_order
order = Order.create(user_id: self.id,files_left: 3)
order.subscription = Subscription.find_or_create_by_category('test')
self.update_attribute(:trans_limit, 1)
end
In create_order try using this:
Order.create!(user: self, subscription: Subscription.where(category: 'test').first, files_left: 3)
Use objects instead of plain ids.
On before_* methods
To make sure that validations pass, return true at the end of these methods. In your case, add return true at the end of the init_value method.

Resources