This is a two part question.
Part one if my Model that i am writing the validations for is inhering from ActiveRecord::Base do i need to include ActiveModel::Validations within that class?? the API for rails doesnt say but here in yehudakatz blog seems to hint that?
part two is where would be the best place to put these validator files? under helpers or as a new model or in lib?
my current validator looks like this
class GenderValidator < ActiveModel::validator
def validate(record)
cred = /(\d{6})(\d{4})(\d{1})(\d{2})/.match(record.id_number.to_s) #breaks up the id into the relevent sections namely birthdate, gender, nationality, validator.
unless cred[0][/\d{13}/] #checks to see if the id is a valid length of numbers if it isnt then skip the validation of gender
return true
else
birthdate = cred[1] #returns a 6 digit string 'yymmdd'
parsed_gender = cred[2] #returns a 4 digit string '0001-4999:female 5000-9999:male'
nationality = cred[3] # should return either a 1 or a 0 1 if the person is foreign or 0 if the person is southafrican
validate_gender(parsed_gender, record)
end
end
private
def validate_gender(parsed_gender, record)
calculate_gender = (parsed_gender <= 4999 ? :female : :male)
unless employee.gender == calculate_gender
employee.errors[:gender] << "Your id indicates you have entered the wrong gender"
end
end
end
an valid id number of each person is optional, but if they do specify it it should check to see if the gender is correct.
if i keep it in the same model the employees model then i get this error
ActionController::RoutingError (uninitialized constant Employee::GenderValidator):
app/models/employee.rb:25:in `<class:Employee>'
app/models/employee.rb:1:in `<top (required)>'
lib/role_requirement_system.rb:19:in `inherited'
app/controllers/employees_controller.rb:1:in `<top (required)>'
librato-rails (0.8.1) lib/librato/rack/middleware.rb:12:in `call'
so i take it they cant be in the same file. what is the best practice for validations? i watched all the rails casts and i have read a few blogs and i am quite new still.
EDIT
in my model i include this class like
include ActiveModel::Validations
and my validations look like this
validates_presence_of :name, :position, :gender
validate :instance_validations, :on => :create
def instance_validations
validates_with GenderValidator
end
just incase you wanted to see that too
thanks ahead!
You do not need include ActiveModel::Validations
My preference to to keep the validation objects in the model folder.
So for model Gender you have a file gender.rb
For the validator GenderValidator you have the file gender_validator.rb
So both files site together in the model folder.
Here is a validator for my Newsletter model
class NewsletterValidator < ActiveModel::Validator
def validate(record)
if record.send_test_email
if test_email_address.blank?
record.errors[:test_email_address] << "Test email address is blank"
end
if record.send_email_to_subscribers
record.errors[:send_test_email] << "You cannot send a test and send to subscribers at the same time"
end
end
end
end
In my Newsletter model I simply have
validates_with NewsletterValidator
You have a spelling mistake in your example
You have
class GenderValidator < ActiveModel::validator
It should be
class GenderValidator < ActiveModel::Validator
Note the capital V
Related
I'm trying to get Clearance to work with AWS Dynamo as the back-end store. The problem I'm having is that I can't get Clearance to not do the email-uniqueness validation, which it can't do because it's not able to do standard ActiveRecord uniqueness validations via a SQL query.
According to the comments in the code, I should be able to have my User object return email_optional? true, and that should disable the uniqueness validation on emails. So I have:
class User < ApplicationRecord
include Dynamoid::Document
include Clearance::User
field :name
field :email
def email_optional?
puts 'yes, email is optional'
true
end
end
But, when I try to create a user I get an error, and, more to the point, the puts is not executed:
$ rails c
Running via Spring preloader in process 18665
Loading development environment (Rails 5.1.3)
irb(main):001:0> u = User.new(name: 'ijd', email: 'ian#fu.bar', password: 'test')
ActiveRecord::StatementInvalid: Could not find table 'editor_development_users'
from (irb):1
Update: the reply from #spickermann reminded me that I should have noted that I also tried without subclassing ActiveRecord::Base (via ApplicationRecord). It gives a different error:
class User
include Dynamoid::Document
....
irb(main):002:0> reload!
Reloading...
=> true
irb(main):003:0> u = User.new(name: 'ijd', email: 'ian#fu.bar', password: 'test')
ArgumentError: Unknown validator: 'UniquenessValidator'
from app/models/user.rb:4:in `include'
from app/models/user.rb:4:in `<class:User>'
from app/models/user.rb:2:in `<top (required)>'
from (irb):3
User.new does not trigger validations. Therefore the error cannot be connected to the validations itself.
At the moment your User model is kind of both: A subclass of ActiveRecord::Base and it behaves like a Dynamoid::Document.
class User < ApplicationRecord
include Dynamoid::Document
# ...
end
ActiveRecord::Base reads the table definition from the database when an instance is initialized. This leads to your exception, because the table does not exist. Just remove the inheritance from ApplicationRecord.
class User
include Dynamoid::Document
# ...
end
The second issue when you remove the inheritance is more complex. Usually, I would suggest to just include ActiveModel::Validations when you want to validate models that do not inherit from ActiveRecord::Base. But the UniquenessValidator isn't defined in ActiveModel::Validations but in ActiveRecord::Validations (what makes kind of sense). This makes Clearance incompatible with models that do not inherit from ActiveRecord::Base.
I would probably define a dummy implementation of a UniquenessValidator as a work-around:
class User
include Dynamoid::Document
class UniquenessValidator
def initialize(_options); end
def def validate_each(_record, _attribute, _value); end
end
# ...
end
Im buliding an app with users model which can have different privileges, described by user_types model (id and type as a string). As I am running rails test, I am getting error
LoadError: Unable to autoload constant User_type, expected app/models/user_type.rb to define it
Here is my model:
class UserType < ActiveRecord::Base
belongs_to :user
validates :type, presence: true, length: { maximum: 100 }
end
Below is the controller and test file
class UserTypeController < ApplicationController
def index
#user_type = User_type.all
end
def new
#user_type = User_type.build(user_type_params)
end
private
def user_type_params
params.require(:user_type).permit(:type)
end
end
Testing model:
require 'test_helper'
class UserTypeTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
def setup
#user_type = User_type.new(id:2,type:"test")
end
test "is_valid" do
assert #user_type.valid?
end
end
I wanted to do some basic "is_valid" test and I got an error described above. I have also dropped last model "user_type" and created it as "UserType" but It didn't work.
Throughout your controller and test you use User_type.all, User_type.build, User_type.new, but your model is named UserType.
Your model's class name is UserType. User_type is not defined anywhere. You either keep the model name as is and fix your test and controller, or rename the model name. The former is the Rails convention, so I'd suggest you go with that (the CamelCased name).
Change your model name to UserType as defined in class and from that UserType model's table remove type column or rename it to something like user_type since that is a reserved column name, can be used for single table inheritance only.
I have two tables: admin_users and users. I want all the created names to be unique (a regular user can't create a name already taken by an admin user and vice-versa). I'm having trouble writing a validates_uniqueness_of validation that is able to analyze information in a different table. I think I have to use a scope of some sort, but I tried all sorts of combinations and couldn't get it to work. To be clear: I'm looking for the correct code to replace the question marks below.
validates_uniqueness_of :user_name, :scope => #???Look in admin users
#table and check to make that this name is not taken.
Any help would be very much appreciated. Thanks!
You can create a custom validator for this.
class UserNameValidator < ActiveModel::Validator
def validate(record)
if AdminUser.exists?(user_name: record.user_name)
record.errors[:base] << "An admin user have this username!"
end
end
end
class User < ActiveRecord::Base
validates_with UserNameValidator
end
I have contact information. The user needs to fill out at least one of three fields, those fields being their email address, mailing address, or phone number.
How would I do this in Ruby on Rails?
Someone can give you a better answer, but this will do. You can just write your own method to check if those fields are blank. If they are blank, render an error.
validate :at_least_one
def at_least_one
return unless email.blank? && address.blank? && phone.blank?
errors.add(:base, 'Error message') # you can add the error to the base or even a particular attribute.
end
While what Justin shows may be fine, Rails does provide a formal scheme for doing just this type of thing. It allows you to create a custom validator just like any of the predefined validators.
See "validates_with" for more details, but here is the example from the Rails guide:
class GoodnessValidator < ActiveModel::Validator
def validate(record)
if record.first_name == "Evil"
record.errors[:base] << "This person is evil"
end
end
end
class Person < ActiveRecord::Base
validates_with GoodnessValidator
end
I'm relatively new to Rails and a bit surprised this isn't a configurable behavior...at least not one I've been able to find yet?!? I would have thought that 99% of forms would benefit from whitespace being trimmed from all string & text fields?!? Guess I'm wrong...
Regardless, I'm looking for a DRY way to strip all whitespace from form fields (of type :string & :text) in a Rails 3 app.
The Views have Helpers that are automatically referenced (included?) and available to each view...but Models don't seem to have such a thing?!? Or do they?
So currently I doing the following which first requires and then includes the whitespace_helper (aka WhitespaceHelper). but this still doesn't seem very DRY to me but it works...
ClassName.rb:
require 'whitespace_helper'
class ClassName < ActiveRecord::Base
include WhitespaceHelper
before_validation :strip_blanks
...
protected
def strip_blanks
self.attributeA.strip!
self.attributeB.strip!
...
end
lib/whitespace_helper.rb:
module WhitespaceHelper
def strip_whitespace
self.attributes.each_pair do |key, value|
self[key] = value.strip if value.respond_to?('strip')
end
end
I guess I'm looking for a single (D.R.Y.) method (class?) to put somewhere (lib/ ?) that would take a list of params (or attributes) and remove the whitespace (.strip! ?) from each attribute w/out being named specifically.
Create a before_validation helper as seen here
module Trimmer
def trimmed_fields *field_list
before_validation do |model|
field_list.each do |n|
model[n] = model[n].strip if model[n].respond_to?('strip')
end
end
end
end
require 'trimmer'
class ClassName < ActiveRecord::Base
extend Trimmer
trimmed_fields :attributeA, :attributeB
end
Use the AutoStripAttributes gem for Rails. it'll help you to easily and cleanly accomplish the task.
class User < ActiveRecord::Base
# Normal usage where " aaa bbb\t " changes to "aaa bbb"
auto_strip_attributes :nick, :comment
# Squeezes spaces inside the string: "James Bond " => "James Bond"
auto_strip_attributes :name, :squish => true
# Won't set to null even if string is blank. " " => ""
auto_strip_attributes :email, :nullify => false
end
Note I haven't tried this and it might be a crazy idea, but you could create a class like this:
MyActiveRecordBase < ActiveRecord::Base
require 'whitespace_helper'
include WhitespaceHelper
end
... and then have your models inherit from that instead of AR::Base:
MyModel < MyActiveRecordBase
# stuff
end