Rails validating virtual attributes - ruby-on-rails

I this model:
class Bunny < ActiveRecord::Base
attr_accessor :number
validates_presence_of :number
validates_numericality_of :number
end
Whenever I submit a form to create this model I get the following error:
undefined method `number_before_type_cast' for #<Bunny:0x103624338>

I fixed the problem by adding this method to my Bunny model:
def number_before_type_cast
number
end
I don't like it, but I suppose it will work until someone posts a better solution.

Rails generates the FIELDNAME_before_type_cast in the model for each field. It stores the value from the form as a String before it's converted (cast) in this case to a number (it might be a date for example). This cast occurs before save, but after validation.
So when validation occurs before that cast is performed it has to use the "before type cast" value to get the value. Since this is not generated for your attribute, it fails.

Related

How can I use a custom predicate to validate multiple fields with dry-validation?

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

Getting "Object expected but got string" error when trying to save in my controller

I’m using Rails 5. I have this model
class ConfidentialMemo < ApplicationRecord
belongs_to :scenario
belongs_to :scenario_role
end
In my view, I have this drop down set up for selecting a field to be populated into the “scneairo_role” field …
<%= f.collection_select :scenario_role, #scenario.roles, :id, :name, include_blank: false %>
In the create method of my controller, I have this
#confidential_memo = ConfidentialMemo.new(confidential_memo_params)
…
private
def confidential_memo_params
params.require(:confidential_memo).permit(:description, :scenario_id, :scenario_role)
end
but I’m getting the error
ScenarioRole(#70207639353420) expected, got String(#70207645188180)
What does this error mean and more importantly, what do I need to adjust tis save my model successfully?
Change :scenario_role to :scenario_role_id in your collection_select method and in your controller params method. Your form is outputting and sending an id (in the form of a string), but if you just try to assign an id to #note.scenario_role, it'll fail with the same type of message.

Errors raised twice in when inherited model object validation fails

Subclass validations as well as superclass validations implemented. How to discard superclass validations in subclass?
My code:
class a < ActiveRecord::Base
validates_presence_of :price
end
class a2 < a
validates_presence_of :price
end
When I am creating a object for a2, using following command:
x = a2.new
x.save
the following errors are displayed:
x.errors.full_messages
=> ['price can't be blank','price can't be blank']
How can I resolve this, so that validations of superclass are ignored.
The two error messages are expected according to your code.
What you did on inherited model is to add one more validation to the validation chain, instead of overwrite it.
I tried using "Add to errors" unless errors.added? which works fine.

Rails validation fails even when data is entered

Here is the command that I'm executing in Rails Console:
Person.create!(:firstName=>"matt", :lastName=>"master", :gender => 1)
My result is this error message:
ActiveRecord::RecordInvalid: Validation failed: Firstname can't be blank
My model validation code looks as such:
class Person < ActiveRecord::Base
belongs_to :user, :class_name => 'User', :foreign_key => 'fk_ssmUserId'
validates_presence_of :firstName, :lastName
When I comment out validates_presence_of everything works and my data is entered properly into the database, so I know that the values are actually being passed into the new object. I even inspected the new object created inside the Rails::ActiveRecord::Validations code to make sure it was being instantiated correctly before being saved. It was. Also, I have other models with validates_presence_of that work 100% fine every time. It's just this one model. I am using Rails 3.1.0.rc1.
Any ideas?
Thanks!
:firstName=>"matt", :lastName=>"master", :gender => 1
check that the key correspond to your model columns. Seems to be everything should be fine.
I have a suspicion that this error relates to the fact that you're creating and saving the object using the .create! method from the console. Your Person class appears to require a foreign key, a value which is probably not being instantiated when you create an object at the console. To test this, try typing:
test = Person.create(:firstName=>"matt", :lastName=>"master", :gender => 1)
with no bang after the .create method. This should not generate an error. Now type:
test
It's very likely that you have required key values set as "nil" and that the object can't be saved from console until you fill in the appropriate values.

ActiveRecord association getting saved before the record in an update

I have an Entry model which has many Tags. Tags are added to an entry by typing them into a textbox on my form, via a tag_names virtual attribute. Before validation on the Entry model, the tag_names string is converted into actual Tag objects using find_or_create_by_name. The Tag model also has validation to make sure the tag name matches a regex, which is run via the association.
My Entry model looks like this:
class Entry < ActiveRecord::Base
has_many :entry_tags
has_many :tags, :through => :entry_tags
before_validation :update_tags
attr_writer :tag_names
private
def update_tags
if #tag_names
self.tags = #tag_names.split(",").uniq.map do |name|
Tag.find_or_create_by_name(name.strip)
end
end
end
end
When I create a new Entry object and assign it tags, everything works correctly -- the tags are not saved if there is a validation error on one of the Tags, and an error message is passed back. However, if I try to update an existing Entry object with an invalid tag, instead of passing back a message, my self.tags= call (in update_tags above) is throwing an exception with the validation error message. Even if I overwrite find_or_create_by_name to actually just return a new object instead of calling create, I get the same outcome.
It seems to me (and the docs seem to corroborate) that the tags= call is actually saving my Tag objects before the main record gets saved when the Entry object already exists. Is there anything I can do to make this save not happen, or to stop it from raising an exception and just causing my save to return false?
I would try something like this:
class Entry < ActiveRecord::Base
has_many :entry_tags
has_many :tags, :through => :entry_tags
before_validation :update_tags
attr_writer :tag_names
validates_associated :tags
private
def update_tags
return unless #tag_names
current_tag_names = tags.map(&:name)
user_tag_names = #tag_names.split(",").uniq
#add in new tags
user_tag_names.each do |name|
next if current_tag_names.include?(name)
tags.build :name => name
end
#remove dropped tags
( current_tag_names - user_tag_names ).each do |name|
removed_tag = tags.find_by_name(name)
tags.delete(removed_tag)
end
end
end
This way you're only initializing the related models in your update_tags action and so won't throw validation errors. I also added in the validates_associated :tags so that errors on these related models can be reported back via the standard input form using error_messages_for :entry.
Update included code for removing dropped tags.
You could catch the exception raised and return false, in that case, from update_tags which will halt the save on the Entry.
Alternatively, if you want to avoid handling that exception, you could build a new Tag instance where one doesn't already exist and check whether it is valid before proceeding (new_tag.valid?) and if it is not then return false from update_tags.

Resources