Retrieve validating field names - ruby-on-rails

I am using Ruby on Rails 3 and I would like to retrieve validating field names. That is, I defined some validation for a class and I would like to retrieve what fields (their names) are candidate for validation on submitting a form.
I need that because I would like to "play" with class error attributes (<name_class>.errors).
How can I do?

You can access your model's validators method. This will return an array of validators on your model.
For example, if you had this:
class User < ActiveRecord::Base
validates :name, :presence => true
validates :email, :uniqueness => true
end
Then you could access the validators like this:
User.validators
# => [#<ActiveModel::Validations::PresenceValidator:0x123456 #attributes=[:name], #options={}>....]
User.validators.first.attributes
# => [:name]
User.validators.first.class
# => ActiveModel::Validations::PresenceValidator

Related

Rails - one model, 2 kinds of validation rules

In an app I have 3 types of contact forms - in the model - the attributes :aaa, :bbb, :ccc belongs to the second contact form, the previous attributes belongs to the first contact form.
class Message
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :name, :email, :body, :aaa, :bbb, :ccc
validates :name, :email, :body, :aaa, :bbb, :ccc, :presence => true
validates :email, :format => { :with => %r{.+#.+\..+} }, :allow_blank => true
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
end
What I am trying to do: I am looking for a way, how to validate attributes for the respective contact forms, specifically:
the first contact form contains attributes: :name, :email, :body, which I need to validate
the second contract form contains attributes: :aaa, :bbb, :ccc, :email, which I need to validate
How to do that? How to distinguish, which attributes belongs to which form and validate them?
If there are 3 types of contract forms why not make them 3 separate classes?
If for some erason you still want to keep it in one class, you can do it using 'with_options' magic:
with_options :if => :is_form_1? do |p|
p.validates_presence_of :attr1
p.validates_presence_of :attr2
p.validates_presence_of :attr3
end
with_options :if => :is_form_2? do |p|
p.validates_presence_of :attr4
p.validates_presence_of :attr5
p.validates_presence_of :attr6
end
def is_form_1?
#some logic
end
def is_form_2?
#some logic
end
Still, I don't like the idea of keeping it in one class.
I'd suggest you think about this in behavioural rather than implementation terms. You mention there are three contact forms, but what is the underlying use that you're putting each one to? You shouldn't be thinking about forms when you're setting up your model.
That having been said, you can achieve what you want using the validation_scopes gem. Using validation_scopes, you can define sets of validation rules that you can treat independently. In your controllers, you can then check whichever set of validation rules apply to the context (i.e. which form the user has filled in).
In your model you can set up validation scopes named for each form (or better, named for the context in a way that has semantic value, but I don't know enough about your app to know what the contexts are), like this:
validation_scope :form_one_errors do |vs|
validates :name, :body, :presence => true
end
validation_scope :form_two_errors do |vs|
validates :aaa, :bbb, :ccc, :presence => true
end
Since email needs to be validated in both contexts, you can just set it up as a normal validation (as per your code in the question).
Then in the controller for, say, form one, you can check the scope to see if there are any errors for that context. Note that you have to check the errors for the validation scope separately for the regular validation errors.
if !message.valid?
# Do something with message.errors
elsif message.has_form_one_errors?
# Do something with message.form_one_errors
else
# All good
end

Rails 3: Mongoid validation issue

Using Mongoid, I am trying to validate the :code input on the submission form to make sure they are using a proper code that is already stored in the database. There are about 2000+ codes so a helper method array collection wouldn't be feasable.
What is the best way to do this?
I was thinking of doing an inclusion validation, like this:
class Request
include Mongoid::Document
field :code, type: String
validates :code, :presence => true,
:inclusion => { :in => proc { Listing.all_codes } }
end
Then the model that has all the stored codes, like this:
class Listing
include Mongoid::Document
field :code, type: String
def self.all_codes
where(:code => exists?) # <--- this is broken
end
end
But I can't seem to get this to function the way I would like. Any feedback would be much appreciated.
Your Request model looks fine. But the Listing.all_codes needs to return an array of only codes. so:
class Listing
include Mongoid::Document
field :code, type: String
def self.all_codes
only(:code).map(&:code)
end
end

Form validation - if something is false, require this validation

In my form validation of my model, I'm trying to say that if the params of a column called :virtual is false, then the :location field should validate for :presence => true.
My current code is:
validates :location, if :virtual => false, :presence => true
But that's giving me a syntax error. What's the correct way to format this?
Something like:
attr_accessor :virtual # sets up a "virtual attribute" called "virtual" to which you can read/write a value
# this step isn't necessary if you already have an attribute on the model called "virtual"
validates :location, :presence => true, :unless => :virtual?
The use of virtual? should check whether the attribute virtual is true or false. Using unless means this validation is only performed if virtual is false (or is a value that is considered false).
More detail on virtual attributes and validation: Rails: Using form fields that are unassociated with a model in validations
validates :location, presence: true, if: Proc.new { |p| p.virtual == false }

Rails Model Validation to check the value of an attribute

I have a Project model and a User model. A project must have a client (User class) and so the Project model has a client_id foreign key.
The User model has a type attribute and will contain 3 if the user is a client.
I want to validate that when a project is assigned to a client, that #user.type is 3.
Project.rb
validates :client_id, presence: true, ##user.type must be 3
belongs_to :client, :class_name => User, :foreign_key => :client_id
User.rb
#constants
TYPES = {
:manager => 1,
:contractor => 2,
:client => 3
}
Not to sure how to go about the validation. I read through the rails guide on validations but still can't seem to get a solution. Any ideas?
Use the inclusion validation helper. Docs here
Here's a quick example from the docs
class Coffee < ActiveRecord::Base
validates :size, :inclusion => { :in => %w(small medium large),
:message => "%{value} is not a valid size" }
end
EDIT:
Ok, I see what you mean. Don't use validation helpers for this, do it manually.
# somewhere in your model (don't be tempted to put this in your controller)
def assigning_client
if #user.type == 3
# do the assignment
else
errors.add(:base, :message => "User must be a client")
end
end
The error will prevent the info from being saved as long as you use the bang version save! which forces validation.
Just a pointer here. Don't use an attribute named type in your activerecord models. It conflicts with the way rails uses STI(Single Table Inheritance) as it uses the type attribute to determine the type of the class when its subclassing another

Dynamic form validation in Rails

I have a form which I want to validate. The validation is based on properties in a couple of other model objects, but the form itself does not correspond to a ActiveRecord model.
Would it be possible to use ActiveModel to achieve this?
class Person < ActiveModel
has_one :shoe
validates :name, :length => { :maximum => self.shoe.size }
end
I basically want to validate a form based on the properties of another model object. Is this possible in anyway?
class Person
include ActiveModel::Validations
# has_one :shoe # This won't work
validates :validates_name_length
private
def validates_name_length
errors.add :name, 'too long' if name && name.length > shoe.size
end
end

Resources