Why wont this work in my rails controller - ruby-on-rails

I have this if condition that checks if the country is not in the USA
if params[:address][:country] != 'United States'
logger.info "I am in here"
#address.errors.add(:base, "We're sorry, you cant update your address")
#address.errors.add(:country, "We're sorry, you cant update your address")
end
if #address.update_attributes(params[:address])
flash[:notice] = 'Information was successfully updated.'
But I assumed that if you added an error to the base of an object it would halt but nothing is stopping the update and the flash notice always appears...How do i stop this and add a flash error instead

Randomly adding an error via #address.errors.add() will not cause the record to be invalid. That's why your call to update_attributes is still occurring (and not throwing a validation error). As far as I know, to function properly, errors.add() needs to happen inside the rails validation mechanism. Otherwise, you'll get some strange behavior.
For example, check this out in rails console
# assume a valid record
#address = Address.first
#address.valid? # => true
#address.errors.add(:random_error, "random error")
#address.errors # => {:random_error => ["random error"]}
#address.valid? # => true
#address.errors # => [] - call to valid? caused added error to disappear
You'd expect the call to valid? after adding an error to be false but it's not. And it also ends up clearing out the error you just added.
You said you can't move this to the model but that's really the right place for this. You can see more about active record validations here. Notice that all the examples have the validation happening in the models.

Related

Check if ActiveRecord object is destroyed using the .destroy() return value

I am maintaining someone's code base and they have something like this:
if #widget_part.destroy
flash[:message] = "Error deleting widget part"
else
flash[:message] = "Widget part destroyed successfully"
end
What does destroy return? Is it ok to test like this? The reason I'm asking is that I tried to use
flash[:message] = "Error deleting widget part : #{#widget_part.errors.inspect}"
and there are no error messages so I am confused. It gives something like
#<ActiveModel::Errors:0x00000103e118e8 #base=#<WidgetPart widget_id: 7, ...,
id: 67>, #messages={}>
If you're unsure, you can use destroyed? method. Return value of destroy is undocumented, but it returns just freezed destroyed object (you cannot update it). It doesn't return status of destroy action.
Although generally destroying object should always succeed, you can listen for ActiveRecordError. For example Optimistic Locking can raise ActiveRecord::StaleObjectError on record destroy.
As some people mentioned above, that destroy does not return a boolean value, instead it returns a frozen object. And additionally it does update the state of the instance object that you call it on. Here is how I write the controller:
#widget_part.destroy
if #widget_part.destroyed?
flash[:success] = 'The part is destroyed'
else
flash[:error] = 'Failed to destroy'
end
According to the Ruby on Rails API documentation, the destroy method will return the object that you destroyed, but in a frozen state.
When an object is frozen, no changes should be made to the object since it can no longer be persisted.
You can check if an object was destroyed using object.destroyed?.
Note that while #destroyed? works in the OP’s case, it only works when called on the same model instance as #destroy or #delete; it doesn’t check the database to see if the underlying record has been deleted via a different instance.
item1 = Item.take
# => #<Item:0x00000001322ed3c0
item2 = Item.find(item1.id)
# => #<Item:0x00000001116b92b8
item1.destroy
# => #<Item:0x00000001322ed3c0
item1.destroyed?
# => true
item2.destroyed?
# => false
item2.reload
# => raises ActiveRecord::RecordNotFound
If you need to check whether another process has deleted the record out from under you (e.g. by another user, or in a test where the record is deleted via a controller action), you need to call #exists? on the model class.
Item.exists?(item2.id)
# => false

Rails Controller Object.save

I am working through Head First Rails and I came across some code that is confusing me a little. The goal of the code is to check and see if any error was made when a new record is being created. If there is an error, then the goal is the redisplay the page. If there is no error, the goal is to save the record to the database. Here is the controller code that the book gives:
def create
#ad = Ad.new(params[:ad])
if #ad.save
redirect_to "/ads/#{#ad.id}"
else
render :template => "ads/new"
end
end
The only thing about this code that confuses me is the fact the line (if #ad.save). Now, I know this line is testing to see if there are errors. If there are, it returns false, and if there are not, it returns true. However, I noticed that if no errors exist (it returns true), the record is actually saved. I thought "if" statments in ruby just tested a condition, but in this case, the condition is being tested AND executed. The strange this is that if I add another #ad.save, the database DOES NOT save the record twice. Like so:
def create
#ad = Ad.new(params[:ad])
if #ad.save
#ad.save
redirect_to "/ads/#{#ad.id}"
else
render :template => "ads/new"
end
end
This code does the exact same thing as the first bit of code. Why in the first bit of code is the #ad.save being executed, and how come on the second bit of code the #ad.save is not executed twice (only one record is created)?
Your assumption about if statements in ruby is incorrect. They can in fact execute code.
def return_true
puts 'inside method'
true
end
if return_true
puts "it ran some code"
end
# output will be:
# inside method
# it ran some code
In your second example the save is being run at least once. If the result of #as.save is truthy then it's run a second time. If it is going through the first part of the if branch then something else is preventing it from being saved to the database twice but there's not enough info for me to tell why. It could be that you have a unique contraint. Try doing #ad.save!. The bang version will throw an error if there are any validation errors.

show error messages from two models when updating Rails

I am updating the attributes of two models from one form.
User.transaction do
begin
#user.update_attributes!(params[:user])
#board.update_attributes!(params[:board])
rescue ActiveRecord::RecordInvalid
end
end
When the #user.update_attributes produces an error the transaction is stopped and the error message is shown in the view.
However I want to try to update both #user and #board and get the error messages for both so that the user can correct all their mistakes one time.
How can I do this?
Thank you very much in advance.
All you need to do is not use the bang (!) version of update_attributes. Try:
User.transaction do
if #user.update_attributes(params[:user]) && #board.update_attributes(params[:board])
...do stuff...
else
render ... (go back to the action the user got here from)
end
end
Then in your view code put an errors block using a conditional like if #user.errors.any?.
The bang version of update_attribtues and most activerecord methods means it will raise an error if it fails, the regular version just returns false and doesn't save, so you can use that whenever it's ok to fail you just need to take action based on success/failure. Only use the bang version when it's not ok to fail, or when you want to show a 500 screen or something for some reason...
You could do the following:
User.transaction do
#user.update_attributes(params[:user])
#board.update_attributes(params[:board])
raise ActiveRecord::Rollback unless #user.valid? && #board.valid?
end
This will ensure that both update attribute methods run so that you can get error messages for both objects. If either object is invalid though no changes will be persisted to the database.
I think there is some mistake in the method name #user.update_attributes!(params[:user]) it should be like this #user.update_attributes(params[:user]) or once you need cross check your params values whether it is correct or not else your code looks correct. You can have a help with this update_attributes

In rails, how can I find out what caused a .save() to fail, other than validation errors?

I have an ActiveRecord model which is returning true from valid? (and .errors is empty), but is returning false from save(). If the model instance is valid, how can I find out what's causing the save to fail?
If #user.save (for example) returns false, then just run this to get all the errors:
#user.errors.full_messages
Try using the bang version save! (with an exclamation mark at the end) and inspecting the resulting error.
Check all your callbacks.
I had a problem like this where I had and "after_validate" method that was failing after I had made a bunch of changes to the model. The model was valid but the "after_validate" was returning false, so if I used model.valid it said true, but then if I saved it gave me validation errors (passed through from the after_validate callback). It was weird.
Look at the application trace and you should be able to see what line of code is raising the exception.
Yea, I fixed this issue by making sure I return true in all my before_* callbacks then it starts working :)
Make sure you aren't trying to save a deleted record.
I had the same issue. But unlike the selected answer - my issue wasn't callbacks related.
In my case I had tried to save to a deleted record (deleted from DB).
#user = User.new
#user.save! # user saved to DB
#user.persisted? # true
#user.destroy # user deleted from DB
#user.persisted? # false, user still has its id
#user.valid? # return true
#user.errors # empty
#user.save # return false
#user.save! # raise ActiveRecord::RecordNotSaved
The problem I had was that I had forgotten to add the validation to the model.
class ContactGroup < ActiveRecord::Base
validates_presence_of :name
end

Am i creating a nil object? (undefined method ___ for nil:NilClass) error - ruby on rails

My validations were working for a while, or so I thought. Now I come back to them after a while doing something else and I am getting the error above. I think it means I am creating a nil object with the .new method but I am just lost. It seemed to be a problem with the creation of a new object by the controller because even if I # out the validation the next validation in the tree on another attribute of #thing throws up the same error. However even if I create the object in the console and test it's there the .save method throws up the error still - undefined method 'user_id' for nil:NilClass
ThingsController:
def create
#thing = Thing.new(params[:thing])
#thing.user_id = #currentuser_id
if #thing.save
flash[:notice] = "Successfully created thing."
redirect_to #thing
else
#flash[:notice] = "Your thing did not get created."
render 'otherthings/show'
end
end
Thing.rb
validate :user_valid
def user_valid
errors.add("you must be logged in to add a thing") unless #thing.user_id?
end
I'm a bit of a ruby on rails noob (i.e. <8weeks) and this is my first stackoverflow question, so go easy on me if this is stupidly obvious. I've tried params["thing"] too, as that worked in the console after manually creating params to match the log files (whereas params [:thing] didn't) but that doesn't change anything in terms of the error message I get.
When you are calling unless #thing.user_id?, it's flipping out because there's no #thing (it doesn't get passed from your controller to your model as an instance variable).
I don't remember how exactly it works, but I'm pretty sure that when calling validate :user_valid, it will pass along the record to be validated for you. If that's indeed the case you could try:
def user_valid
errors.add("you must be logged in to add a thing") unless user_id?
end

Resources