Custom validation, on two lines? - ruby-on-rails

Is there a way to validate an input field using just a few lines of code?
Right not I've to do this when using a custom validation.
Model
class Search < ActiveRecord::Base
validates :email, :presence => true, :email => true
end
Validator
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors[attribute] << (options[:message] || "is not an email") unless
value =~ /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
end
end
end
This is what I really want to do.
class Search < ActiveRecord::Base
validates :email, :email => lambda do |record, value|
record.errors[:email] << "invalid field" unless value =~ /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
end
end
Is that possible?

Take a look at ActiveModel's validates_format_of http://api.rubyonrails.org/classes/ActiveModel/Validations/HelperMethods.html#method-i-validates_format_of
class Person < ActiveRecord::Base
validates_format_of :email, :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create
end
Or use the shortcut:
class Person < ActiveRecord::Base
validates :email, :format => { :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create }
end

Here is my solution.
class Search < ActiveRecord::Base
validate :my_custom_validator
def my_custom_validator
if #email.to_s.match(/hello/)
self.errors[:email] << "Email can't contain the word 'hello'"
end
end
end

Related

Using Roles for Validations in Rails

Is it possible to use the roles used for attr_accessible and attr_protected? I'm trying to setup a validation that only executes when not an admin (like this sort of http://launchware.com/articles/whats-new-in-edge-scoped-mass-assignment-in-rails-3-1). For example:
class User < ActiveRecord::Base
def validate(record)
unless # role.admin?
record.errors[:name] << 'Wrong length' if ...
end
end
end
user = User.create({ ... }, role: "admin")
After looking into this and digging through the source code, it appears that the role passed in when creating an Active Record object is exposed through a protected method mass_assignment_role. Thus, the code in question can be re-written as:
class User < ActiveRecord::Base
def validate(record)
unless mass_assignment_role.eql? :admin
record.errors[:name] << 'Wrong length' if ...
end
end
end
user = User.create({ ... }, role: "admin")
Sure can would be something like this:
class User < ActiveRecord::Base
attr_accessible :role
validates :record_validation
def record_validation
unless self.role == "admin"
errors.add(:name, "error message") if ..
end
end
You could do this
class User < ActiveRecord::Base
with_options :if => :is_admin? do |admin|
admin.validates :password, :length => { :minimum => 10 } #sample validations
admin.validates :email, :presence => true #sample validations
end
end
5.4 Grouping conditional validations

Has "def validate" been taken out in Rails 3.1?

Has "def validate" been taken out in Rails 3.1? I'm on Rails 3.1 pre and it doesn't seem to be working
class Category < ActiveRecord::Base
validates_presence_of :title
private
def validate
errors.add(:description, "is too short") if (description.size < 200)
end
end
The "title" validation works but the "description" validation doesn't.
Does something like this work for you?
class Category < ActiveRecord::Base
validates_presence_of :title
validate :description_length
def description_length
errors.add(:description, "is too short") if (description.size < 200)
end
end
class Category < ActiveRecord::Base
validates_presence_of :title
private
validate do
errors.add(:description, "is too short") if (description.size < 200)
end
end
For other types of validations, you can also add 'Validators' like the one listed here:
http://edgeguides.rubyonrails.org/3_0_release_notes.html#validations
class TitleValidator < ActiveModel::EachValidator
Titles = ['Mr.', 'Mrs.', 'Dr.']
def validate_each(record, attribute, value)
unless Titles.include?(value)
record.errors[attribute] << 'must be a valid title'
end
end
end
class Person
include ActiveModel::Validations
attr_accessor :title
validates :title, :presence => true, :title => true
end
# Or for Active Record
class Person < ActiveRecord::Base
validates :title, :presence => true, :title => true
end

undefined local variable or method `hashed_password' for User model

I am trying to set up the User model to successfully save user in the db but I'm hindered by, NameError: undefined local variable or method `hashed_password' for #<User:0x000001029fef18>
User model:
require 'digest'
class User < ActiveRecord::Base
attr_accessor :password
validates :email, :uniqueness => true,
:length => { :within => 5..50 },
:format => { :with => /^[^#][\w.-]+#[\w.-]+[.][a-z]{2,4}$/i }
validates :password, :confirmation => true,
:length => { :within => 4..20 },
:presence => true,
:if => :password_required?
has_one :profile
has_many :articles, :order => 'published_at DESC, title ASC',
:dependent => :nullify
has_many :replies, :through => :articles, :source => :comments
before_save :encrypt_new_password
def self.authenticate(email, password)
user = find_by_email(email)
return user if user && user.authenticated?(password)
end
def authenticated?(password)
self.hashed_password == encrypt(password)
end
protected
def encrypt_new_password
return if password.blank?
self.hashed_password = encrypt(password)
end
def password_required?
hashed_password.blank? || password.present?
end
def encrypt(string)
Digest::SHA1.hexdigest(string)
end
end
Add the hashed_password field to your users table by using a migration. It's currently missing.
My first bet is that hashed_password is not defined as a column in your model. Might want to check your migration file for this specific model
Add
attr_accessor :password, :password_confirmation
to Users.rb

Rails - Submitting Nested Values That Already Exist

I have a model, Tran that has a foreign key to the User model. In the view for creation a Tran (transaction), I have a dropdown that allows the user to select the User that started the transaction. When I post this transaction, the record is set with the correct user ID:
Then, in my Trans model I added "belongs_to", as I understand I should do this for foreign keys:
class Tran < ActiveRecord::Base
belongs_to :buying_user, :class_name => 'User'
Now, when my client passes up the params in the post, my Tran.new craps out because I am passing up a userID and not a full record. Is the
#trans_controller.rb
def create
#title = "Create Transaction"
#bombs on this call
#tran = Tran.new(params[:tran])
How am I supposed to handle this?
Update as requested:
tran.rb
class Tran < ActiveRecord::Base
has_many :transaction_users, :dependent => :destroy, :class_name => 'TransactionUser'
belongs_to :submitting_user, :class_name => 'User'
belongs_to :buying_user, :class_name => 'User'
accepts_nested_attributes_for :transaction_users, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
validates :description, :presence => true,
:length => {:maximum => 100 }
validates :total, :presence => true
validates_numericality_of :total, :greater_than => 0
validates :submitting_user, :presence => true
validates :buying_user, :presence => true
validates_associated :transaction_users
end
user.rb
class User < ActiveRecord::Base
has_many :trans
attr_accessor :password
attr_accessible :firstname, :lastname, :email, :password, :password_confirmation
email_regex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :firstname, :presence => true,
:length => {:maximum => 50 }
validates :lastname, :presence => true,
:length => {:maximum => 50 }
validates :email, :presence => true,
:format => {:with => email_regex },
:uniqueness => { :case_sensitive => false }
validates :password, :presence => true,
:confirmation => true,
:length => { :within => 6..40 }
# Register callback to before save so that we can call extra code like password encryption
before_save :encrypt_password
# Class methods
def self.authenticate(email, submitted_password)
user = find_by_email(email)
return nil if user.nil?
return user if user.has_password?(submitted_password)
end
def self.authenticate_with_salt(id, cookie_salt)
user = find_by_id(id)
(user && user.salt == cookie_salt) ? user : nil
end
# Public methods
def has_password?(submitted_password)
self.encrypted_password == encrypt(submitted_password)
end
def full_name
"#{self.lastname}, #{self.firstname}"
end
def self.active_users
# TODO
#User.find_
User.all
end
private
def encrypt_password
self.salt = make_salt if new_record?
self.encrypted_password = encrypt(password)
end
def encrypt (string)
secure_hash("#{salt}--#{string}")
end
def make_salt
secure_hash("#{Time.now.utc}--#{password}")
end
def secure_hash(string)
Digest::SHA2.hexdigest(string)
end
end
params hash on submit:
{"commit"=>"Submit",
"tran"=>{"total"=>"100",
"submitting_user"=>"1",
"description"=>"Description"},
"authenticity_token"=>"88qI+iqF92fo/M9rPfMs1CLpEXqFLGQXfj0c9krXXac=",
"utf8"=>"✓",
"user"=>"1"}
error:
User(#70040336455300) expected, got String(#70040382612480)
beginning of controller:
def create
#title = "Create Transaction"
#tran = Tran.new(params[:tran])
It crashes on the Tran.new line. Thanks so much!
Typically the User model would have has_many :transactions, :class_name => Tran
Then you would do this...
#user.transaction_create(params[:tran])
or
#user.build
it depends on what parameters are actually passed in params[:tran], but the idea is that the has_many side does the creating of the belongs_to.
I figured it out! The problem the whole time was that my db column name on my Trans table that linked to my Users table was submitting_user instead of submitting_user_id. Then, when I added the belongs_to association to submitting_user rails got confused and made that field a User, instead of an integer.

Same custom validation for several fields in Rails

I have four date_time fields in my model in Rails app. I want to apply the same validation method to them, so that only a valid date time could be accepted. Validation method is from earlier question on stack overflow:
validate :datetime_field_is_valid_datetime
def datetime_field_is_valid_datetime
errors.add(:datetime_field, 'must be a valid datetime') if ((DateTime.parse(datetime_field) rescue ArgumentError) == ArgumentError) && !datetime_field.nil? && !datetime_field.blank?
end
Is there more elegant way to validate these fields, other than defining four exactly same methods for every DateTime field?
Best solution is to create your own validator:
class MyModel < ActiveRecord::Base
include ActiveModel::Validations
class DateValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors[attribute] << "must be a valid datetime" unless (DateTime.parse(value) rescue nil)
end
end
validates :datetime_field, :presence => true, :date => true
validates :another_datetime_field, :presence => true, :date => true
validates :third_datetime_field, :presence => true, :date => true
end
UPD
you can share same validations this way:
validates :datetime_field, :another_datetime_field, :third_datetime_field, :presence => true, :date => true
def self.validate_is_valid_datetime(field)
validate do |model|
if model.send("#{field}?") && ((DateTime.parse(model.send(field)) rescue ArgumentError) == ArgumentError)
model.errors.add(field, 'must be a valid datetime')
end
end
end
validate_is_valid_datetime :datetime_field

Resources