Rails object.valid? with arguments - ruby-on-rails

Is it possible to pass :symbols to the valid? method so that I can define if the object is valid up to a certain point?
Eg. if I have an object Person and want to call Person.valid?(:basic_info) and it will return true only if a certain subset of fields (say name & gender) are present?
I saw something that I thought might be of use but cannot get it working, it's conditional validations http://guides.rubyonrails.org/active_record_validations_callbacks.html#conditional-validation , in particular grouping conditional validations, but I couldn't get it working...
Can anyone help me out here please...

I don't think there already present like this however you can write a method on your own like following
def is_valid_field?(field)
self.valid?
self.errors[field].blank?
end
and then just person.is_valid_field?(:basic_info)

To validate basic_info you'll have to define a custom validator:
class Person < ActiveRecord::Base
validate :basic_info_present
def basic_info_present
if name.blank? || gender.blank?
errors.add(:basic_info, "can't be in blank")
end
end
end
If you then want to see if there are errors on the specific field, you can use #Salil's approach.
Note however that since there is no actual attribute called basic_info in your model, the validation errors here will not come up in forms, etc. (although they will be in the errors hash). That may or may not be what you want.

I got this to work using conditional validations, so now i can use .valid?(:basic) say for when i only want to check that the person has a name with the call...
validates_presence_of :name, :when => [:basic]
Documentation here: http://apidock.com/rails/ActiveRecord/Validations/valid%3F
This way I can have the object return true when calling .valid? even when it doesn't have a name, good times...

Related

Can you make an optional model attribute required for a form?

Is it possible to make a attribute required just for a particular form?
The field is nullable in the model, and there is no validation setup for it currently and I want to keep it that way.
But on 1 form, I would like to make the field required.
Is this possible without creating a separate model for this?
You could use ActionControllers require for the used parameters, something like this:
def person_params
params.require(:person).permit(:name).tap do |person_params|
person_params.require(:name) # SAFER
end
end
http://api.rubyonrails.org/classes/ActionController/Parameters.html#method-i-require
Does that help you?
I know that you doesn't want to touch the model, but you can do a conditional validation like this that isn't invasive
validates_presence_of :your_attribute, :if => :from_specific_form?
and create some methods that work with this
private
def from_form
#from_specific_form = true
end
def from_specific_form?
#from_specific_form
end
Then when you want the validation to work, just do something like this
xyz = YourModel.new
xyz.from_form

How can I use a custom predicate to validate multiple fields with dry-validation?

I have an address form that I want to validate as a whole rather than validating each input on its own. I can only tell if the address is valid by passing the line1, city, state, zip to the custom predicate method so that it can check them as a unit.
How can I do this? I only see how to validate individual fields.
It seems that this "High-level Rules" example could help you :
schema = Dry::Validation.Schema do
required(:barcode).maybe(:str?)
required(:job_number).maybe(:int?)
required(:sample_number).maybe(:int?)
rule(barcode_only: [:barcode, :job_number, :sample_number]) do |barcode, job_num, sample_num|
barcode.filled? > (job_num.none? & sample_num.none?)
end
end
barcode_only checks 3 attributes at a time.
So your code could be :
rule(valid_address: [:line1, :city, :state, :zip]) do |line, city, state, zip|
# some boolean logic based on line, city, state and zip
end
Update—this is for ActiveRecords rather than dry-validation gem.
See this tutorial, http://guides.rubyonrails.org/active_record_validations.html
Quoting from the tutorial,
You can also create methods that verify the state of your models and add messages to the errors collection when they are invalid. You must then register these methods by using the validate (API) class method, passing in the symbols for the validation methods' names.
class Invoice < ApplicationRecord
validate :discount_cannot_be_greater_than_total_value
def discount_cannot_be_greater_than_total_value
if discount > total_value
errors.add(:discount, "can't be greater than total value")
end
end
end

Best way to ensure a Model attribute is consistently downcased and stripped?

I'd like to know what the best way to ensure a user supplied parameter is downcased and stripped in all situations.
I would like to achieve the following:
Guarantee that the attribute will not be saved to the DB unless stripped/downcased
Queries against the db should always downcase/strip the attribute
Validations are run against a downcased/stripped version of user supplied params
Models return downcase/stripped attribute (which shouldn't be a problem given item #1)
you need to write a before_save callback method, within which you downcase and strip the attributes set by the user.
For eg:
class User < ActiveRecord::Base
before_save :format_values
def format_values
self.name = self.name.downcase
end
end
EDIT
I had missed your 3rd point about validations. So if you need to also run validations on these values. You'd need to use the before_validation callback instead.
class User < ActiveRecord::Base
before_validation :format_values
def format_values
self.name = self.name.strip.downcase if name
end
end
Updated the answer based on the comment.
No need to get fancy with callbacks (don't use callbacks, anyway). Just override the setter for your attribute.
class MyModel
def some_attribute=(value)
value = value.strip.downcase if value
write_attribute(:some_attribute, value)
end
end
You would do that in a before_validation callback:
# in your model
before_validation :normalize_attribute
private
def normalize_attribute
# change `attribute` to your actual attribute's name
self.attribute = attribute.strip.downcase if attribute
end
Or you could do that with a custom setter:
# change `attribute` to your actual attribute's name
def attribute=(value)
write_attribute(:attribute, value.strip.downcase) if value
end
The first option will sanitize the attribute's value every time the object is saved, even if the value has not changed. This might be helpful if you introduce this sanitize method when records in the database already exist, because this allow to sanitize all existing record with just one line of code in the Rails console: Model.find_each(&:save). The second option will only sanitize values when they are set. This is a bit more performant.
I both cases I suggest to check for if attribute otherwise you might call strip.downcase on nil values what would lead to an exception.

Validation rails one method for two fields

I have on my form two fields:
phone and mobile phone
I would like to use one method to validate two fields with one the same method how to do it??
Any validation can be used for any number of attributes. For example:
validates_presence_of :foo, :bar
If you're using a custom validation method, just make sure it inspects both attributes - something like this:
validate :phone_format
def phone_format
[phone, mobile].each do |attr|
errors.add(attr, "some error message") unless attr =~ /some regex/
end
end
Check out http://guides.rubyonrails.org/active_record_validations_callbacks.html
Write a custom validator

How to set some field in model as readonly when a condition is met?

I have models like this:
class Person
has_many :phones
...
end
class Phone
belongs_to :person
end
I want to forbid changing phones associated to person when some condition is met. Forbidden field is set to disabled in html form. When I added a custom validation to check it, it caused save error even when phone doesn't change. I think it is because a hash with attributes is passed to
#person.update_attributes(params[:person])
and there is some data with phone number (because form include fields for phone). How to update only attributes that changed? Or how to create validation that ignore saves when a field isn't changing? Or maybe I'm doing something wrong?
You might be able to use the
changed # => []
changed? # => true|false
changes # => {}
methods that are provided.
The changed method will return an array of changed attributes which you might be able to do an include?(...) against to build the functionality you are looking for.
Maybe something like
validate :check_for_changes
def check_for_changes
errors.add_to_base("Field is not changed") unless changed.include?("field")
end
def validate
errors.add :phone_number, "can't be updated" if phone_number_changed?
end
-- don't know if this works with associations though
Other way would be to override update_attributes, find values that haven't changed and remove them from params hash and finally call original update_attributes.
Why don't you use before_create, before_save callbacks in model to restrict create/update/save/delete or virtually any such operation. I think hooking up observers to decide whether you want to restrict the create or allow; would be a good approach. Following is a short example.
class Person < ActiveRecord::Base
#These callbacks are run every time a save/create is done.
before_save :ensure_my_condition_is_met
before_create :some_other_condition_check
protected
def some_other_condition_check
#checks here
end
def ensure_my_condition_is_met
# checks here
end
end
More information for callbacks can be obtained here:
http://guides.rubyonrails.org/activerecord_validations_callbacks.html#callbacks-overview
Hope it helps.

Resources