Rails 3 validates_uniqueness_of works in an unexpected way - ruby-on-rails

I'm new to RoR. I'm facing a problem when using validates_uniqueness_of. I've a table with 3 columns:
name || father_name || dob
Vimal Raj || Selvam || 1985-08-30
I've a code in my model like this:
class Candidate < ActiveRecord::Base
attr_accessible :dob, :father_name, :name
validates_uniqueness_of :name, scope: [:father_name, :dob], case_sensitive: false,
message: ": %{value} already present in the database!!!"
before_save :capitalize_name, :capitalize_father_name
private
def capitalize_name
self.name.capitalize!
end
def capitalize_father_name
self.father_name.capitalize!
end
end
It throws error as expected when I insert => "vimal raj, Selvam, 1985-08-30"
But it is accepting the following data => "Vimal Raj, selvam, 1985-08-30" . I was expecting it will throw an error, but unexpectedly it accepts the record and inserts into the db as a new record.
Please help me on how to solve this.

If you want a one-liner solution, please try this :
before_validation lambda {self.name.capitalize!; self.father_name.capitalize!}
Hope, it will help.

I think the case_sensitivity is only matching on name, not on father_name. I would try changing before_save to before_validation so that both name and father_name are consistently the same capitalization when your validation is evaluated.

Related

Adding referential integrity not on id/foreign_key

In a Rails 6 app, I have the Course model with 3 attributes: instructor_id, year, and tag.
If there a way to make a course with a tag invalid if there isn't an already defined course having the same value as year for the same instructor_id?
For instance, with the following courses table.
|instructor_id|year|tag|
|1 |2019| |
The following statements are all correct
Course.new(instructor_id: 1, tag: 2019).valid? #=> true
Course.new(instructor_id: 1, tag: 2020).valid? #=> false
Or should I write a custom validator for that?
To clarify, here my custom validator
class Course < ApplicationRecord
validates :tag, referential_integrity: { reference: :year, scope: :instructor },
allow_nil: true
end
class ReferentialIntegrityValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors.add(attribute, :required) unless record.class.find_by(options[:scope] => record.send(options[:scope]),
options[:reference] => value)
end
end
Can I achieve the same suing built-in validation?
Maybe you are looking for a validation like this or similar with year instead of tag
validates :instructor_id, uniqueness: { scope: [:tag] }

Validating Rails Globalize Gem with Enum

When using the globalize gem with Active Record enums, I get an error, as if globalize doesn't know that the enum exists.
class Stuff < ActiveRecord::Base
enum stuff_type: { one: 1, two: 2 }
translates :name
validates :name, presence: true, uniqueness { case_sensitive: false, scope: :stuff_type }
default_scope do
includes(:translations)
end
end
If I do:
s = Stuff.new(name: 'stuff')
s.one!
I get an error as the following:
ActiveRecord::StatementInvalid: PG::InvalidTextRepresentation: ERROR: invalid input syntax for integer: "one"
This happens because of the validation, cause it seems like globalize doesn't understand the enum.
Am I doing something wrong? How should I accomplish this?
The solution was to create my own validation method!
Something like:
validate :name_by_type
def name_by_type
max_occurrences = persisted? ? 1 : 0
occurrences = Stuff.where(stuff_type: stuff_type, name: name).count
errors['name'] << 'Name already in use' if occurrences > max_occurrences
end

undefined method `text?' for nil:NilClass Validate uniqness rails3 ruby 187

I just updated from rails 2.3 to 3, i'm trying to replace this old method with something cleaner, because it's outputting the model and field name, wtf!
However I get the above error when calling validates_uniqueness_of (the presence works fine). I passed in the primary id scope, and still get it. Any help is welcome.
def validate
if org_name.blank?
errors.add(:org_name, :blank, :default => nil)
else
if (org = Organization.find_by_org_name(org_name)) && org != self
errors.add(:org_name, :taken, :default => nil, :value => org_name)
end
end
end
to
validates :org_name, :presence => true
validates_uniqueness_of :org_name, :scope => :org_id
Ths is the Rails 3 syntax for uniqueness validtion:
validates :org_name, uniqueness: {scope: :org_id}
This is easy to fix.
Firstly, analyse the error message:
Org name translation missing:
en.activerecord.errors.models.user.attributes.org_name.blank
This is caused by the following line of code:
errors.add(:org_name, :blank, :default => nil)
When you call the above, you are telling rails to look for a translation whose key is :blank. You probably didn't set that up yet, so to do that, just go into your locales file (config/locales/en.yml), and add the following:
en:
hello: "Hello world"
activerecord:
errors:
models:
organization:
attributes:
org_name:
blank: "can't be blank."
Hopefully that will fix it for you.

Rails 3 - adding an index failsafe to ensure the uniqueness of would-be duplicates

I have a rails question that I have been unable to find an answer for on my own. I apologize if it's very simple or obvious, I'm a complete beginner here.
So I have a column in my db called :client_code, which is defined in the model as the down-cased concatenation of the first character of :first_name, and :last_name. So for instance, if I pull up a new form and enter 'John' for :first_name, and 'Doe' for :last_name, :client_code is automatically given the value of 'jdoe'. Here's the relevant portion of the model code:
class Client < ActiveRecord::Base
before_validation :client_code_default_format
validates_presence_of :first_name, :last_name, :email
validates_uniqueness_of :client_code
...
def client_code_default_format
self.client_code = "#{first_name[0]}#{last_name}".downcase
end
end
I would like to add something to this code so that, in the event that someone enters a different client with the same exact name, it does't fail the uniqueness validation but instead creates a slightly modified :client_code ('jdoe2', for example). I could probably figure out how to add an index to all of them, but I would prefer to only include numbers as a failsafe in the case of duplicates. Can anyone point me in the right direction?
Calculating the number of current matching Client objects with the same client_code should work
def client_code_default_format
preferred_client_code = "#{first_name[0]}#{last_name}".downcase
count = Client.count(:conditions => "client_code = #{preferred_client_code}")
self.client_code = count == 0 ? preferred_client_code : preferred_client_code + count
end
Many thanks to #Dieseltime for his response. I took his suggestion and was able to get the functionality I wanted with some minor changes:
before_validation :format_client_code
validates_presence_of :first_name, :last_name, :email, :company_id
validates_uniqueness_of :client_code
...
def format_client_code
unless self.client_code != nil
default_client_code = "#{first_name[0]}#{last_name}".downcase
count = Client.count(:conditions => "client_code = '#{default_client_code}'")
if count == 0
self.client_code = default_client_code
else
self.client_code = default_client_code + (count + 1).to_s
end
end
end

How do you validate the presence of one field from many

I'm answering my own questions - just putting this up here for google-fu in case it helps someone else. This code allows you to validate the presence of one field in a list. See comments in code for usage. Just paste this into lib/custom_validations.rb and add require 'custom_validations' to your environment.rb
#good post on how to do stuff like this http://www.marklunds.com/articles/one/312
module ActiveRecord
module Validations
module ClassMethods
# Use to check for this, that or those was entered... example:
# :validates_presence_of_at_least_one_field :last_name, :company_name - would require either last_name or company_name to be filled in
# also works with arrays
# :validates_presence_of_at_least_one_field :email, [:name, :address, :city, :state] - would require email or a mailing type address
def validates_presence_of_at_least_one_field(*attr_names)
msg = attr_names.collect {|a| a.is_a?(Array) ? " ( #{a.join(", ")} ) " : a.to_s}.join(", ") +
"can't all be blank. At least one field (set) must be filled in."
configuration = {
:on => :save,
:message => msg }
configuration.update(attr_names.extract_options!)
send(validation_method(configuration[:on]), configuration) do |record|
found = false
attr_names.each do |a|
a = [a] unless a.is_a?(Array)
found = true
a.each do |attr|
value = record.respond_to?(attr.to_s) ? record.send(attr.to_s) : record[attr.to_s]
found = !value.blank?
end
break if found
end
record.errors.add_to_base(configuration[:message]) unless found
end
end
end
end
end
This works for me in Rails 3, although I'm only validating whether one or the other field is present:
validates :last_name, :presence => {unless => Proc.new { |a| a.company_name.present? }, :message => "You must enter a last name, company name, or both"}
That will only validate presence of last_name if company name is blank. You only need the one because both will be blank in the error condition, so to have a validator on company_name as well is redundant. The only annoying thing is that it spits out the column name before the message, and I used the answer from this question regarding Humanized Attributes to get around it (just setting the last_name humanized attribute to ""

Resources