Unique undefined method error rails - ruby-on-rails

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

Related

Why can't I access this value in an (OpenStruct) object?

I have an (OpenStruct) object for which I can only seem to access some values. A simplified version of it is below:
#<Project::API::FormData::FormAnswer:0x007f94de911ed0
#form_answer=
#<OpenStruct
user_id=130,
timer_value=[{"foo" => "bar"}]
>
>
If I want to get user_id, that's easy
> answer.user_id
130
But what about timer_value?
> answer.timer_value
NoMethodError: undefined method `timer_value' for #<Project::API::FormData::FormAnswer:0x007f94de911ed0>
> answer.timer_value
NoMethodError: undefined method `timer_value' for #<Project::API::FormData::FormAnswer:0x007f94de911ed0>
maybe, for some reason, for this I need to access it through the form_answer first:
> answer.form_answer.timer_value
NoMethodError: undefined method `form_answer' for #<Project::API::FormData::FormAnswer:0x007f94de911ed0>
---
> answer.first.timer_value
NoMethodError: undefined method `first' for #<Project::API::FormData::FormAnswer:0x007f94de911ed0>
I'm not experienced with OpenStruct - is this the cause?
I have an (OpenStruct) object
No, you don't. You seem to have a PORO* that has a field/instance_variable of type OpenStruct. And given the fact that answer.user_id works, you likely have something like this in that class:
delegate :user_id, to: :#form_answer
That is, FormAnswer class re-exports a property of one of its internal fields. But does not re-export timer_value, for example. Nor does it expose #form_answer.
* plain old ruby object
Thanks Sergio, I found that just as you posted - is indeed an issue in where the class is defined - which I thought that I'd read through, but as ever read and read again. It seems whomever created the class left a bug monkeying up the method definitions:
fields.each do |field_name|
define_method(field_name) { #form_answer.public_send(field_name) }
end
Which is all good, but the fields array was malformed meaning only some values were coming through.
> answer.respond_to?(:timer_value)
true
Bingo!

NoMethodError (undefined method `<' for nil:NilClass):

This is the error I have received:
NoMethodError (undefined method `<' for nil:NilClass):
app/controllers/concerns/withdraws/withdrawable.rb:20:in `create'
This is the part of the code in question:
def create
#withdraw = model_kls.new(withdraw_params)
#verified = current_user.id_document_verified?
#local_sum = params[:withdraw][:sum]
if !#local_sum
render text: I18n.t('private.withdraws.create.amount_empty_error'), status: 403
return
end
if !#verified && channel.currency_obj.withdraw_limit < #local_sum <<<<<- Here is the error
render text: I18n.t('private.withdraws.create.unverified_withdraw_limit_error', limit: channel.currency_obj.withdraw_limit), status: 403
return
end
That's all my code:
https://github.com/DigitalCoin1/Spero-Exchange
The error in question is in this file:
https://github.com/DigitalCoin1/Spero-Exchange/blob/rebuild-peatio/app/controllers/concerns/withdraws/withdrawable.rb
Thank you very much!!!
Remember, (almost) everything is Ruby is an object... including nil.
Keeping that in mind, consider what happens when you call a method that doesn't exist on nil:
irb(main):001:0> nil.something
Traceback (most recent call last):
2: from /Users/scott/.rbenv/versions/2.5.1/bin/irb:11:in `<main>'
1: from (irb):1
NoMethodError (undefined method `something' for nil:NilClass)
Additionally, in Ruby operators such as >, <, and == are actually method calls. So, for example, an instance of Integer such as 3 has a method defined on it called < and when you call 3 < 4, that calls the method on that instance. This works like that because in Ruby you can omit parentheses when making method calls. For example:
irb(main):001:0> 3 < 4
=> true
irb(main):002:0> 3.<(4)
=> true
So putting these two examples together:
irb(main):014:0> nil < 4
Traceback (most recent call last):
2: from /Users/scott/.rbenv/versions/2.5.1/bin/irb:11:in `<main>'
1: from (irb):14
NoMethodError (undefined method `<' for nil:NilClass)
Now, let's take a look at your code.
You're getting the exception:
NoMethodError (undefined method `<' for nil:NilClass)
On this line:
!#verified && channel.currency_obj.withdraw_limit < #local_sum
Looking at this code, you are only calling < in one place. This means that whatever is to the left of it (channel.currency_obj.withdraw_limit) must be nil.
There are a few ways we can fix this... The best way, (in my opinion) is to make sure that channel.currency_obj can never be nil. I unfortunately don't have enough code to show you exactly how to do that, so let's look at some other options...
We can use Ruby 2.3+'s safe navigation operator (&.) -- but it's a little weird to use with an operator like <.
channel.currency_obj.withdraw_limit&. < #local_sum
Note: in this example the expression will evaluate to nil and because nil is falsey the conditional will return false.
Or, we can just add another expression to our conditional to check for nil:
!#verified && channel.currency_obj.withdraw_limit && channel.currency_obj.withdraw_limit < #local_sum
The error happened when channel.currency_obj.withdraw_limit return nil or #local_sum is nil.
And it can't compare nil value.
You must check #local_sum again, and make sure that it have a value. Or channel.currency_obj.withdraw_limit make sure that it have a value.
But i guess that channel.currency_obj.withdraw_limit return nil.
That your problem.
NoMethodError (undefined method `<' for nil:NilClass):
app/controllers/concerns/withdraws/withdrawable.rb:20:in `create'
This error denotes that it is trying to compare < for a nil value.
Can you please print and check channel.currency_obj.withdraw_limit and #local_sum before the error statement.
To avoid nil errors you can include nil check.
if channel.currency_obj.withdraw_limit != nil and #local_sum != nil

rails : undefined method `>' for nil:NilClass

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!

params[...] comparison raises "undefined method `>' for nil:NilClass" error in ruby on rails

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

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)

Resources