false vs 0 while serving object in database Rails - ruby-on-rails

There is the following code:
class BeautySalonCategory < ActiveRecord::Base
before_create :set_default_values
protected
def set_default_values
self.available = false
end
end
When I use Rails console and input 'BeautySalonCategory.create!(name: "Some name")' I get the following error:
ActiveRecord::RecordNotSaved
But if I change 'self.available = false' to 'self.available = 0' then no errors won't appear. Why? Thanks.

There's two things going on: Rails expects your before_create handlers to return a non-false value when they successfully run, and the result of an a = b statement in Ruby is b. So your self.available = false version returns false, indicating to Rails that the before_create failed. If you define your method like this, everything will be fine:
def set_default_values
self.available = false
true
end
(Props to maringan for spotting this in the comments.)

Related

rubocop app controller function validate param integer use of nil? predicate

I tried rewriting this function numerous ways to get around this error, however, I want to defer to other experts before I disable the cop around it.
def numeric?(obj)
obj.to_s.match(/\A[+-]?\d+?(\.\d+)?\Z/) == nil ? false : true
end
This is used like so:
def index
if params[:job_id] && numeric?(params[:job_id])
This issue was solved via: Checking if a variable is an integer
Update trying:
def numeric?(string)
!!Kernel.Float(string)
rescue TypeError, ArgumentError
false
end
Reference How do I determine if a string is numeric?
New error:
def numeric?(arg)
!/\A[+-]?\d+\z/.match(arg.to_s).nil?
end
Passes all Rubocop tests from a default configuration. Complete gist with tests at https://gist.github.com/aarontc/d549ee4a82d21d263c9b
The following code snippet does the trick:
def numeric?(arg)
return false if arg.is_a?(Float)
return !Integer(arg).nil? rescue false
end
Returns false for the following: 'a', 12.34, and '12.34'.
Returns true for the following: '1', 1.
You can write the method
def numeric?(obj)
obj.to_s.match(/\A[+-]?\d+?(\.\d+)?\Z/).nil?
end
You really don't need to do nil comparisons and then based on the decision returning true/false. #nil? method does it for you.

Possible bad code or Rails multi-thread issue

I'm having an issue with the below code frequently in a Rails 4.4, Ruby 2.2 with puma web server application. This code has excessive if statements just to try and figure out the problem.
We can assume that assign_coupon() returns true or false. When it returns true it creates an association between the review and the coupon, so you can type review.coupon. Before you run assign_coupon() or if assign_coupon() returns false review.coupon will be nil.
The issue is that assign_coupon() is not working and review.coupon is nil, yet the code gets past 'if CouponCodeService.assign_coupon(review)' and then also passes 'if !review.coupon.nil?' and is running 'review.approve!'.
I cannot figure out for the life of me how the code gets passed 2 failing if statements to run 'review.approve!'. This should not be possible.
if review.product.has_coupon_codes?
if CouponCodeService.assign_coupon(review) # this is false
if !review.coupon.nil? # this is false
review.approve! # this is some how executed.
success_message(review)
review.send_reviewer_approved_email
else
review.update(aasm_state: 'requested')
error_message(review)
end
else
review.update(aasm_state: 'requested')
error_message(review)
end
end
def self.assign_coupon(review)
begin
product = review.product
if product.has_coupon_codes?
coupon = product.coupons.where(claimed: false).first
coupon.review_id = review.id
coupon.claimed = true
if coupon.save
return true
else
return false
end
else
return false
end
rescue Exception => e
return false
end
end

Update fails first time, succeeds second time

We've got this object, #current_employer, that's acting a bit weird. Update fails the first time, succeeds the second.
(byebug) #current_employer.update(settings_params)
false
(byebug) #current_employer.update(settings_params)
true
Here's where we initialise it:
#current_employer = Employer.find(decoded_auth_token[:employer_id])
It's just a standard "find".
Current workaround:
if #current_employer.update(settings_params) || #current_employer.update(settings_params)
...
Anyone seen this before?
Update
Tracked it down to this line in a "before_save" call
# self.is_test = false if is_test.nil?
Seems like is_test is a reserved keyword?
Solved
The full callback, with the fix commented inline:
def set_default_values
self.has_accepted_terms = false if has_accepted_terms.nil?
self.live = true if live.nil?
self.account_name.downcase!
self.display_name ||= account_name
self.display_name ||= ""
self.contact_first_name ||= ""
self.contact_last_name ||= ""
self.phone_number ||= ""
self.is_test_account = false if is_test_account.nil?
true # FIX. The proceeding line was returning 'false', which was giving us a 'false' return value for the before_save callback, preventing the save.
end
Model
If it's failing in one instance and succeeding almost immediately afterwards, the typical issue is that you're passing incorrect / conflicting attributes to the model.
I would speculate that the settings_params you're sending have a value which is preventing the save from occurring. You alluded to this with your update:
# self.is_test = false if is_test.nil?
The way to fix this is to cut out any of the potentially erroneous attributes from your params hash:
def settings_params
params.require(:employer).permit(:totally, :safe, :attributes)
end
Your model should update consistently - regardless of what conditions are present. If it's failing, it means there'll be another problem within the model save flow.
--
Without seeing extra information, I'm unable to see what they may be
Update
A better way to set default values is as follows:
How can I set default values in ActiveRecord?
You may wish to use the attribute-defaults gem:
class Foo < ActiveRecord::Base
attr_default :age, 18
attr_default :last_seen do
Time.now
end
end

Rails - Exclude an attribute from being saved

I have a column named updated_at in postgres. I'm trying to have the db set the time by default. But Rails still executes the query updated_at=NULL. But postgres will only set the timestamp by default when updated_at is not in the query at all.
How do I have Rails exclude a column?
You can disable this behaviour by setting ActiveRecord::Base class variable
record_timestamps to false.
In config/environment.rb, Rails::Initializer.run block :
config.active_record.record_timestamps = false
(if this doesn't work, try instead ActiveRecord::Base.record_timestamps = false at the end of the file)
If you want to set only for a given model :
class Foo < ActiveRecord::Base
self.record_timestamps = false
end
Credit to Jean-François at http://www.ruby-forum.com/topic/72569
I've been running into a similar issue in Rails 2.2.2. As of this version there is an attr_readonly method in ActiveRecord but create doesn't respect it, only update. I don't know if this has been changed in the latest version. I overrode the create method to force is to respect this setting.
def create
if self.id.nil? && connection.prefetch_primary_key?(self.class.table_name)
self.id = connection.next_sequence_value(self.class.sequence_name)
end
quoted_attributes = attributes_with_quotes(true, false)
statement = if quoted_attributes.empty?
connection.empty_insert_statement(self.class.table_name)
else
"INSERT INTO #{self.class.quoted_table_name} " +
"(#{quoted_attributes.keys.join(', ')}) " +
"VALUES(#{quoted_attributes.values.join(', ')})"
end
self.id = connection.insert(statement, "#{self.class.name} Create",
self.class.primary_key, self.id, self.class.sequence_name)
#new_record = false
id
end
The change is just to pass false as the second parameter to attributes_with_quotes, and use quoted_attributes.keys for the column names when building the SQL. This has worked for me. The downside is that by overriding this you will lose before_create and after_create callbacks, and I haven't had time to dig into it enough to figure out why. If anyone cares to expand/improve on this solution or offer a better solution, I'm all ears.

How do I set attributes in an ActiveRecord Object before I save it?

I am trying to understand the Active Record Callbacks, but they do not work, like I want to.
e.g.
Model
Checklist<ActiveRecord...
attr_accessible :item1, :item2, :done # they are all boolean
before_save :check_done
private
def check_done
if item1 && item2
write_attribute :done, true
else
write_attribute :done, false
end
end
this doesn't work if I instantiate an object in the console and try to save it, the save operation returns "false" :(
What's wrong with this code?
thanks in advance :)
EDIT: It looks like there is something wrong with the "before_save" call, if I use "after_save", the code works...but the attribute isn't saved (obviously). That's really strange
EDIT 2 Wierd...the development logs shows this
FROM sqlite_master
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
[0m
[1m[35mChecklist Load (0.2ms)[0m SELECT "checklists".* FROM "checklists" ORDER BY checklists.id DESC LIMIT 1
WARNING: Can't mass-assign protected attributes: id
but that is really odd, because if I remove the attr_accessible line I do still get this error...
EDIT 3
If anyone asks, yes I am trying to update an existing record.
EDIT 4
Yes, I like to edit
If I type in the console
c.save => # false
c.errors => #<OrderedHash {}>
The problem with your callback is that it returns false if either item1 or item2 is false.
From the Active Record Callbacks documentation:
If the returning value of a before_validation callback can be evaluated to false, the process will be aborted and Base#save will return false.
The solution is simple; return true at the end of your callback, like so:
def check_done
self.done = (item1 && item2)
return true
end
before_save { |record|
record.done = item1 && item2
}
PLease try:
def check_done
if item1 && item2
done = true
else
done = false
end
end
In your private method, try
def check_done
self.done = (self.item1 && self.item2) ? true : false
end

Resources