How can I check if the validation is true and use it in a condition in the controller?
Model
validate :no_reservation_overlap
scope :overlapping, ->(period_start, period_end) do
where "((date_start <= ?) and (date_end >= ?))", period_end, period_start
end
private
def no_reservation_overlap
if (Reservation.overlapping(date_start, date_end).any?)
errors.add(:date_end, 'it overlaps another reservation')
end
end
Link for more information: Date range overlap per user rails
I want to be able to check if validation is true and pass it to the controller
Controller
if validation == true
#do something
end
I tried using on: create to prevent it from executing a create action.
Just call valid? on an ActiveRecord object. For example:
#reservation = Reservation.new(reservation_params)
if #reservation.valid?
# Do something if any validation fails
if #reservation.errors[:date_end].include? 'it overlaps another reservation'
# Do something if the overlapping validation fails
end
end
You can read more about validations in the Ruby on Rails Guide
If you want to check for a specific validation error, you can look at this Stack Overflow question.
if #user.valid?
do something
end
Related
In Rails 5 I can't seem to set a field without having the validation fail and return an error.
My model has:
validates_presence_of :account_id, :guid, :name
before_save :set_guid
private
def set_buid
self.guid = SecureRandom.uuid
end
When I am creating the model, it fails with the validation error saying guid cannot be blank.
def create
#user = User.new(new_user_params)
if #user.save
..
..
private
def new_user_params
params.require(:user).permit(:name)
end
2
Another issue I found is that merging fields doesn't work now either. In rails 4 I do this:
if #user.update_attributes(new_user_params.merge(location_id: #location_id)
If I #user.inspect I can see that the location_id is not set. This worked in rails 4?
How can I work around these 2 issues? Is there a bug somewhere in my code?
You have at least two options.
Set the value in the create action of your controller
Snippet:
def create
#user = User.new(new_user_params)
#user.guid = SecureRandom.uuid
if #user.save
...
end
In your model, use before_validation and add a condition before assigning a value:
Snippet:
before_validation :set_guid
def set_guid
return if self.persisted?
self.guid = SecureRandom.uuid
end
1
Use before_validation instead:
before_validation :set_guid
Check the docs.
2
Hash#merge works fine with rails ; your problem seems to be that user is not updating at all, check that all attributes in new_user_params (including location_id) ara valid entries for User.
If update_attributes fails, it will do so silently, that is, no exception will be raised. Check here for more details.
Try using the bang method instead:
if #user.update_attributes!(new_user_params.merge(location_id: #location_id))
I'm very confused about this. My model has the following custom validation:
def custom_validation
errors[:base] << "Please select at least one item" if #transactionparams.blank?
end
Basically it's checking to make sure that certain parameters belonging to a different model are not blank.
def request_params
#requestparams = params.require(:request).permit(:detail, :startdate, :enddate)
#transactionparams = params["transaction"]
#transactionparams = #transactionparams.first.reject { |k, v| (v == "0") || (v == "")}
end
If it's not blank, then what happens is that the record is saved, and then all kinds of other things happen.
def create
request_params
#request = #user.requests.create(#requestparams)
if #request.save
...
else
render 'new'
end
end
If the record is not saved, the re-rendered new view then shows what the errors are that stopped #request from being created. The problem is that whether or not #transactionparams.blank? is true or false, the record always fails to save, and I checked this specifically with a puts in the log.
What's happening? I read through the docs because I thought that maybe custom validators couldn't be used on other variables... but that's not the case...
Thanks!
OK actually read up on related articles. It's bad practice to ever access a variable from the controller in the model. That's why... If i put the puts inspection in the model not controller, #transactionparams is always nil.
Lets say I have the following model:
class Car < ActiveRecord::Base
attr_accessible :wheels,
:engine_cylinders
validates :wheels, :engine_cylinders, presence: true, numericality: true
end
Lets say I then have the following controller action:
#car = Car.find(params[:id])
#car.wheels = "foo"
#car.engine_cylinders = 4
#car.save
This save will fail as wheels will fail the numericality condition.
Is there any way to persist the succesful attributes (in this case engine_cylinders), while adding the invalid attributes to the errors array? E.g. is there a "soft" validation in Rails?
You want to write a Custom Validator.
class Car < ActiveRecord::Base
validate :wheel_range,
:engine_cylinder_range
def engine_cylinder_range
flash[:notice] = "engine_cylinder was not saved because it wasn't a number" unless engine_cylinder.is_a? Fixnum
# set engine_cylinder back to old value
end
def wheel_range
flash[:notice] = "wheels was not saved because it wasn't a number" unless wheels.is_a? Fixnum
# set wheels back to old value
end
end
You don't have to use flash here, you could use any variable for internal processing or re-display.
You may also want to put this custom validation check on the :before_save hook. Use the _was magic method to get the old value.
If you're looking to bypass validations you can always do so with:
if #car.save
# ...
else
#car.save(validate: false)
end
You may want to have a different conditional on that or whatever... but this is how you bypass validations on a one-off basis.
This may, however, destroy the errors array so you could rebuild it after the save(validate: false) with:
#car.valid?
You can also bypass validations one-at-a-time using #car.update_attribute(:attribute, <value>).
If you just want to know if the model is valid or not without saving it #car.valid? does exactly that. It also adds invalid attributes to errors array. And #pdobb already pointed out how to bypass validation when saving.
I am trying to output the value of a method on my Item model (current_user is defined in application_controller). I currently have as my rabl template:
object #item
attributes :id, :name
code :is_liked do |this_item|
if current_user
this_item.is_liked current_user
else
false
end
end
and in my model:
class Item < ActiveRecord::Base
...
def is_liked user
if user
if user.liked_item_ids.include?(self.id)
return true
else
return false
end
end
end
....
end
but it isn't working. I'm not sure what a proper way of outputting this would be. Any idea how to get this to work correctly?
edit 1
Here's the error that I'm getting:
Failure/Error: Unable to find matching line from backtrace
ActionView::Template::Error:
stack level too deep
Your rabl seems to be fine, however, when your find yourself adding some logic in your views (rabl can be compared to a view) you might want to consider refactor the logic in a presenter.
More information about presenters with rabl here
Regarding your error, like #apneadiving just said, there is a recursion issue in your codebase somewhere. Just by curiosity, have you try to rename the code block into something else than your method's name ? Depending on which version on rabl you are using, this could be the issue.
Finaly, you should consider refactoring your is_liked method:
def is_liked user
return user.liked_item_ids.include?(id) if user
false
end
Try:
node(:is_liked) {|this_item| this_item.is_liked(current_user) }
You already have the method, you can simply invoke here within a node instead of recreating logic.
I am writing my specs on my model Thing which has a date field which should be after the date of creation, thus I use the validate_timeliness plugin like that
validate_date :date, :after Time.now
I want to be able to add some Things with an anterior date but validation fails. I want to bypass the validation when creating some Things with the machinist factory.
Any clue ?
Shouldn't your validation ensure that the date is after the created_at attribute?? Rather than Time.now???
You shouldn't be trying to use invalid data in your tests, what you probably should do instead is fudge the created at time.
#thing = Thing.make(:created_at => 1.day.ago)
The only reason to try and put a time in the past in your spec surely should be to test that the validation is indeed working ..
#thing = Thing.make_unsaved(:date => 1.day.ago)
#thing.should have(1).error_on(:date)
Is there a reason why you want to do this? What are you trying to test??
If you call your_obj.save with a Boolean parameter =true like this: some_obj.save!(true), than all validations would be skipped. This is probably the undocumented ActiveRecord feature that is widely used in my company :)
Hmm, there's no straightforward way to do with Machinist itself. But you can try to trick it ... in spec/spec_helper, redefine the Thing model before the Machinist blueprints are loaded.
class Thing
def before_validation
self.date = 1.hour.from_now
end
end
You can catch the exception thrown by the validation. If you require the following code in your spec_helper after requiring machinist. To use it you can add a false as the first argument to #make.
module Machinist
module ActiveRecordExtensions
module ClassMethods
def make_with_skip_validation(*args, &block)
validate = !(!args.pop if ( (args.first == true) || (args.first == false) ))
begin
make_without_skip_validation(*args, &block)
rescue ActiveRecord::RecordInvalid => invalid
if validate
raise invalid
else
invalid.record.save(false)
end
end
end
alias_method_chain :make, :skip_validation
end
end
end