I need a custom validation method that receives all set fields. I need this because I dynamically add multiple fields for a has_many :through relation in a form and I need to make sure there aren't any duplicate values entered.
I know that regular Rails validation methods operate on a single field, but I couldn't find anything to handle all fields.
From the docs, you can do this:
class Person < ActiveRecord::Base
validates_each :name, :surname, :any_other_attributes, do |record, attr, value|
...
# e.g. record.errors.add(attr, 'must start with upper case') if value =~ /\A[a-z]/
end
end
Related
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
I have a Rails 3.2.18 app where I'm trying to do some conditional validation on a model.
In the call model there are two fields :location_id (which is an association to a list of pre-defined locations) and :location_other (which is a text field where someone could type in a string or in this case an address).
What I want to be able to do is use validations when creating a call to where either the :location_id or :location_other is validated to be present.
I've read through the Rails validations guide and am a little confused. Was hoping someone could shed some light on how to do this easily with a conditional.
I believe this is what you're looking for:
class Call < ActiveRecord::Base
validate :location_id_or_other
def location_id_or_other
if location_id.blank? && location_other.blank?
errors.add(:location_other, 'needs to be present if location_id is not present')
end
end
end
location_id_or_other is a custom validation method that checks if location_id and location_other are blank. If they both are, then it adds a validation error. If the presence of location_id and location_other is an exclusive or, i.e. only one of the two can be present, not either, and not both, then you can make the following change to the if block in the method.
if location_id.blank? == location_other.blank?
errors.add(:location_other, "must be present if location_id isn't, but can't be present if location_id is")
end
Alternate Solution
class Call < ActiveRecord::Base
validates :location_id, presence: true, unless: :location_other
validates :location_other, presence: true, unless: :location_id
end
This solution (only) works if the presence of location_id and location_other is an exclusive or.
Check out the Rails Validation Guide for more information.
I have a model (simplified version below - the full one has a lot more fields) on which I want to do a search.
class Media < ActiveRecord::Base
attr_accessible :subject, :title, :ref_code
validates :title, :presence => true
validates :subject, :presence => true
def self.search(terms_hash)
return if terms_hash.blank?
composed_scope = self.scoped
terms_hash.each_pair do |key, value|
if self.respond_to? key and not value.is_blank?
value.split(' ').each do |term|
term = "%#{term}%"
composed_scope = composed_scope.where("#{key} LIKE ?", term)
end
end
composed_scope
end
end
Since the advanced search form is almost identical to the form used to create/update instances of the model, I want to create a method that dynamically looks through the params list of the request and matches form fields to model attributes via the name. So if the search request was,
/search?utf8=✓&ref_code=111&title=test&subject=code&commit=Search
then my controller could just call Media.search(params) and it would return a scope that would search the appropriate fields for the appropriate value(s). As you can see I tried using respond_to? but that is only defined for instances of the model rather than the actual model class and I need something that ignores any params not related to the model (eg. utf8 and commit in this case). If this all works well, I plan to refactor the code so that I can reuse the same method for multiple models.
Is there a class method similar to respond_to?? How would you go about the same task?
I know this is related to get model attribute dynamically in rails 3 but it doesn't really answer what I'm trying to do because I want to do it on the the model rather than an instance of the model.
There is a columns method on ActiveRecord model classes so you can easily get a list of attribute names:
names = Model.columns.map(&:name)
You could remove a few common ones easily enough:
names = Model.columns.map(&:name) - %w[id created_at updated_at]
The accessible_attributes class method might also be of interest, that will tell you what has been given to attr_accessible.
I know ActiveRecord provide some macros like validates_uniqueness_of validates_size_of
to do some validation for the user input. but I'm wondering whether it is possible to provide
some call-back like validation method to be used as a cutomised validation method in the model level. for example,
I want to check the input string only consits of letters from 'a' to 'h', funny? but it happened from time to time.
You can create custom functions with:
validate :custom_function
def custom_function
...
end
You can also use regular expressions to validate strings. For your example I would use:
validates_format_of :attribute, :with => /^[a-h]+$/
The rails guides has a good example of how to create your own custom validators. If you are using Rails 3 you could do it like this:
class Foo < ActiveRecord::Base
validate :from_a_to_h
# Use the name of your attribute in place of :input and input.
def from_a_to_h
errors.add(:input, "must contain only letters from a to h") if input =~ /[i-Z]+/
end
end
In Rails, what is the difference between attr_accessor and attr_accessible? From my understanding, using attr_accessor is used to create getter and setter methods for that variable, so that we can access the variable like Object.variable or Object.variable = some_value.
I read that attr_accessible makes that specific variable accessible to the outside world.
Can someone please tell me whats the difference
attr_accessor is a Ruby method that makes a getter and a setter. attr_accessible is a Rails method that allows you to pass in values to a mass assignment: new(attrs) or update_attributes(attrs).
Here's a mass assignment:
Order.new({ :type => 'Corn', :quantity => 6 })
You can imagine that the order might also have a discount code, say :price_off. If you don't tag :price_off as attr_accessible you stop malicious code from being able to do like so:
Order.new({ :type => 'Corn', :quantity => 6, :price_off => 30 })
Even if your form doesn't have a field for :price_off, if it's in your model it's available by default. This means a crafted POST could still set it. Using attr_accessible white lists those things that can be mass assigned.
Many people on this thread and on google explain very well that attr_accessible specifies a whitelist of attributes that are allowed to be updated in bulk (all the attributes of an object model together at the same time)
This is mainly (and only) to protect your application from "Mass assignment" pirate exploit.
This is explained here on the official Rails doc : Mass Assignment
attr_accessor is a ruby code to (quickly) create setter and getter methods in a Class. That's all.
Now, what is missing as an explanation is that when you create somehow a link between a (Rails) model with a database table, you NEVER, NEVER, NEVER need attr_accessor in your model to create setters and getters in order to be able to modify your table's records.
This is because your model inherits all methods from the ActiveRecord::Base Class, which already defines basic CRUD accessors (Create, Read, Update, Delete) for you.
This is explained on the offical doc here Rails Model and here Overwriting default accessor (scroll down to the chapter "Overwrite default accessor")
Say for instance that: we have a database table called "users" that contains three columns "firstname", "lastname" and "role" :
SQL instructions :
CREATE TABLE users (
firstname string,
lastname string
role string
);
I assumed that you set the option config.active_record.whitelist_attributes = true in your config/environment/production.rb to protect your application from Mass assignment exploit. This is explained here : Mass Assignment
Your Rails model will perfectly work with the Model here below :
class User < ActiveRecord::Base
end
However you will need to update each attribute of user separately in your controller for your form's View to work :
def update
#user = User.find_by_id(params[:id])
#user.firstname = params[:user][:firstname]
#user.lastname = params[:user][:lastname]
if #user.save
# Use of I18 internationalization t method for the flash message
flash[:success] = t('activerecord.successful.messages.updated', :model => User.model_name.human)
end
respond_with(#user)
end
Now to ease your life, you don't want to make a complicated controller for your User model.
So you will use the attr_accessible special method in your Class model :
class User < ActiveRecord::Base
attr_accessible :firstname, :lastname
end
So you can use the "highway" (mass assignment) to update :
def update
#user = User.find_by_id(params[:id])
if #user.update_attributes(params[:user])
# Use of I18 internationlization t method for the flash message
flash[:success] = t('activerecord.successful.messages.updated', :model => User.model_name.human)
end
respond_with(#user)
end
You didn't add the "role" attributes to the attr_accessible list because you don't let your users set their role by themselves (like admin). You do this yourself on another special admin View.
Though your user view doesn't show a "role" field, a pirate could easily send a HTTP POST request that include "role" in the params hash. The missing "role" attribute on the attr_accessible is to protect your application from that.
You can still modify your user.role attribute on its own like below, but not with all attributes together.
#user.role = DEFAULT_ROLE
Why the hell would you use the attr_accessor?
Well, this would be in the case that your user-form shows a field that doesn't exist in your users table as a column.
For instance, say your user view shows a "please-tell-the-admin-that-I'm-in-here" field.
You don't want to store this info in your table. You just want that Rails send you an e-mail warning you that one "crazy" ;-) user has subscribed.
To be able to make use of this info you need to store it temporarily somewhere.
What more easy than recover it in a user.peekaboo attribute ?
So you add this field to your model :
class User < ActiveRecord::Base
attr_accessible :firstname, :lastname
attr_accessor :peekaboo
end
So you will be able to make an educated use of the user.peekaboo attribute somewhere in your controller to send an e-mail or do whatever you want.
ActiveRecord will not save the "peekaboo" attribute in your table when you do a user.save because she don't see any column matching this name in her model.
attr_accessor is a Ruby method that gives you setter and getter methods to an instance variable of the same name. So it is equivalent to
class MyModel
def my_variable
#my_variable
end
def my_variable=(value)
#my_variable = value
end
end
attr_accessible is a Rails method that determines what variables can be set in a mass assignment.
When you submit a form, and you have something like MyModel.new params[:my_model] then you want to have a little bit more control, so that people can't submit things that you don't want them to.
You might do attr_accessible :email so that when someone updates their account, they can change their email address. But you wouldn't do attr_accessible :email, :salary because then a person could set their salary through a form submission. In other words, they could hack their way to a raise.
That kind of information needs to be explicitly handled. Just removing it from the form isn't enough. Someone could go in with firebug and add the element into the form to submit a salary field. They could use the built in curl to submit a new salary to the controller update method, they could create a script that submits a post with that information.
So attr_accessor is about creating methods to store variables, and attr_accessible is about the security of mass assignments.
attr_accessor is ruby code and is used when you do not have a column in your database, but still want to show a field in your forms. The only way to allow this is to attr_accessor :fieldname and you can use this field in your View, or model, if you wanted, but mostly in your View.
Let's consider the following example
class Address
attr_reader :street
attr_writer :street
def initialize
#street = ""
end
end
Here we have used attr_reader (readable attribute) and attr_writer (writable attribute) for accessing purpose. But we can achieve the same functionality using attr_accessor. In short, attr_accessor provides access to both getter and setter methods.
So modified code is as below
class Address
attr_accessor :street
def initialize
#street = ""
end
end
attr_accessible allows you to list all the columns you want to allow Mass Assignment. The opposite of this is attr_protected which means this field I do NOT want anyone to be allowed to Mass Assign to. More than likely it is going to be a field in your database that you don't want anyone monkeying around with. Like a status field, or the like.
In two words:
attr_accessor is getter, setter method.
whereas attr_accessible is to say that particular attribute is accessible or not. that's it.
I wish to add we should use Strong parameter instead of attr_accessible to protect from mass asignment.
Cheers!
A quick & concise difference overview :
attr_accessor is an easy way to create read and write accessors in
your class. It is used when you do not have a column in your database,
but still want to show a field in your forms. This field is a
“virtual attribute” in a Rails model.
virtual attribute – an attribute not corresponding to a column in the database.
attr_accessible is used to identify attributes that are accessible
by your controller methods makes a property available for
mass-assignment.. It will only allow access to the attributes that you
specify, denying the rest.