Error with `check_validity!' - ruby-on-rails

I'm getting this error when trying to migrate. I've looked into my user.rb but I don't see how validate_format_of is causing an error. Below is my user.rb and error log:
ArgumentError: Either :with or :without must be supplied (but not both)
/Users/admin/.rvm/gems/ruby-2.2.0/gems/activemodel-4.1.6/lib/active_model/validations/format.rb:17:in `check_validity!'
/Users/admin/.rvm/gems/ruby-2.2.0/gems/activemodel-4.1.6/lib/active_model/validator.rb:157:in `initialize'
/Users/admin/.rvm/gems/ruby-2.2.0/gems/activemodel-4.1.6/lib/active_model/validations/with.rb:89:in `new'
/Users/admin/.rvm/gems/ruby-2.2.0/gems/activemodel-4.1.6/lib/active_model/validations/with.rb:89:in `block in validates_with'
/Users/admin/.rvm/gems/ruby-2.2.0/gems/activemodel-4.1.6/lib/active_model/validations/with.rb:88:in `each'
/Users/admin/.rvm/gems/ruby-2.2.0/gems/activemodel-4.1.6/lib/active_model/validations/with.rb:88:in `validates_with'
/Users/admin/.rvm/gems/ruby-2.2.0/gems/activemodel-4.1.6/lib/active_model/validations/format.rb:109:in `validates_format_of'
/Users/admin/Documents/workspace/ruby_on_rails/zoan/app/models/user.rb:19:in `<class:User>'
/Users/admin/Documents/workspace/ruby_on_rails/zoan/app/models/user.rb:1:in `<top (required)>'
Model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
TEMP_EMAIL_PREFIX = 'main#gmail'
TEMP_EMAIL_REGEX = /\Amain#gmail/
attr_accessor :login
self.per_page = 20
extend FriendlyId
friendly_id :username, use: [:slugged, :finders]
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable
validates :name, presence: true, length: { maximum: 100 }
validates :username, presence: true, length: { maximum: 20 }, :uniqueness => { case_sensitive: false }
validates_format_of :email, on: :update
has_many :tweets
has_many :relationships
has_many :friends, through: :relationships
has_many :inverse_relationships, class_name: "Relationship", foreign_key: "friend_id"
has_many :inverse_friends, through: :inverse_relationships, source: :user
has_many :favorites
has_many :votes
has_many :retweets, foreign_key: "retweeter_id"
mount_uploader :avatar, AvatarUploader
mount_uploader :cover, CoverUploader
# def self.find_for_database_authentication(warden_conditions)
# conditions = warden_conditions.dup
# if login = conditions.delete(:login)
# where(conditions).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
# else
# where(conditions).first
# end
# end
def self.find_for_oauth(auth, signed_in_resource = nil)
# Get the identity and user if they exist
identity = Identity.find_for_oauth(auth)
# If a signed_in_resource is provided it always overrides the existing user
# to prevent the identity being locked with accidentally created accounts.
# Note that this may leave zombie accounts (with no associated identity) which
# can be cleaned up at a later date.
user = signed_in_resource ? signed_in_resource : identity.user
# Create the user if needed
if user.nil?
# Get the existing user by email if the provider gives us a verified email.
# If no verified email was provided we assign a temporary email and ask the
# user to verify it on the next step via UsersController.finish_signup
email_is_verified = auth.info.email && (auth.info.verified || auth.info.verified_email)
email = auth.info.email if email_is_verified
user = User.where(:email => email).first if email
# Create the user if it's a new registration
if user.nil?
user = User.new(
name: auth.extra.raw_info.name,
#username: auth.info.nickname || auth.uid,
email: email ? email : "#{TEMP_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com",
password: Devise.friendly_token[0,20]
)
user.skip_confirmation!
user.save!
end
end
# Associate the identity with the user if needed
if identity.user != user
identity.user = user
identity.save!
end
user
end
def email_verified?
self.email && self.email !~ TEMP_EMAIL_REGEX
end
end

Check out the example on the docs: http://apidock.com/rails/ActiveModel/Validations/ClassMethods/validates_format_of
Basically you need to provide validates_format_of with a regular expression so it has something to compare your string with.
You can provide the regular expression by using the :with option.
In your example, to validate for an email upon updating, do this:
validates_format_of :email, :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :update
That regular expression takes care of most email formats: http://rubular.com/r/YEPtKO3j5L

validates_format_of :email, on: :update
You have to specify how to check email. Use "with"

Related

Device cannot login

I have a simple user model
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable,:confirmable, :validatable
include BCrypt
#attr_accessor :password, :password_confirmation
has_many :deals
has_many :charges
has_one :menu
has_many :vouchers
has_one :authentication
has_many :restaurant_tags
has_many :restaurant_hours
#validates :login, uniqueness: true
#validates :login, presence: true
validates :password, presence: { on: :create }
validates :password, confirmation: true
#validates :login, format: { with: /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create, message: "must be a valid email address."}
validates :role, inclusion: { in: ["admin", "client", "restaurant"] }
#before_save :encrypt_password
def encrypt_password
if password.present?
self.salt = BCrypt::Engine.generate_salt
self.crypted_password = BCrypt::Engine.hash_secret(password, salt)
end
end
def self.authenticate(login, password)
user = find_by_login(login)
if user && user.crypted_password == BCrypt::Engine.hash_secret(password, user.salt)
user
else
nil
end
end
def restaurant?
role == "restaurant"
end
def client?
role == "client"
end
def admin?
role == "admin"
end
end
and devise.rb
Devise.setup do |config|
config.mailer_sender = "please-change-me-at-config-initializers-devise#example.com"
require 'devise/orm/active_record'
config.authentication_keys = [ :email, :login]
config.case_insensitive_keys = [ :email, login]
config.strip_whitespace_keys = [ :email, login ]
config.skip_session_storage = [:http_auth]
config.stretches = Rails.env.test? ? 1 : 10
config.reconfirmable = true
config.password_length = 6..128
config.reset_password_within = 6.hours
config.sign_out_via = :delete
end
I can sign up successfully and after confirmation I can see I am logged in but when I sign out and then try to login I am face error Completed 401 Unauthorized in 2ms. I have already spent whole day on this please help me out Thanks
I have solved the problem the devise was checking the login field for authenticate while I was using email in my views i just updated
config.authentication_keys = [:email]
config.case_insensitive_keys = [:email]
config.strip_whitespace_keys = [:email]
and this solve my problem thanks for helping
please update this method
def self.authenticate(login, password)
user = self.find_by_login(login)
if user && user.crypted_password == BCrypt::Engine.hash_secret(password, user.salt)
user
else
nil
end
end

Creating an empty profile on devise registration

I have used devise for authentication in a rails app and I want a user to be able to sign up and once signed up be able to edit their own profile. I've followed along with other answers on stack but when I try and register using the default devise registration form I'm getting this error.
NoMethodError in Devise::RegistrationsController#create
undefined method `create' for nil:NilClass
app/models/user.rb:17:in `create_profile'
My User.rb is as follows
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :profile
attr_accessible :login, :username, :email, :password, :password_confirmation, :remember_me, :profile_attributes
attr_accessor :login
accepts_nested_attributes_for :profile
validates :username, :uniqueness => { :case_sensitive => false }, :presence => true
after_create :create_profile
def create_profile
self.profile.create
end
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions).where(["username = :value OR lower(email) = lower(:value)", { :value => login }]).first
else
where(conditions).first
end
end
end
Profile.rb is
class Profile < ActiveRecord::Base
attr_accessible :fax, :phone_1, :phone_2, :url
belongs_to :user
end
profiles controller is the norm apart from the edit action
def edit
#profile = current_user.profile
end
You need to use self.create_profile instead of self.profile.create for has_one association.
Example
An Account class declares has_one :beneficiary, which will add:
Account#beneficiary (similar to Beneficiary.where(account_id: id).first)
Account#beneficiary=(beneficiary) (similar to beneficiary.account_id = account.id; beneficiary.save)
Account#build_beneficiary (similar to Beneficiary.new("account_id" => id))
Account#create_beneficiary (similar to b = Beneficiary.new("account_id" => id); b.save; b)
Account#create_beneficiary! (similar to b = Beneficiary.new("account_id" => id); b.save!; b)
More docs here

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.

Admin role isn't being assigned in my seed data when I have roles in my model?

In my seed file I am trying to create 3 users, 1 admin and 2 default users but it keeps assigning all 3 users to the default role before creation. Here is my code:
User.rb
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :remember_me, :username
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable
has_many :user_prices
has_many :products, :through => :user_prices
validates_presence_of :username, :email, :password, :password_confirmation
validates_format_of :username, :with => /\A[a-z0-9]{5,20}\z/i
validates_uniqueness_of :username, :email
before_create :setup_default_role_for_new_users
ROLES = %w[admin default]
private
def setup_default_role_for_new_users
if self.role.blank?
self.role = "default"
end
end
end
Seed.rb
puts 'Loading seed data now....'
user1 = User.create(:email => 'admin#email.com', :role => 'admin')
user2 = User.create(:email => 'user1#email.com')
user3 = User.create(:email => 'user2#email.com')
puts 'Users added'
I know user2 and user3 will have the default role but user1 should be admin. How is this done?
since :role isnt in your accessible attributes, its protected from mass assignment, which is what you are doing in your seed file.
so in order to set role, you can use something like this
user1 = User.create(:email => 'admin#email.com')
user1.update_attribute(:role, 'admin')
Use if not unless:
def setup_default_role_for_new_users
if self.role.blank? # if not unless
self.role = "default"
end
end

Devise - uninitialized constant User::PasswordHistory

I have a rails 3 application that I am working on and have implemented devise. I have it working, and now I wish to extend it so that a user is unable to use a old password more than once. Found this functionality on github which to my suprise was good. Disallow previously passwords - Git Hub
I thought this would straight forward but it is clearly not. My code looks like the following:
create_passwrod_histories.rb
class CreatePasswordHistories < ActiveRecord::Migration
def self.up
create_table(:password_histories) do |t|
t.integer :user_id
t.string :encrypted_password
t.timestamps
end
end
def self.down
drop_table :password_histories
end
end
User.rb
class User < ActiveRecord::Base
include ActiveModel::Validations
has_many :roles_users
has_many :roles, :through => :roles_users
has_many :projects
has_many :password_histories
after_save :store_digest
# authorization include this in whichever model that will use ACL9
acts_as_authorization_subject
def has_role?(role_name, object=nil)
!! if object.nil?
self.roles.find_by_name(role_name.to_s) ||
self.roles.member?(get_role(role_name, nil))
else
method = "is_#{role_name.to_s}?".to_sym
object.respond_to?(method) && object.send(method, self)
end
end
def login(user)
post_via_redirect user_session_path, 'user[username]' => user.username, 'user[password]' => user.password
end
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable #:registerable,
devise :database_authenticatable, :recoverable, :rememberable, :trackable, :validatable, :timeoutable
acts_as_authorization_subject :association_name => :roles
attr_accessor :login
# Setup accessible (or protected) attributes for your model
attr_accessible :id, :login, :username, :full_name, :email, :password, :password_confirmation, :remember_me, :role_ids
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates_presence_of :username, :full_name
validates_format_of :username, :with => /^[-\w\._#]+$/i, :allow_blank => true, :message => "should only contain letters, numbers, or . - _ #"
validates_length_of :username, :minimum => 1, :allow_blank => true
validates_uniqueness_of :username, :email
validates :email, :presence => true,
:format => { :with => email_regex }
validates :password, :unique_password => true
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.downcase }]).first
end
private
def store_digest
if encrypted_password_changed?
PasswordHistory.create(:user => self, :encrypted_password => encrypted_password)
end
end
end
unique_password_validator.rb
require 'bcrypt'
class UniquePasswordValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.password_histories.each do |ph|
bcrypt = ::BCrypt::Password.new(ph.encrypted_password)
hashed_value = ::BCrypt::Engine.hash_secret([value, Devise.pepper].join, bcrypt.salt)
record.errors[attribute] << "has been used previously." and return if hashed_value == ph.encrypted_password
end
end
end
I then run my app and try to use the same password. It then throws up the follwoing error uninitialized constant User::PasswordHistory
The only way that I can see from your code why that would be happening is if you didn't have the PasswordHistory model object. That code from Github doesn't actually explicitly tell you to do it, but you certainly need it. So, maybe you created and ran the migration but forgot to create the model, as in:
class PasswordHistory < ActiveRecord::Base
...
end

Resources