ActiveRecord format validation not firing - ruby

I'm trying to apply a format validation to a model, but when I create the model it isn't coming back as invalid. I added a length validation and it works just fine. What am I doing wrong?
require 'rubygems'
require 'active_record'
class User < ActiveRecord::Base
validates :username, :format => { :with => /[A-Za-z]+/, :message => "Only letters a-z are allowed" }
validates :username, :length => { :maximum => 20, :too_long => "%{count} letters is too many"}
end
ActiveRecord::Base.establish_connection( ... )
user = User.create!(:username => 'johnsmith1234', :signupdate => '2010-11-12')
puts user.valid?
The output is always true unless I have a length of over 20 characters, then I get an error on the length. So why doesn't the format validation fire?

/[A-Za-z]/ checks for one or more alphabets in given string. If you want only alphabets you need to specify ^ and $. (i.e) /^[A-Za-Z]$/
validates :username, :format => { :with => /^[A-Za-z]+$/, :message => "Only letters a-z are allowed" }
One more thing use new to create new user. Because create or create! will throw error if your validation fails.
user = User.new(:name => "john123")
if user.valid?
#do something
else
#do something `user.errors.full_messages` will have your validation messages if it has error
end

Related

The provided regular expression is using multiline anchors (^ or $)? [duplicate]

This question already has answers here:
Regular expressions with validations in RoR 4
(5 answers)
Closed 8 years ago.
I'm getting an error:
The provided regular expression is using multiline anchors (^ or $). Did you mean to use \A and \z, or forgot to add the :multiline => true option?
when loading only one page in my Rails application.
It highlights the model it's using saying the error is:
class Associate < Locations::Associate
This is the model:
class Associate < Locations::Associate
# Returns an array of permissions which are valid at the associate level.
#
def self.associate_permissions
ASSOCIATE_PERMISSIONS
end
# Generates an array of permission values that can be used in the new or edit
# template.
#
def permission_list
my_permissions = (permissions || '').split(/,/)
list = []
Associate.associate_permissions.each do |value|
list << {:label => value[0], :value => value[1], :checked => my_permissions.include? (value[1])}
end
list
end
end
The controller:
class AssociatesController < ApplicationController
def index
#associates = Associate.paginate :order => 'code',
:page => params[:page], :per_page => 50
respond_to do |format|
format.html # index.html.erb
format.json { render json: #associates }
end
end
end
Can anyone tell me how to solve this error?
I am assuming that you are getting the error while rendering an index view.
Update the index action with
#associates = Associate.order('code').paginate(:page => params[:page], :per_page => 50)
instead of
#associates = Associate.paginate :order => 'code',
:page => params[:page], :per_page => 50
I figured out my issue. I have a gem that runs most of my models. in the gems model I have:
class Locations::Associate < Locations::Database
require 'digest'
attr_accessible :code, :email, :include_on_reports, :name,
:permissions, :phone, :writer
has_many :associate_branches, :dependent => :destroy
validates :code, :presence => true, :uniqueness => true,
:format => { with: /^[A-Z]{3}\d{4}$/, on: :create }
.....
end
I had to change the regex to:
validates :code, :presence => true, :uniqueness => true,
:format => { with: /\A[A-Z]{3}\d{4}\z/, on: :create }
thanks for all the help.

Is it possible to combine with_options and :if for conditional validations?

I'm trying to use with_options to group my conditional validations together for admin users.
The second validation for username uniqueness ends up overriding the with_options condition.
Is there a better way to do this? Or should I just forget about with_options and write two separate statements?
with_options :if => Proc.new { |user| user.admin? } do |admin|
admin.validates :email, :presence => true
admin.validates :username, :uniqueness => true, :if => Proc.new { |user| user.category == "customized_username" }
end
If you only have these two validations, I don't think it's a bad idea to drop the with_options block and just add the conditions directly to each validation:
admin.validates :email, :presence => true, :if => Proc.new { |user| user.admin? }
admin.validates :username, :uniqueness => true, :if => Proc.new { |user| user.admin? && user.category == "customized_username" }
Another thing you may want to consider is Single table inheritance (STI) instead of using a boolean field. I would recommend this if you find yourself doing user.admin? all over your application. Using STI, you would have regular users and Admin users and each could contain different logic for each class. The only real change you'd need to make is changing your "admin" field to "type" and making it a string:
class User < ActiveRecord::Base
end
class AdminUser < User
validates :email, :presence => true
validates :username, :uniqueness => true, :if => Proc.new { |user| user.category == "customized_username" }
end
You should use two lines, however, it is cleaner to have method than repeat the same logic in Proc.
validates :email, :presence => true, :if => :admin?
validates :username, :uniqueness => true, :if => [:admin?, :custom_user?]
def custom_user?
category == "customized_username"
end
Use unless instead:
with_options :unless => Proc.new { |user| !user.admin? } do |admin|
admin.validates :email, :presence => true
admin.validates :username, :uniqueness => true, :if => Proc.new { |user| user.category == "customized_username" }
end

undefined method `password_hash' error with datamapper

I'm trying to build [Ryan Bates' "authenticaion from scratch"][1] using sinatra but authentication does not work. I get a "undefined method password_hash" error.
Everything else is working. I checked datamapper documentation, especially for lazy loading, but couldn't find anything useful. The code is below. What mistake am I doing?
main.rb (only relevant part)
post "/login" do
user = User.authenticate params[:email], params[:password]
if user
session[:user_id] = user.id
redirect "/"
else
session[:errors] = "No such user or bad password."
redirect "/login"
end
end
user.rb
require 'data_mapper'
require 'dm-validations'
require 'bcrypt'
module Kimsin
DataMapper.setup :default, 'sqlite3:///home/barerd/RProjects/kimsin/users.db'
class User
attr_accessor :password, :confirm_password
include DataMapper::Resource
property :id, Serial
property :email, String, :required => true, :unique => true, :format => :email_address,
:messages => { :presence => "We need your email address.", :is_unique => "We already have that email.", :format => "Doesn't look like an email adress.."}
property :password_salt, String
property :password_hash, String, :length => 80
validates_presence_of :password, :confirm_password, :messages => { :presence => "You have to type a password and confirm it." }
validates_format_of :password, :confirm_password, :with => /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])([\x20-\x7E]){8,40}$/, :messages => { :format => "The password should be 8 to 40 characters long and contain at least one digit, one lowercase and one uppercase letter and one special character." }
before :save, :encrypt_password
def self.authenticate email, password
user = User.all :email.like => email
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
def encrypt_password
if password != nil
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret password, password_salt
end
end
end
DataMapper.finalize
end
Adding a attr_reader :password_hash solved it.

Right shape of validation rules (Rails 3)

I use these rules in my model:
validates_presence_of :email, :message => "E-mail must be filled!"
validates_format_of :email, :with => /\A[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]+\z/, :message => "This is not email address!"
validates_uniqueness_of :email, :message => "This email is already taken!"
validates_confirmation_of :password, :message => "Password must be the same!"
validates_presence_of :password_confirmation, :length => { :minimum => 6, :maximum => 40 }, :message => "Your password must have at least 6 characters!"
If I send an empty form, so as the output will be displayed all of those 5 lines above. What I have to do to display only the 1st and 5th rules?
Here's one solution.
Essentially the advice is to use the :allow_blank option on all validations except the presence validations. This makes Rails skip the validations if the field is blank. That way, you only see the error messages related to missing values when submitting an empty form.

How do I work with checkboxes with DataMapper and Sinatra?

I'm trying to make a simple room management service. The rooms have these properties:
class Room
include DataMapper::Resource
validates_is_unique :number
property :id, Serial
property :number, Integer
property :guest, String
property :status, Enum[ :free, :occupied ], :default => :free
end
Then I create a new room like this
post '/new' do
content_type :json
#room = Room.new :guest => params[:guest],
:number => params[:number],
:status => params[:status]
if #room.save
{ :number => #room.number, :guest => #room.guest, :status => #room.status }.to_json
end
end
through this haml form
%form#new_message{:action => '/new', :method => 'post'}
%p
%input{:type => "text", :id => "number", :name => "number"}
%input{:type => "text", :id => "guest", :name => "guest"}
%input{:type => "checkbox", :id => "status", :name => "status", :value => "occupied"}
%input{:type => "submit", :value => "post"}
When the box is checked the :status is "occupied" but when I leave it unchecked the object won't save. I thought it would work since it is defaulted to "free" but no...
For whatever stupid reason, checkboxes do not get submitted if they are not clicked. This means they are not in the hash that hits your app. When you say :status => params[:status] you are really saying :status => nil. Since you have set a value, it checks that against your enum, and nil is not in your enum, so it fails validations. (based on how you are using this, doesn't it seem like it should be a boolean called either "occupied" or "available" ?)
Anyway, you could either explicitly set it to free, or not set it at all, and let the default take care of it. That is what I opted for when checking it, by moving it into a mass assignment. The code I used is below.
require 'rubygems'
require 'sinatra'
require 'haml'
require 'dm-core'
require 'dm-validations'
require 'dm-types'
require 'dm-migrations'
require 'sqlite3'
configure do
class Room
include DataMapper::Resource
validates_uniqueness_of :number
property :id, Serial
property :number, Integer
property :guest, String
property :status, Enum[ :free, :occupied ], :default => :free
end
set :sessions , true
DataMapper::Logger.new($stdout, :debug)
DataMapper.setup( :default , "sqlite3://#{Dir.pwd}/development.sqlite3" )
DataMapper.finalize
DataMapper.auto_upgrade!
end
get '/' do
#rooms = Room.all
haml :index
end
post '/new' do
p params
#room = Room.new params[:room]
if #room.save
session[:flash] = "room reserved"
redirect '/'
else
session[:flash] = #room.errors.to_a
redirect '/new'
end
end
get '/new' do
haml :new
end
__END__
##layout
!!!
%html
#flash
= session[:flash].inspect
= yield
##new
%form#new_message{:action => '/new', :method => 'post' , :name => 'room' }
%p
%input{:type => "text", :id => "number", :name => "room[number]"}
%input{:type => "text", :id => "guest", :name => "room[guest]"}
%input{:type => "checkbox", :id => "status", :name => "room[status]", :value => "occupied"}
%input{:type => "submit", :value => "post"}
##index
%table
- #rooms.each do |room|
%tr
%td= room.number
%td= room.guest
%td= room.status
View the HTML source of your web-form! There should be a hidden field which sets the unchecked checkboxes to '0' as the default , in case nobody checks them...
see also:
http://railscasts.com/episodes/17-habtm-checkboxes?autoplay=true (towards the end)

Resources