Ruby on Rails model field updates through console, not through browser - ruby-on-rails

I noticed that one of my model fields would not update through my app in the browser after it had been initially set. When I went to investigate this I discovered that the field was only declared through a custom validator:
validate :amount_validator
def amount_validator
if self.amount == nil
errors.add(:amount, "Please fill in the amount.")
end
end
I thought the issue was that this was missing:
validates :amount, presence: true
I added this but I still couldn't update the field through the browser. When I saved the value and the page refreshed it had reverted to its original value. I read another SO question that indicated I should try updating this field through the console and see if there were any errors. I did this, it worked with no errors. Went back into the browser and the value had changed but I still could not update it through the browser. Thanks for your help.

Depending on what rails version you're using, the error might be around accessible attributes (Rails 3) or strong paramenters (Rails 4).
On Rails 3, make sure that you have this in your model:
attr_accessible :amount
On Rails 4, make sure that you are allowing the attribute in the hash that you pass to update_attributes in your controller:
your_model.update_attributes(params.require(:your_model_name).permit([:amount]))

Related

Is there a better way of validating a non model field in rails

I have a form field in ROR 4 app called as 'measure'. It is not a database column, but its values will help model create child entries of its own via acts_as_tree : https://github.com/rails/acts_as_tree
I have to throw a validation when 'measure' is invalid. So I have created a virtual attribute known as measure and check for its validations only on a certain condition.
model someModel
attr_accessor :measure
validates_presence_of :measure, :if => condition?
Problem is when I am saving the code, I am thrown a validation which is fine. I am also thrown the same validation when I am trying to update the record in some other method of the model. The only way to surpass that is by writing this code:
# I do not want to do this, is there a better way?
self.measure = "someRandomvalue"
self.save
I am making this as virtual attribute only for throwing validations. Is there a better way of throwing validations? The form has other validations, I do not want the error for this validations to be shown differently just because it is not an attribute.
I want it to validated only when active record is saved via create and update action of the controller and not when it is being updated by some random method of model.
I have seen other developers in my team doing similar thing and was always curious about one thing - "What are you trying to achieve doing things the way you are doing?". You see, I am not sure if validators should be used for values that will not be serialized.
Anyways, you may try using format validator instead of presence, which worked in my team's case:
# Rails 3/4
validates :measure, format: { with: /^.+$/, allow_nil: true }
# Rails 2
validates_format_of :measure, :with => /^.+$/, :allow_nil => true
You may also try using allow_blank instead of allow_nil.
I would rather create a custom validator along the lines of validates_accessor_of for values that I know will never be serialized.
HTH

Why won't rails create associated records/objects from nested form using strong parameters?

I'm trying to create a record and it's associated records from a nested form using strong parameters. My primary model is:
class MaterialDonationRequest < ActiveRecord::Base
has_many :donation_items, dependent: :destroy
accepts_nested_attributes_for :donation_items, allow_destroy: true
validates :name, presence: true
attr_accessor :due_on_event, :date, :donation_items_attributes, :event_id
end
My associated (nested) model is:
class DonationItem < ActiveRecord::Base
validates :name, presence: true
belongs_to :material_donation_request
belongs_to :global_profile
validates :name, presence: true
attr_accessor :_destroy
end
In my material_donation_requests_controller.rb, I have the following for strong parameters:
def material_donation_request_params
params.require(:material_donation_request).permit(:name, :description, :event_flag, :due_on_event, :date, :event_id, donation_items_attributes: [:id, :name, :description, :amount, :_destroy])
end
Here's the line in my create method where I create the object:
#material_donation_request = MaterialDonationRequest.new(material_donation_request_params)
After doing this, #material_donation_request is created and populated correctly from the form. But the associated donation_items do not get created. For instance, in the debugger, when I enter #material_donation_request.donation_items.first, Rails returns nil.
For reference, here is what Rails returns for material_donation_request_params in the manual tests I'm running:
{"name"=>"Name", "description"=>"", "due_on_event"=>"true", "date"=>"", "donation_items_attributes"=>{"0"=>{"name"=>"", "amount"=>"1", "_destroy"=>""}, "1427122183210"=>{"name"=>"", "amount"=>"2", "_destroy"=>""}}}
Why isn't Rails creating the associated objects from the form as well? Everywhere I've looked, it seems like this structure should create everything, and a subsequent save should save everything (or at least throw validation errors as in this case-see update below). Is there something I'm missing?
Update
Since it was brought up in the answers, yes, the material_donation_params shown above would not pass validation. That's the scenario I've been manually testing. It should generate a validation error on save, but instead, simply saves the MaterialDonationRequest with no errors of any kind, and saves nothing to DonationItems.
To be clear, though, if I fill out the form completely and get the following material_donation_request_params:
{"name"=>"Name", "description"=>"", "due_on_event"=>"true", "date"=>"", "donation_items_attributes"=>{"0"=>{"name"=>"first", "amount"=>"1", "_destroy"=>""}, "1427122183210"=>{"name"=>"second", "amount"=>"2", "_destroy"=>""}}}
and then do #material_donation_request.save, it only saves the MaterialDonationRequest, and not any of the DonationItems.
Final Update
Okay. I've deleted my previous "final update" because what I wrote, and what I wrote in some of the comments was wrong. What ended up fixing this was not an update to Rails 4.1.8. I ran the bundle update command before actually saving the gem file with the new Rails version. So really, what ended up fixing this was simply updating all the gems that didn't have fixed version numbers. God only knows why things weren't working with the previous set of gems. Sorry that this isn't so helpful...
From Rails Validations guide
presence
This helper validates that the specified attributes are not empty. It uses the blank? method to check if the value is either nil or a blank string, that is, a string that is either empty or consists of whitespace.
You are requiring donation_item to be present, but your resulting params hash clearly has donation names blank, validation is failing. Calling save! when debugging these things can be helpful since it would throw a error on failure.
I figured out the answer. In total desperation, I upgraded my Rails version from 4.0.2 which is what I had been using, to 4.1.8. After doing this, with no other changes whatsoever (except gem dependencies, of course), it just started working the way it's supposed to. So I guess Rails 4.0.2 has a problem with nested forms and strong parameters.

Rails updating user settings using form_for and blank password fields

I am following Hartl's Rails Tutorial, creating the edit user page. This is how I update in the update method:
#user.update_attributes(user_params)
where user_params is a method for strong parameters
In the user model I have also included the following line:
validates :password, length: { minimum: 6 }, allow_blank: true
Now, when I edit the user's name/email and submit the form with blank password fields, the user is successfully updated (the password stays the same instead of changing to a blank one). This is what I want. But why does that work? Is it something special about the form_for method? If I use rails console and try to update a user using the update_attributes method and a blank password, the object fails to save.
It is because of the allow_blank option which Rails in this case will ignore the password if it comes blank or nil.
Another approach I like is the one from devise on which by using a method called password_required? they can handle multiple kinds of custom validations and gives you total control in case you need to overwrite, but that's just extra.

Rails simple validations not working

class User < ActiveRecord::Base
attr_accessible :email, :name
validates :name,:presence=>true,
:length=>{:maximum=>15}
validates :email,:presence=>true,
:length=>{:maximum=>15}
end
I am new to rails and the simplest of the validators are not working. I think I may be making a very silly mistake . I have a User model with 2 attributes only and when I create a new user in ruby console with wrong validations like no name or a longer name than 15 characters it gets added happily Please suggest.I am using rails version:3.2.13 and ruby version:1.9.3
If you are on rails console, be sure to type reload! after making changes to models. In this way, all changes will be reloaded in the console instance.
Moreover, are you sure you are saving these models? You should try something like this:
user = User.new(email: "john.doe#gmail.com")
user.save
If the result of the last line is false, you can view the validation errors with
p user.errors

Custom Method in Rails Validation

tl;dr the validation works in the browser, but fails with rspec. I'm not sure why.
answer: I was attempting to mass assign user_id, but user_id wasn't mass-assignable. Was building it correctly in the controller, incorrectly in rspec.
This is my Listing model. Every listing has a price and a user_id associated with it.
In my "listing" model, I have "price".
In my "user" model I have a boolean attribute called "vip".
The max value for "price" depends on the User's VIP-status. If VIP, max price is 400. Else, max price is 200.
I wrote a method "listing_ceiling" that should return the correct max value depending on the user's VIP-ness.
It works in the browser, but when I run through my tests, rspec comes back with the error:
undefined method `vip?' for nil:NilClass
I'm not exactly sure where I'm falling short here. It works correctly in the browser, but fails when testing with rspec. Here's the code:
validates_numericality_of :price, :presence => true, :greater_than => 10, :less_than => :listing_ceiling
def listing_ceiling
if self.user.vip?
400.01
else
200.01
end
end
Any ideas why it would fail this way?
The Listing#user returns nil. There could be two problems.
Your listing does not have a user. i.e. foreign key user_id is nil for the Listing in question. OR
Your listing has a value in user_id column. But there is no User with that id.
Solution is to assign a user to your listing.
You should check your RSpec, the problem doesn't lie in your cutom validation method but in your test setup. As the error message states user == nil!

Resources