I am using the Rails Admin gem. When I add a new activity type and create it again with the same name, it validates that name is already taken. But whenever I try to edit one it will give you an error: "name can't be blank"
For example, I created Swimming, and I tried to add a new activity type which is swimming/SWIMMING etc. To avoid this I used the before_validation callback, to make the first letter a capital, then check the uniqueness of name.
Yes, it's working but whenever I try to edit the name field it will become blank after I submit it.
NOTE: I also tried to use validates :name, presence: true, :uniqueness => {:case_sensitive => true} only without the before_validation but it didn't work.
Activity Type
class ActivityType < ApplicationRecord
before_destroy :ensure_has_no_activity_type
before_validation :capitalize_first_letter_name
has_many :activities
validates :name, presence: true,:uniqueness => {:case_sensitive => true}, length: { maximum: 20 },format: Utilities::RegexValidations.alphanumeric_underscore
validates :description, presence: false
private
def ensure_has_no_activity_type
if activities.present?
errors.add(:base, 'Cannot delete activity type that has activity')
throw(:abort)
end
end
def capitalize_first_letter_name
# Capitalize the first letter and the rest will be small letter
self.name = self.name.capitalize!
end
end
Question: Why whenever I tried to edit and try to submit it, does the name field become blank? What is the reason for this?
The problem arises from capitalize_first_letter_name. "".capitalize! will return nil. If you change it to "".capitalize that will return blank string as expected.
Moreover, capitalize! will return nil if no changes were made. See https://ruby-doc.org/core-2.2.0/String.html#method-i-capitalize-21.
Related
I am trying to introduce a functionality in my application in which the users can suggest an event to the organizers.To suggest an event the user will have to fill a form. I want to validate the presence of few fields(the attribute of the events) with a condition to be true, i.e. the fields should not be left blank if the user(the submitter of the form) is not an admin.However if the submitter of the form is an admin, the fields can be left blank.
validates :attribute_name, presence: { :if => :user.is_admin? }
undefined method `is_admin?' for :user:Symbol Did you mean? is_haml?
I have also tried :user.is_admin()==True , It also throws error:
undefined method `is_admin' for :user:Symbol Did you mean? is_a?
I have this attribute in the users table:
t.boolean "is_admin", default: false
I have the following associations defined in my events model:
has_many :event_users, dependent: :destroy
has_many :users, through: :event_users
has_one :submitter_event_user, -> { where(event_role: 'submitter') }, class_name: 'EventUser'
has_one :submitter, through: :submitter_event_user, source: :user
In the controllers I have this code:
#event.submitter = current_user
The issue is you are trying to call is_admin? on the Symbol :user as the error suggests.
If I understand correctly this attribute should be present unless user.is_admin? returns true
This can be done in multiple ways:
validates :attribute_name, presence: true, unless: -> {|u| u.is_admin?}
#Or
validates :attribute_name, presence: true, unless: :is_admin?
# or using presence directly as you tried to originally
validates :attribute_name, presence: {unless: -> {|u| u.is_admin?} }
# or
validates :attribute_name, presence: {unless: :is_admin? }
I generally prefer the first option as, IMO, it is the least ambiguous and the most readable but all of them should result in the same function so choose the one you prefer as long as you remain consistent.
In the block form the instance is yielded to the block and the block's return value is used to determine whether or not the validation should run.
When using the symbol the symbol is sent to the instance via the send message transmission e.g. self.send(:is_admin? and again the return value is used to determine if the validation should be applied
ActiveModel::Validations::ClassMethods#validates
Update based on revised question:
Since the Event is related to the User via submitter and this is already being set to the instance of a User you can validate in a very similar fashion via
validates :attribute_name, presence: true,
unless: ->(event) { event.submitter&.is_admin?}
Or make a separate method such as
def admin_submitter?
self.submitter&.is_admin?
end
validates :attribute_name, presence: {unless: :admin_submitter?}
I have a table to enter Vehicle Names, model name is Vehicle
I dont want name to repeat..so i have
validates_uniqueness_of :make, :case_sensitive => false
in my model. Thing is I have used soft delete to delete a record by setting is_deleted flag field to true. This is_deleted column is also present in Vehicle model.
So the problem is if I delete 1 table it just soft-deletes it and when I try to create one vehicle with same name which was soft-deleted then error occures because of validation as the field is not originaly deleted from DB.
Is there a simple way to solve this issue.
I believe this should do the trick:
validates_uniqueness_of :make, :case_sensitive => false, unless: :is_deleted
Conditions
From the sounds of it, you'll want to use a conditional validation, which will look up the name with the constraints of it not having is_deleted? attribute set to true:
#app/models/your_model.rb
Class YourModel < ActiveRecord::Base
validates :make, uniqueness: { scope: :name, message: "Only One Name Allowed Sorry!" }, if: :is_unique?
def is_unique?
return Model.find_by({name: name, is_deleted: false}) #-> will look up any data records which have is_deleted as false & same name
end
end
My app allows users to add words from a master words table into their own custom list. So, a word list contains multiple custom words each of which link to a master word.
In my view, I have a field called word_text (virtual attribute) where I let users enter a word, and in my model I am trying to look up the master_word_id and set it on the custom word table. I am unable to access the #word_text value in the model. I always seem to get an error that the master word is a required field (because the look up is failing).
class CustomWord < ActiveRecord::Base
attr_accessible :master_word_id, :word_list_id, :word_text
attr_accessor :word_text
belongs_to :word_list
belongs_to :master_word
validates :word_list, presence: true
validates :master_word, presence: true
before_validation :set_master_word
private
def set_master_word
logger.debug "Received word text #{#word_text}"
_mw_id = nil
if !#word_text.nil?
master_word = MasterWord.find_word(#word_text)
if master_word.nil?
errors.add("#{#word_text} is not a valid word")
else
_mw_id = master_word.id
end
end
self.master_word_id = _mw_id
end
end
I sincerely appreciate any suggestions as to how I can set the value of the master_word_id correctly.
There are several things to fix:
class CustomWord < ActiveRecord::Base
attr_accessible :master_word_id, :word_list_id, :word_text
attr_accessor :word_text
belongs_to :word_list
belongs_to :master_word
validates :word_list, presence: true
#validates :master_word, presence: true <= This will always throw error
validates :word_text, presence: true
validates :master_word_id, presence: true
before_validation :set_master_word
private
def set_master_word
logger.debug "Received word text #{self.word_text}"
self.master_word_id = MasterWord.find_by_word_text(self.word_text).id
end
end
Not sure if it will work because I don't know the rest of your app but I hope it points you in the right direction.
I am new to Rails and Ruby. On my view, I have 2 radio buttons that ask if the person is a resident of the US. If they are, a state select is shown. If they aren't, a country select is shown.
I am trying to validate that a state was selected, if the person is a resident of the US.
How can I create a validation and access the state out of the addresses_attributes?
Here is my model:
class Person < ActiveRecord::Base
has_many :addresses, :as => :addressable
has_one :user
accepts_nested_attributes_for :user, :allow_destroy => true
accepts_nested_attributes_for :addresses
attr_accessor :resident
attr_accessible :campaign_id,
:first_name,
:last_name,
:user_attributes,
:addresses_attributes,
:resident
validates :first_name, :presence => true
validates :last_name, :presence => true
validates_presence_of :resident, :message => "must be selected"
end
These are the relevant parameters being sent:
"resident"=>"true",
"addresses_attributes"=>{"0"=>{"country_code"=>"",
"state"=>""}}
You need custom validation method.
validate :check_state_presence
def check_state_presence
if self.resident && !self.addresses.state.present?
self.errors[:base] << "You need to Select State if you are a US resident."
end
end
You can sort it out using validates_inclusion_of instead.
Ruby API says:
If you want to validate the presence of a boolean field (where the real values are true and >false), you will want to use validates_inclusion_of :field_name, :in => [true, false].
This is due to the way Object#blank? handles boolean values: false.blank? # => true.
+1 to #VelLes for the help in pointing me in the right direction. I am answering my own question because I had to change #VelLes example a bit to get it to work and I want other people to see the full solution.
Since I am using attr_accessor as a virtual attribute, when the true/false value comes in from the radio button, it gets stored as a string. Therefore if self.resident = "false", it will get evaluated to true.
You can do self.resident == 'false' or convert to a boolean and add a new self.resident? method. I chose the latter.
The boolean conversion came from this blog post, add to a file in config/initializers
class String
def to_bool
return true if self == true || self =~ (/(true|t|yes|y|1)$/i)
return false if self == false || self.blank? || self =~ (/(false|f|no|n|0)$/i)
raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
end
end
My final code is:
validate :check_state_presence
def resident?
resident.to_bool
end
def check_state_presence
if self.resident? && !self.addresses[0].state.present?
#the inline version of the error message
self.addresses[0].errors.add(:state, "must be selected")
end
end
Please let me know if there is a better 'rails' way to do this!
Airports have four-letter ICAO codes. By convention, these are always uppercase. I'm creating a form for receiving user input, but this form needs to be able to accept user input in mixed case, and prevent them from creating dupes.
The default :uniqueness is case-sensitive, of course. I figured out how to transform the user's input to uppercase before it gets saved, but the problem is that this appears to be post-validation, instead of pre-validation.
For example, if there is already an Airport with ICAO of KLAX, a user can enter klax, it will get validated as unique, and then transformed to uppercase and stored, resulting in duplicates.
Here's my model code at present.
class Airport < ActiveRecord::Base
validates :icao, :name, :lat, :lon, :presence => true
validates :icao, :uniqueness => true
before_save :uppercase_icao
def uppercase_icao
icao.upcase!
end
end
Or a slightly different take: Write a setter for icao that converts anything thrown at it to uppercase:
def icao=(val)
self[:icao] = val.upcase
end
And then you can use regular uniqueness validation (back it up with a unique index in your DB). Might even make things a little easier for the DB during finds, 'cause it doesn't have to worry about case-insensitive comparisons any more.
Hope this helps!
try this:
validates :icao, :uniqueness => { :case_sensitive => false }
Updated answer for Rails 4.
class Airport < ActiveRecord::Base
validates :icao, :name, :lat, :lon, :presence => true
validates :icao, :uniqueness => { case_sensitive: false }
def icao=(val)
write_attribute :icao, val.upcase
end
end
Simply fixed (as many problems with Rails are) - as Danny pointed out above, although not in his own answer so I can't accept it :), changing before_save to before_validation fixes it perfectly.