I'm getting the following error when trying to use the :confirmable module of Devise:
NameError (undefined local variable or method `confirmed_at' for #<AdminUser:0x007f27841d0f30>)
My model:
class AdminUser < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable,:recoverable, :rememberable, :trackable, :validatable, :confirmable
after_create { |admin| admin.send_reset_password_instructions }
def password_required?
new_record? ? false : super
end
end
I want to send a confirmation email when AdminUser is created.
It is not enough to merely add the :confirmable option in the model, you'll also need to add the columns required by Devise into your database table.
Assuming you are using a model AdminUser:
class AddConfirmableToDevise < ActiveRecord::Migration
def self.up
add_column :admin_users, :confirmation_token, :string
add_column :admin_users, :confirmed_at, :datetime
add_column :admin_users, :confirmation_sent_at, :datetime
add_column :admin_users, :unconfirmed_email, :string
add_index :admin_users, :confirmation_token, :unique => true
end
def self.down
remove_index :admin_users, :confirmation_token
remove_column :admin_users, :unconfirmed_email
remove_column :admin_users, :confirmation_sent_at
remove_column :admin_users, :confirmed_at
remove_column :admin_users, :confirmation_token
end
end
Simply run the command:
rails g migration add_confirmable_to_devise
db/migrate/YYYYMMDDxxx_add_confirmable_to_devise.rb
class AddConfirmableToDevise < ActiveRecord::Migration
# Note: You can't use change, as User.update_all will fail in the down migration
def up
add_column :users, :confirmation_token, :string
add_column :users, :confirmed_at, :datetime
add_column :users, :confirmation_sent_at, :datetime
add_index :users, :confirmation_token, unique: true
execute("UPDATE users SET confirmed_at = NOW()")
end
def down
remove_columns :users, :confirmation_token, :confirmed_at, :confirmation_sent_at
end
end
For further reference devise
You put :confirmable in your AdminUser model. So you need to have columns for confirmable functions including confirmed_at.
You should have a migration file which Devise generated. In that file, you need to remove comment out for confirmable columns including created_at.
Related
class Advertiser < ActiveRecord::Base
devise :database_authenticatable, :registerable,:recoverable, :rememberable, :trackable, :validatable,:confirmable
acts_as_paranoid
end
I added the Devise gem first without the confirmable option. Then I later added the confirmable option with this migration:
class AddConfirmableToDevise < ActiveRecord::Migration
def up
add_column :advertisers, :confirmation_token, :string
add_column :advertisers, :confirmed_at, :datetime
add_column :advertisers, :confirmation_sent_at, :datetime
add_index :advertisers, :confirmation_token, :unique => true
Advertiser.update_all(:confirmed_at => Time.now)
end
end
When I run migration it gives an error
PG::UndefinedColumn: ERROR: column advertisers.deleted_at does not exist
LINE 1: ...onfirmed_at" = '2015-11-05 06:24:26.513079' WHERE "advertise...
Change the migration file like this:
class AddConfirmableToDevise < ActiveRecord::Migration
def up
add_column :advertisers, :confirmation_token, :string
add_column :advertisers, :confirmed_at, :datetime
add_column :advertisers, :confirmation_sent_at, :datetime
add_index :advertisers, :confirmation_token, unique: true
add_column :advertisers, :deleted_at, :time
execute("UPDATE advertisers SET confirmed_at = NOW()")
end
def down
remove_columns :advertisers, :confirmation_token, :confirmed_at, :confirmation_sent_at
end
end
For further reference see devise
I'm trying to add an authentication system into my app and I've run into this error when I run "rake db:migrate":
NoMethodError: undefined method `attr_accessible' for User (call 'User.connection' to establish a connection):Class
/Users/liferaymac/Desktop/check/app/models/user.rb:8:in `<class:User>'
/Users/liferaymac/Desktop/check/app/models/user.rb:1:in `<top (required)>'
/Users/liferaymac/Desktop/check/config/routes.rb:2:in `block in <top (required)>'
/Users/liferaymac/Desktop/check/config/routes.rb:1:in `<top (required)>'
/Users/liferaymac/Desktop/check/config/environment.rb:5:in `<top (required)>'
Tasks: TOP => db:migrate => environment
(See full trace by running task with --trace)
Here is a view of my user.rb file, which is my model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation
end
And here is my migrate file:
class DeviseCreateUsers < ActiveRecord::Migration
def self.up
create_table(:users) do |t|
t.database_authenticatable :null => false
# t.confirmable
t.recoverable
t.rememberable
t.trackable
# t.lockable :lock_strategy => :failed_attempts, :unlock_strategy => :both
t.timestamps
end
add_index :users, :email, :unique => true
# add_index :users, :confirmation_token, :unique => true
add_index :users, :reset_password_token, :unique => true
# add_index :users, :unlock_token, :unique => true
end
def self.down
drop_table :users
end
end
# migration
create_table(:users) do |t|
t.database_authenticatable :null => false
# t.confirmable
t.recoverable
t.rememberable
t.trackable
# t.lockable :lock_strategy => :failed_attempts, :unlock_strategy => :both
t.timestamps
end
add_index :users, :email, :unique => true
# add_index :users, :confirmation_token, :unique => true
add_index :users, :reset_password_token, :unique => true
# add_index :users, :unlock_token, :unique => true
I'm working from http://railscasts.com/episodes/209-introducing-devise?autoplay=true
I also ran bundle install and installed the devise gem already.
Any insight will be greatly appreciated.
From the Rails Guides:
Rails 4.0 has removed attr_accessible and attr_protected feature in
favor of Strong Parameters. You can use the Protected Attributes gem
for a smooth upgrade path.
Source: http://guides.rubyonrails.org/upgrading_ruby_on_rails.html
You will probably want to keep the attr_accessible method or you will run into errors when creating users with a sign up form. You can bundle the protected_attributes gem to give you access to those methods in your models.
Protected Attributes: https://github.com/rails/protected_attributes
I realized that in Rails 4+ there is no "attr_accessible". I deleted it out of my model and now it works. (Should have expected this from an old video....)
I have followed the steps in https://github.com/plataformatec/devise/wiki/How-To%3a-Add-%3aconfirmable-to-Users however I receive the error: NameError in Devise::RegistrationsController#create undefined local variable or method `confirmed_at' for # when i submit the signup form. How would I go about creating a confirmation email with the devise gem in Ruby on Rails 4? As well as what other requirements are needed? Here is what my code currently looks like:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
has_many :pins
validates :name, presence: true
validates :email, format: { with: /\A([^#\s]+)#((?:[-a-z0-9]+\.)+edu)\z/ }
end
class AddConfirmableToDevise < ActiveRecord::Migration
def change
change_table(:users) do |t|
t.confirmable
end
add_index :users, :confirmation_token, :unique => true
end
def self.up
add_column :users, :confirmation_token, :string
add_column :users, :confirmed_at, :datetime
add_column :users, :confirmation_sent_at, :datetime
# add_column :users, :unconfirmed_email, :string # Only if using reconfirmable
add_index :users, :confirmation_token, :unique => true
# User.reset_column_information # Need for some types of updates, but not for update_all.
# To avoid a short time window between running the migration and updating all existing
# users as confirmed, do the following
User.update_all(:confirmed_at => Time.now)
# All existing user accounts should be able to log in after this.
end
def self.down
remove_columns :users, :confirmation_token, :confirmed_at, :confirmation_sent_at
# remove_columns :users, :unconfirmed_email # Only if using reconfirmable
end
end
Did you make the necessary migrations to add confirmable module to User?
Devise Confirmable Wiki
Edit: I edited your Rails migration to this:
class AddConfirmableToDevise < ActiveRecord::Migration
def up
add_column :users, :confirmation_token, :string
add_column :users, :confirmed_at, :datetime
add_column :users, :confirmation_sent_at, :datetime
# add_column :users, :unconfirmed_email, :string # Only if using reconfirmable
add_index :users, :confirmation_token, :unique => true
# User.reset_column_information # Need for some types of updates, but not for update_all.
# To avoid a short time window between running the migration and updating all existing
# users as confirmed, do the following
User.update_all(:confirmed_at => Time.now)
# All existing user accounts should be able to log in after this.
end
def down
remove_columns :users, :confirmation_token, :confirmed_at, :confirmation_sent_at
# remove_columns :users, :unconfirmed_email # Only if using reconfirmable
end
end
This may not be Devise specific but I'm wondering how to add an additional module to a gem that has already been installed when the initial install didn't include said module? In the case of Devise the migration helper t.confirmable is useful in the initial migration's Self.up method and the whole User table is torn down in the Self.down. My Rails-fu isn't strong enough to uncover what the t.confirmable helper is actually doing...
What happens when the User table already exists and you want to add something like :confirmable or :token_authenticatable? Obviously you can't just create_table(:users) again... so me thinks I want to add_column :users, ... and remove_column :users, ... but how do we go about finding out what needs to happen?
Take a look at Devise::Schema
https://github.com/plataformatec/devise/blob/master/lib/devise/schema.rb
which has this
# Creates confirmation_token, confirmed_at and confirmation_sent_at.
def confirmable
apply_devise_schema :confirmation_token, String
apply_devise_schema :confirmed_at, DateTime
apply_devise_schema :confirmation_sent_at, DateTime
end
and then
https://github.com/plataformatec/devise/blob/master/lib/devise/orm/active_record.rb
def apply_devise_schema(name, type, options={})
column name, type.to_s.downcase.to_sym, options
end
So in your migration just do
add_column :users, :confirmation_token, :string
add_column :users, :confirmed_at, :datetime
add_column :users, :confirmation_sent_at, :datetime
and the opposite for the down..
Your migration:
class DeviseAddConfirmable < ActiveRecord::Migration
def change
change_table(:users) do |t|
t.confirmable
end
add_index :users, :confirmation_token, :unique => true
end
end
A number of Rails 2.3 apps are using Restful Authentication but that plugin seems to have some issues with Rails 3. In upgrading to Rails 3 I have been using Devise. Is there any way to smoothly transition from Restful Authentication to Devise? Has anyone done a migration that shows how to update the User model?
Here is a good guide on migration from restful_authentication to devise
https://github.com/plataformatec/devise/wiki/How-To:-Migrate-from-restful_authentication-to-Devise
Reason for edit: prior link took folks to a blank page.
I updated my application from Restful Authentication to Devise already. Here is my migration:
class AlterUsersForDevise < ActiveRecord::Migration
def self.up
remove_column :users, :name
change_column :users, :email, :string, :default => "", :null => false, :limit => 128
rename_column :users, :crypted_password, :encrypted_password
change_column :users, :encrypted_password, :string, :limit => 128, :default => "", :null => false
rename_column :users, :salt, :password_salt
change_column :users, :password_salt, :string, :default => "", :null => false, :limit => 255
add_column :users, :reset_password_token, :string
change_column :users, :remember_token, :string, :limit => 255
rename_column :users, :remember_token_expires_at, :remember_created_at
add_column :users, :sign_in_count, :integer, :default => 0
add_column :users, :current_sign_in_at, :datetime
add_column :users, :last_sign_in_at, :datetime
add_column :users, :current_sign_in_ip, :string
add_column :users, :last_sign_in_ip, :string
rename_column :users, :activation_code, :confirmation_token
change_column :users, :confirmation_token, :string, :limit => 255
rename_column :users, :activated_at, :confirmed_at
add_column :users, :confirmation_sent_at, :datetime
end
def self.down
add_column :users, :name, :string, :limit => 100, :default => ""
rename_column :users, :encrypted_password, :crypted_password
change_column :users, :crypted_password, :string, :limit => 40
rename_column :users, :password_salt, :salt
change_column :users, :salt, :string, :limit => 40
remove_column :users, :reset_password_token
change_column :users, :remember_token, :string, :limit => 40
rename_column :users, :remember_created_at, :remember_token_expires_at
remove_column :users, :sign_in_count
remove_column :users, :current_sign_in_at
remove_column :users, :last_sign_in_at
remove_column :users, :current_sign_in_ip
remove_column :users, :last_sign_in_ip
rename_column :users, :confirmation_token, :activation_code
change_column :users, :confirmation_token, :string, :limit => 40
rename_column :users, :confirmed_at, :activated_at
remove_column :users, :confirmation_sent_at
end
end
My application isn't live so far. So i use the password encryption from Devise instead the one from Restful Authorization. If you application is already alive, and you have active users you should configure Devise to use SHA1 from Restful Authentication to en- and decrypt passwords. Otherwise all your users must request a new password.
You can configure this in the devise initializer.
Hope that helps...
Here's how to overcome the password problem:
You need to make a custom encryptor like so:
# /config/initializers/devise_encryptor.rb
require "digest/sha1"
module Devise
module Encryptors
class OldRestfulAuthentication < Base
def self.digest(password, stretches, salt, pepper)
Digest::SHA1.hexdigest("--#{salt}--#{password}--")
end
end
end
end
And then choose it in devise.rb like so:
config.encryptor = :old_restful_authentication
That should do it!
I was having problems with the password encryption (but I found the answer, see my other response). The old app used an old version of Restful Authentication. It was handling the password encryption like so:
# before filter
def encrypt_password
return if password.blank?
self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{login}--") if new_record?
self.crypted_password = encrypt(password)
end
# Encrypts some data with the salt.
def self.encrypt(password, salt)
Digest::SHA1.hexdigest("--#{salt}--#{password}--")
end
# Encrypts the password with the user salt
def encrypt(password)
self.class.encrypt(password, salt)
end
If I set Devise's config.encryptor to :restful_authentication_sha1 it doesn't work.
In my case it works (analized authentication.rb and by_password.rb in old gem restful_authentication):
config/initializers/devise.rb add this:
config.encryptor = :restful_authentication
config.stretches = 10 #REST_AUTH_DIGEST_STRETCHES frome Restful Authentication file config/initializers/site_key.rb
config.pepper = 'mashauronilavrechkumyachik' #REST_AUTH_SITE_KEY frome Restful Authentication file config/initializers/site_key.rb
app/models/user.rb add :encryptable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:encryptable, :omniauthable, :authentication_keys => [:login]
config/initializers/devise_encryptor.rb create with this this:
# -*- encoding : utf-8 -*-
require "digest/sha1"
module Devise
module Encryptable
module Encryptors
class RestfulAuthentication < Base
def self.digest(password, stretches, salt, pepper)
digest = pepper
stretches.times do
digest = secure_digest(digest, salt, password, pepper)
end
digest
end
def self.secure_digest(*args)
Digest::SHA1.hexdigest(args.flatten.join('--'))
end
def self.encrypt_password
return if password.blank?
self.password_salt = make_token if new_record?
self.encrypted_password = encrypt(password)
end
def self.make_token
secure_digest(Time.now, (1..10).map{ rand.to_s })
end
def self.encrypt(password)
self.password_digest(password, stretches, salt, pepper)
end
end
end
end
end