I am using Devise in Rails 3, and have a User model in rails that is starting to get kinda crowded.. so I would like to put all of the login meethods inside of a module and include them from my model. I'm trying something like:
app/model/user.rb
class User < ActiveRecord::Base
include UserImageable
extend Loginable
has_one :profile, :dependent => :destroy
has_many :items, :dependent => :destroy
has_many :products, :through => :items
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :first_name, :last_name, :phone_number, :location, :photo, :profile_attributes, :access_token
delegate :first_name, :last_name, :phone_number, :phone_number=, :location, :location=, :photo, :to => :profile
accepts_nested_attributes_for :profile
end
and
lib/autoloads/loginable.rb
module Loginable
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :omniauthable
def password_require?
new_record?
end
end
but the server doesn't seem to like that, as it loads with a NoMeethodError
loginable.rb:4:in `<module:Loginable>': undefined method `devise' for Loginable:Module (NoMethodError)
Is there a way to do what I'm shooting for, or not really?
Thanks
This is not the answer you are looking for but, here is my 2 cents: You shouldn't put all that stuff in the User model. devise models have a clear responsibility: signing.
But if you really want to put everything hooked in User.rb, you can split the model in extensions (partially enabling DCI):
Add that to your lib/models/{modelname}/devise_ext.rb
module Models
module User
module DeviseExt
extend ActiveSupport::Concern
included do
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :omniauthable
end
module ClassMethods
end
module InstanceMethods
def password_require?
new_record?
end
end #InstanceMethods
end
end
end
Then, you just add it into your model:
include Models::User::DeviseExt
In the app we have in my company we actually have no code at all in models, we put everything in extensions.
I've not used Devise yet, but try this:
module Loginable
def self.included(base)
base.send :devise, :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :omniauthable
end
def password_require?
new_record?
end
end
and in your model, include rather than extend:
class User < ActiveRecord::Base
include Loginable
end
This is an old question, but answers here didn't help me with Rails 4.2.
The problem is that when you define instance methods inside a module and include it into User model, they actually get defined on that instance.
But they don't override same methods in devise itself (like email_required?) defined inside devise method. So when you define email_required? on User model itself it works fine, but in included module they don't override devise's method.
But with Ruby 2 you can do this with prepend. Like this:
module User::AuthHelper
extend ActiveSupport::Concern
included do
prepend DeviseInstanceMethods
devise :database_authenticatable, :async, :registerable, :recoverable, :rememberable, :trackable, :validatable, :confirmable, \
:omniauthable, omniauth_providers: [:facebook, :instagram], authentication_keys: [:username]
end
module DeviseInstanceMethods
def email_changed?
false
end
def email_required?
false
end
end
end
Now all methods in DeviseInstanceMethods will override devise's methods.
I don't know whether this is best solution, but it works for me. Hope it helps.
Related
I am new to stackoverflow and i want to implement user with multiple roles .
I had started with rolify gem . I had generated 3 devise users manager , owner , user (visitor).
Association used for my application is
class Role < ApplicationRecord
has_and_belongs_to_many :users, :join_table => :users_roles
belongs_to :resource,
:polymorphic => true,
:optional => true
validates :resource_type,
:inclusion => { :in => Rolify.resource_types },
:allow_nil => true
scopify
end
class User < ApplicationRecord
rolify
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
after_create :assign_default_role
def assign_default_role
self.add_role(:visitor) if self.roles.blank?
end
end
class Owner < User
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
after_create :assign_default_role
def assign_default_role
self.add_role(:owner) if self.roles.blank?
end
end
class Manager < User
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
after_create :assign_default_role
def assign_default_role
self.add_role(:moderator) if self.roles.blank?
end
end
My concern is i am using rolify gem to assign role but i want to keep manager , owner , visitor table separate but if i didn't use Single table inheritance then how can i able to implement roles and keep table separate
I work on project (ruby '2.2.0', rails '4.2.3') which use both standard devise user management (for web page) and devise_token_auth (for API part of the service). Everything works fine unless I
include DeviseTokenAuth::Concerns::User
in the models/user.rb. Then sending confirmation emails after user registration does not occur.
I would be grateful for the solution of this problem.
My models/user.rb:
class User < ActiveRecord::Base
# Include devise modules.
devise :invitable, :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable, :omniauthable
include DeviseTokenAuth::Concerns::User
enum role: [:user, :vip, :admin]
after_initialize :set_default_role, :if => :new_record?
def set_default_role
self.role ||= :user
end
end
routes.rb:
Rails.application.routes.draw do
# standard devise routes available at /users
# NOTE: make sure this comes first!!!
devise_for :users
# token auth routes available at /api/v1/auth
namespace :api do
scope :v1 do
mount_devise_token_auth_for 'User', at: 'auth'
end
end
end
I got the same problem to you. This workaround helped me
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable
include DeviseTokenAuth::Concerns::User
after_create :send_confirmation_email, if: -> { !Rails.env.test? && User.devise_modules.include?(:confirmable) }
private
def send_confirmation_email
self.send_confirmation_instructions
end
end
I have a Devise User model with the following contents for which I did run migration.
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :token_authenticatable, :confirmable, :lockable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :role
# attr_accessible :title, :body
ROLES = ['admin', 'network/system admin', 'manager', 'programmer']
def role?(base_role)
ROLES.index(base_role.to_s) <= ROLES.index(role)
end
end
later on , I added the below two lines to the same model and run migration for Ticket, Projects and Assignments.
has_many :projects, :through => :assignments
has_many :tickets
Does the above update the association of user with the Tickets and Projects? Is there any problem in changing associations in model after running migration for the same? I want to know it as I am developing a Rails app now.
Thanks :)-
You should also have association...
has_many :assignments
in your user model.
no other updation is required.
Not sure if the question makes sense so I'll describe through an example:
Basically I have a company model in my app and a company employee. The employee is a devise model and can sign up/sign in. I have a wizard set up for the employee to select the company they work for after signing up, so the model accepts nested attributes for company.
During the stage where they select the company they work for, I want to set up a validation to ensure they only select the company they work for by matching the employee's email domain with the company's email domain in my db. At which point should i do this? Should I set up a custom validator or use a callback?
Here's my code:
Employee:
class Employee < ActiveRecord::Base
##################
# Base
###################
rolify
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable, :omniauthable
# Setup accessible (or protected) attributes for your model
attr_accessible :name, :email, :password, :password_confirmation, :remember_me, :company_id, :company_attributes
##################
# Associations
###################
belongs_to :company
accepts_nested_attributes_for :company
has_many :authentications, dependent: :destroy
end
Company:
class Company < ActiveRecord::Base
##################
# Base
###################
attr_accessible :name, :address_attributes, :email, :phone_number, :website, :confirmed
##################
# Associations
###################
has_one :address
accepts_nested_attributes_for :address
has_many :employees
end
And here is the controller which is responsible for employees selecting a company, it's a wicked gem wizard controller.
class EmployeeStepsController < ApplicationController
before_filter :authenticate_employee!
include Wicked::Wizard
steps :personal, :company_details, :enter_company_details
def show
#employee = current_employee
case step
when :enter_company_details
if #employee.company
skip_step
else
#employee.build_company.build_address
end
end
render_wizard
end
def update
#employee = current_employee
#employee.attributes = params[:employee]
render_wizard #employee
end
private
def finish_wizard_path
employee_url(#employee)
end
end
I have another controller which deals with adding companies into the site separately for site admins but I only want to trigger the email validation in the wizard controller aka when employees are selecting their company. Any advice on this?
class Employee < ActiveRecord::Base
##################
# Base
###################
rolify
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable, :omniauthable
# Setup accessible (or protected) attributes for your model
attr_accessible :name, :email, :password, :password_confirmation, :remember_me, :company_id, :company_attributes
##################
# Associations
###################
belongs_to :company
accepts_nested_attributes_for :company
has_many :authentications, dependent: :destroy
# HERE
validate :my_custom_vaildator
private
def my_custom_vaildator
# do stuff .....
# based off that stuff add errors
if some_logic_about_your_company?
self.errors.add(:base, "select the company you work for.")
elsif some_other_logic?
self.errors.add(:name, "your name sucks.")
end
end
end
REMEMBER:
never return nil or false. If you do it will go BANG!
=)
all this said the front end should never allow them to select something that isn't allowed.
following code is generated by devise.
How can we just use "devise" here to include the modules in my User class.
How is the devise recognized here ?
I don't see any include statement here. Is it included in the ActiveRecord ?
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :name, :email, :password, :password_confirmation, :remember_me
end
When you load Devise (probably via a Rails initializer), it extends the ActiveRecord::Base class with Devise::Model
See it in the devise source code.
ActiveRecord::Base.extend Devise::Models