I'm a beginner in rails and I need to compare 2 DateTime on my controller :
#b = Book.find(params[:book][:id])
if #b.expiry_date > DateTime.now
... something here ...
end
But I get this error :
undefined method `>' for nil:NilClass
Anyone have an idea why ?
Operators are methods in ruby, so your undefined method '>' for nil:NilClass error indicates that #b.expiry_date is nil on this line: if #b.expiry_date > DateTime.now.
You can use a short-circuiting logic to only evaluate the condition if #b.expiry_date is present.
if #b.expiry_date && (#b.expiry_date > DateTime.now)
The if expressions is only true if both sides of the && are also true, so(#b.expiry_date > DateTime.now) won't be executed if the first condition, #b.expiry_date, is false or nil.
Otherwise, you'll need to add logic/validations to ensure the existence of expiry_date.
Just add a check that the record isn't nil in your if statement as shown:
#b = Book.find(params[:book][:id])
if !#b.expiry_date.nil? && #b.expiry_date > DateTime.now
... something here ...
end
If all Books are supposed to have expiry dates you should add a validation that insists an expiry date is included when creating/updating a Book record in your Book Model!
Related
If I try to check for values in my database, I sometimes get errors related to the fact that there were no hits from my query.
So I started using .present? to check if the query returned any result, before doing the original check.
Is there a smoother way to avoid nilClass errors than this?
temp = Event.where(basket_id: basket.basket_id).where(event_type: ["operatorJoined", "operatorDisconnected"]).last
if temp.present? && temp.event_type == "operatorJoined"
You could be writing:
if temp && temp.event_type == "operatorJoined"
... or you might like to look at the Sandy Metz presentation "Nothing is Something" to learn more about avoiding this problem by using the Null Object pattern.
I would really hope this code would be something like:
temp = Basket.events.operator_join_or_disc.last
if temp && temp.operator_joined?
Ruby 2.3 introduced a safe call operator (I've seen it called safe navigation operator quite a lot, as well) &. which is similar to the try! method in rails:
class A
attr_accessor :the_other_a
def do_something
puts "doing something on #{object_id}"
end
end
a = A.new
a.do_something # => shows 'doing something on 70306921519040'
a.the_other_a.do_something # => NoMethodError: undefined method `do_something' for nil:NilClass
a.the_other_a&.do_something # => nil
a.the_other_a = A.new
a.the_other_a&.do_something # => shows 'doing something on 70306910376860'
a.the_other_a&.do_something_else # => NoMethodError: undefined method `do_something_else' for #<A:0x007fe334d62738>
a.the_other_a.try(:do_something_else) # => nil
a.the_other_a.try!(:do_something_else) # => NoMethodError: undefined method `do_something_else' for #<A:0x007fe334d62738>
So, in your example something like this should work:
temp = Event.where(basket_id: basket.basket_id).where(event_type: ["operatorJoined", "operatorDisconnected"]).last
if temp&.event_type == "operatorJoined"
However, present? just checks for !blank?, so if the variable (temp in this example) could be false, '', ' ', [], or {} (among any other things that would return true for blank?) then temp.present? && temp.something_else would not be the same as temp&.something_else. Doesn't apply in this situation, since it's the result of the ActiveRecord query, but something to keep in mind.
I am looking for a nice way to find if a ActiveRecord attribute is a boolean, i have this method here which will check of an summary attribute exists or has data, it does this by the value.nil? || value.zero? but now we have an exception where the value could be a boolean. so it blows up with a NoMethodError: undefined method zero?' for false:FalseClass
def self.summary_attribute_status(element)
# Check if all summary attributes zero or nil
result = element.class.attributes_for_tag(:summary_attributes).all? do |attribute|
value = element.send(attri bute)
next element
value.nil? || value.zero?
end
return result ? nil : 'something'
end
I could do it like this:
value.nil? || value.zero? || (!value.is_a? TrueClass || !value.is_a? FalseClass)
But its seems like there should be a better way of finding its general type, i tried finding if they shared a boolean type parent or superclass but they both inherit from Object < BasicObject. is there no other way?
There is another way to detect if a value is a boolean in ruby.
!!value == value #true if boolean
But I don't think there is any method built-in for that.
If you have for example a User class that has a boolean attribute admin than you can do something like this:
User.columns_hash['admin'].type
# => :boolean
Read more about columns_hash here: http://api.rubyonrails.org/classes/ActiveRecord/ModelSchema/ClassMethods.html#method-i-columns_hash
I have a really unique case of the 'undefined method error' in Rails. I have a Task Order model that has the attributes "obligatedAmount" and "awardAmount". When creating a new Task Order, one of my business rules is the "obligatedAmount" cannot be greater than the "awardAmount". So ensure this, I made a custom validation:
validate :check_amount_obilgated
validates_presence_of :awardAmount
validates_presence_of :obligatedAmount
def check_amount_obilgated #cannot be greater than contract award amount
if obligatedAmount > awardAmount
errors.add(:obligatedAmount, "The Obligated Amount cannot be greater than the Award Amount")
end
end
This works fine. HOWEVER, if I make a new Task Order and I leave the "obligatedAmount" OR the "awardAmount"empty, I Rails takes me to the error page with the error snippet:
undefined method `>' for nil:NilClass'
def check_amount_obilgated #cannot be greater than contract award amount
if obligatedAmount > awardAmount
errors.add(:obligatedAmount, "The Obligated Amount cannot be greater than the Award Amount")
end
end
So I guess the issue is that if one or both values are missing, the ">" operator cannot work. However, I put in the validates_presence_of :awardAmount and :obligatedAmount... is there any way I can get the validations to kick in first or is there any way around this error? Please let me know. Thank you!!
Use to_i to convert nil to zero
def check_amount_obilgated #cannot be greater than contract award amount
if obligatedAmount.to_i > awardAmount.to_i
errors.add(:obligatedAmount, "The Obligated Amount cannot be greater than the Award Amount")
end
end
So the explaination is pretty straightforward. The > operator is defined on the Fixnum class. NilClass does not have > defined, so it will throw an undefined method error. If nil is passed to the valid call, you'll get a comparison error as nil can't be coerced implicitly to a Fixnum.
A quick check in irb shows the errors you can expect if nil shows up in either the right-hand or left-hand operand:
2.1.2 :001 > 1 > nil
ArgumentError: comparison of Fixnum with nil failed
from (irb):1:in `>'
from (irb):1
from /Users/hungerandthirst/.rvm/rubies/ruby-2.1.2/bin/irb:11:in `<main>'
2.1.2 :002 > nil > 1
NoMethodError: undefined method `>' for nil:NilClass
from (irb):2
from /Users/hungerandthirst/.rvm/rubies/ruby-2.1.2/bin/irb:11:in `<main>'
A simple cast to_i on both operands will result in the values being zero when nil and you will always be able to run the comparison.
2.1.2 :005 > nil.to_i
=> 0
So in your code, do:
obligatedAmount.to_i > awardAmount.to_i
I want to check below:
if params[:hidval] > "0"
OR
if !params[:hidval] < "1"
But it gave me error below:
undefined method `>' for nil:NilClass
How do I check above conditions in ruby on rails?
The error message says it all. Make sure, the param actually exists. You try to compare nothing (NilClass) with a number. (which is actually a string, that will be your next problem)
Probably correct would be:
if params[:hidval].present? && params[:hidval] > 0
# Do something ...
end
As of Ruby 2.3, you can use the Safe Navigation Operator &. to streamline this:
irb(main):028:0> 5 > 0
=> true
irb(main):029:0> nil > 0
NoMethodError: undefined method '>' for nil:NilClass
from (irb):29
irb(main):030:0> 5 &.> 0
=> true
irb(main):031:0> nil &.> 0
=> nil
irb(main):032:0> 5 &.> 99
=> false
This will typically get you what you want (e.g. conditional branching) since nil is falsey. But keep that in mind if you actually depend on an exact false value.
&. means to only call the trailing method if the leading object is not null. It makes a lot more sense in a chained method call like:
user&.deactivate!
But, since the greater-than operator > is actually a method in Ruby, it works here as well.
Personally, I'm not convinced the painful aesthetics of params[:hidval] &.> "0" makes this worthwhile...but that's just a question of style.
Try this code
if params[:hidval].present?
if params[:hidval].to_i > 0
# Greater than 0 condition
else
# Less than equal to 0 condition
end
end
Re-reading this question, I realize I missed the mark on my first answer. It side-stepped the problem without addressing it.
The real problem you saw this error is that you probably put ! in the wrong place.
If you meant, if it is not less than "1", then you should write:
if !( params[:hidval] < "1" )
This compares hidval and "1" and THEN negates the result.
What you posted:
if !params[:hidval] < "1"
...first negates hidval (usually resulting in false) and then compares false (or possibly true) to see if it is less than "1". Not only does comparing false to a string not make much sense, you'll actually get you this error:
NoMethodError (undefined method `<' for false:FalseClass)
NoMethodError: undefined method `to_d' for nil:NilClass
this seems to be inconsistent with other "to_"
eg with Rails 3.2.14, and ruby 1.9.3p362:
1.9.3p362 :055 > nil.to_f
=> 0.0
1.9.3p362 :056 > nil.to_d
NoMethodError: undefined method `to_d' for nil:NilClass
1.9.3p362 :057 > nil.to_s
=> ""
1.9.3p362 :058 > nil.to_i
=> 0
it means that when ever i might want to convert to big decimal that i first have to make sure the value is not nil and assign it a 0 value anyway... so... comments on what is the best way to make this consistent? and should i consider this a bug?
to_d isn't part of core Ruby. It's part of the BigDecimal package; specifically you get it when you require "bigdecimal/util". It monkey-patches itself into some of the core classes, but as you've discovered, not all of them.
If you just want nil.to_d to return nil (which seems like the only sensible thing for it to return), you can monkey-patch it yourself:
class NilClass
def to_d
nil
end
end
irb(main):015:0> nil.to_d
=> nil
If you want it to return an actual BigDecimal with value 0, then return BigDecimal.new(0) instead, but I think nil should be nil.
or
(nil.try(:to_d) || 0) + value
You can always use #try, which will return nil if the receiver is nil, or call the method if the receiver is not nil
nil.try :to_d
#=> nil
1.try :to_d
#=> #<BigDecimal:7ffe8e39c1e8,'0.1E1',9(36)>
you're probably better off converting to a string first if that's what you need
BigDecimal.new(amount.to_s)