Rails: validations not enforced, call manually? - ruby-on-rails

I check for
validates :group_id, :presence => true
in my model "project". Yet when I create a project without a group_id, I don't get a validation-error. If I try to do
p = Project(:name => "Test")
p.save
the rails console returns false, with save! it tells me the validation for group_id failed. So the validation is somehow performed on the save method, yet it isn't by default in my app. Do I have to do a manual
if #project.save == true
...
end
or something in my controller?
Thank you,
B

You can check #project.valid? before save.
def signup
if request.post?
#user_data = UserData.new(:login => params[:user], :password => params[:password])
if #user_data.valid?
if #user_data.save
session[:cuser] = UserData.authenticate(#user_data.login, #user_data.password).login
redirect_to(:controller=> 'sync', :action=> 'all')
return
end
else
#error_on_signup = true
end
end

Related

How should I refactor create_unique methods in Rails 3?

I have following ugly create_unique method in few models ex:
def self.create_unique(p)
s = Subscription.find :first, :conditions => ['user_id = ? AND app_id = ?', p[:user_id], p[:app_id]]
Subscription.create(p) if !s
end
And then in controllers #create actions I have
s = Subscription.create_unique({:user_id => current_user.id, :app_id => app.id})
if s
raise Exceptions::NotAuthorized unless current_user == s.user
#app = s.app
s.destroy
flash[:notice] = 'You have been unsubscribed from '+#app.name+'.'
redirect_to '/'
end
did you try dynamic finders ?
find_or_initialize_by_user_id_and_app_id
find_or_create_by_user_id_and_app_id
first_or_initialize...
first_or_create....
check manual http://guides.rubyonrails.org/active_record_querying.html#dynamic-finders
also option is to create validation rule for check unique value
class Subscription < ActiveRecord::Base
validates_uniqueness_of :user_id, :scope => :app_id
end
then
sub = Subscription.new({:user_id => current_user.id, :app_id => app.id})
sub.valid? #false
You can use validates_uniquness_of :app_id,:scope=>:user_id so app id is uniq for respected user_id

validation for models in ruby on rails

I have a model:
class HelloRails < ActiveRecord::Base
attr_accessible :filename, :filevalidate
include In4systemsModelCommon
validates :filename, presence: true
def update
parameters = [self.filename, #current_user]
parameters.map!{|p| ActiveRecord::Base.connection.quote p}
sql = "call stored_procedure(#{parameters.join(',')})"
ActiveRecord::Base.connection.execute(sql)
end
end
In the view I have a text_field called as :filename. When I click the submit button it calls this update method in model to call the stored proc to execute. Now validations are not working.
I dont want to accept nil for filename. How can I do this?
It doesn't validate because you are executing sql directly:
ActiveRecord::Base.connection.execute(sql)
Validations are only run when you use the "normal" ActiveRecord methods like save and update_attributes. To run validations manually you can call valid? on your object.
Model:
def update
return false unless self.valid? # validation failed: return false
parameters = [self.filename, #current_user]
parameters.map!{|p| ActiveRecord::Base.connection.quote p}
sql = "call stored_procedure(#{parameters.join(',')})"
ActiveRecord::Base.connection.execute(sql)
true
end
Then in your controller you have to check wether #object.update returns true or false and display errors in your view if necessary.
Controller:
# This is just an example. I don't know your code.
def update
#object = HelloRails.find(params[:id])
if #object.update
redirect_to somewhere
else
render :action => 'edit'
end
end

Ruby on Rails Change Order of Custom Model Validations

I have a form that has model validations which works properly in my local system however when i check it on live site the order of the model validations order gets changed although the code is same in both.
this is the block of code in model:
def validate
#email validation
if !email.blank?
#errors.add(:email,I18n.t(:ismissing))
#else
if email != email_confirmation
errors.add(:email,I18n.t(:ErrorMessageConfirmEmailNotmatch))
else
if email.length <=200 then
if email.match(/^[^#][\w.-]*#[\w.-]+[.][a-z]{2,4}$/i).nil?
errors.add(:email,I18n.t(:ErrorMessageInvalid))
else
if #new_record==true
if User.find(:all, :conditions => ['lower(email) = ?', email.downcase]).count>0
#errors.add(:email," ID already exists. Provide another Email ID")
errors.add(:email,I18n.t(:ErrorMessageAlreadyExists))
end
else
if #changed_attributes["email"]!=nil
if User.User.find(:all, :conditions => ['lower(email) = ?', email.downcase]).count>0
#errors.add(:email," ID already exists. Provide another Email ID")
errors.add(:email,I18n.t(:ErrorMessageAlreadyExists))
end
end
end
end
else
errors.add(:email, I18n.t(:ErroeMessageMustlessthen,:size=>200))
end
end
else
errors.add(:email,I18n.t(:ismissing))
end
#end : Email validation
if email_confirmation.blank?
errors.add(:email_confirmation,I18n.t(:ismissing))
end
#pasword validation
if #new_record==true
if password.blank?
errors.add(:password,I18n.t(:ismissing))
else
if password_confirmation != password
errors.add(:password,I18n.t(:ErrorMessageConfirmPasswordNotmatch))
end
if !password.nil?
if password.length < 4 || password.length > 50 then
errors.add(:password,I18n.t(:ErroeMessageShouldBetween,:from=>"4",:to=>"50"))
end
errors.add(:password,I18n.t(:ErrorMessageInvalidPassword)) if password.match('^[a-z0-9##*-_]*$').nil?
end
end
end
#end password validation
if #new_record==true
if password_confirmation.blank?
errors.add(:password_confirmation,I18n.t(:ismissing))
end
end
if dob.blank?
errors.add(:dob,I18n.t(:ErrorMessageInvalid))
else
begin
#dt = DateTime.strptime(dob, "%m/%d/%Y").to_date
if dob.year <= 1900 then
errors.add(:dob,I18n.t(:ErrorMessageInvalidYear))
end
if dob>=Date.today then
errors.add(:dob,I18n.t(:ErroeMessageInvalidBirthday))
end
rescue Exception => ex
#errors.add(:dob,'is Invalid (MM/DD/YYYY format)')
errors.add(:dob,I18n.t(:ErroeMessageInvalidBirthday))
end
end
end
and the controller calls the Validate method on registration.An urgent help is required If anybody has any suggestions or ideas .
Thanks in Advance
You can use rails default validations..I did for email and gave you the sample here..
validates :email,
:presence =>{ :message => I18n.t(:ismissing)},
:length => {:allow_blank => true, :maximum => 200, :message => I18n.t(:ErroeMessageMustlessthen,:size=>200)},
:format => {:allow_blank => true, :with => /^[^#][\w.-]*#[\w.-]+[.][a-z]{2,4}$/i, :message => I18n.t(:ErrorMess
:uniqueness => {:allow_blank => true, :message => I18n.t(:ErrorMessageAlreadyExists)},
:confirmation => {:message => I18n.t(:ErrorMessageConfirmEmailNotmatch)}
Likewise you can also do for other fields.
Not sure why these wouldn't be executing in order. Have you logged something to indicate that in production?
Rather than put everything in a big validate method, maybe split into several (probably a better practice in general), then call in the order you want.
eg.
before_save :validate_email, :validate_dob
def validate_email
...
end
def validate_dob
...
end

two forms one model in Ruby, separate validations for each

I have three fields in one form and two fields in another (same as the earlier form, but just missing one field). I want to validate only two fields in the smaller form, but the issue is that it is validating all the three.
I have written the following logic:
**
class User < ActiveRecord::Base
validate :validate_form #for form with 2 fields
private
def validate_form
if :classify_create
self.errors.add(:weight, "need weight") if weight.blank?
self.errors.add(:height, "need height") if height.blank?
end
end
# Validations of attributes (for form with three fields)
validates :weight, :presence => true
validates :height, :presence => true
validates :gender, :presence => true
end
**
and this is my controller action: basically I have written two separate creates:
**# for form with two fields
def classify
#user = User.new
#title = "Classify"
end
def classify_create
#user = User.where("weight = ? and height = ?", params[:weight] ,params[:height])
end
# for form with three fields
def new
#user = User.new
#title = "Train"
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to #user
else
#title = "Train"
render 'new'
end
end**
When I submit to the two field form, it gives me an error for gender too and redirects to the page with form having three fields. How should I go about it?
Any help will be appreciated.
Regards,
Arun
First, I would not use classify as a method name. You may conflict with a core inflector provided by ActiveSupport. Call it classification or something.
Second, your validation is running on if #user.save in the create method.
In classify_create you use User.where(...) which is a finder method. You're pulling a matching record and setting it to #user. This does not run validation, yet you receive validation errors. You are posting to create, not classify_create. Bad routes will cause this.
Let's address conditional validation first. In your User model, create a variable to act as a bypass switch for your gender validation. Then tell your validation to check if this bypass switch is false before running:
User < ActiveRecord::Base
attr_accessor :skip_gender # defaults to nil (false)
# ...
validates :gender, :presence => true, :if => :validate_gender? # validate if...
# ...
private
def validate_gender?
!self.skip_gender # true = run validation, false = skip validation
end
# ...
end
Next, clean up your controller. Write two create methods, one setting the switch, one not. (This isn't DRY):
def new_classification
# for form with two fields
#user = User.new
#title = "Classify"
end
def new
# for form with three fields
#user = User.new
#title = "Train"
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to #user
else
render :action => 'new' # render three-field form
end
end
def create_classification
#user = User.where(:weight => params[:weight], :height => params[:height])
# ... do something with #user ...
#user.skip_gender = true # tell #user to skip gender validation
if #user.save
redirect_to #user
else
render :action => 'new_classification' # render two-field form
end
end
Next, adjust config/routes.rb to specify routes to your custom methods.
resources :users do
member do
get 'new_classification', :to => 'users#new_classification', \
:as => :new_classification_for
post 'create_classification', :to => 'users#create_classification', \
:as => :create_classification_for
end
end
Now change your two-field form view. Specify where your form is submitted to.
<%= form_for #user, :url => create_classification_for_user_url(#user) do |f| %>
That should get you by with what you have...
Your problem is two-fold:
You're trying to use one controller for two distinct actions.
The Rails validation model is somewhat limited and inflexible, there should be separate validation passes for controller methods and models.
The easy solution is to kludge around the limitations with a separate controller:
def create_genderless
# Force the issue to an explicit "unknown" state so that
# "unknown" and "missing" can be differentiated.
params[:user][:gender] = 'U'
# And then punt to the existing `create` method.
create
end
Then a bit more validation in your model for good measure
class User < ActiveRecord::Base
validates :gender, :inclusion => { :in => %w[M F U] }
#...
end
Then update your forms to use UserController#create or UserController#create_genderless as appropriate.

Rails, Alphanumeric validation in the controller

In my app I let users select a username, just like the twitter signup page: https://twitter.com/signup
When the user starts typing a username, I want in real-time to let the user know if the username is available & valid.
The regex I've been using to validate the username is alphanumeric is:
/^[a-z0-9]+[-a-z0-9]*[a-z0-9]+$/i
Given params[:username]
In the controller, how can I validate if the username is alphanumeric or not. Note, I'm not saving the record here just validation. so a model validation wouldn't work.
Ideas? Thanks
You'd still want to use model validations.
Something like this perhaps:
class User
validates :username, :format => { :with => /your regex/ }, :uniqueness => true
end
# then in some controller action or rack app
def test_username
user = User.new(:username => params[:username])
# Call user.valid? to trigger the validations, then test to see if there are
# any on username, which is all you're concerned about here.
#
# If there are errors, they'd be returned so you can use them in the view,
# if not, just return success or something.
#
if !user.valid? && user.errors[:username].any?
render :json => { :success => false, :errors => user.errors[:username] }
else
render :json => { :success => true }
end
end
r = /^[a-z0-9]+[-a-z0-9]*[a-z0-9]+$/i
unless your_string.match(r).nil?
# validation succeeded
end
I think your regex is a little overly verbose. I'd actually try the following regex for the alphanumeric validation:
/\A[A-Z0-9]+\z/i

Resources