-
Notifications
You must be signed in to change notification settings - Fork 120
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Avoid big numbers for money #3189
Conversation
Shouldn't the |
Okay, so apparently in Ruby: Which means that |
@alecgibson I put it at the end because otherwise it would fall in the condition |
@mgrassotti have you clicked the example link above? It still breaks the site. At the moment we have two paths to generating a
The above link breaks the code, because it follows the second path, where we don't check I think logically we should do all casting first, and then check
|
6b67b5f
to
e855e7d
Compare
e855e7d
to
9a923c5
Compare
@alecgibson sorry I understand now. I pushed an update |
lib/smart_answer/money.rb
Outdated
@@ -9,14 +9,11 @@ class Money | |||
attr_reader :value | |||
|
|||
def initialize(raw_input) | |||
if raw_input.is_a?(Numeric) | |||
@value = BigDecimal.new(raw_input.to_s) | |||
number, error_message = self.class.raw_input_to_number(raw_input) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I like this pattern of returning a tuple with an error message. Why not just raise
directly from raw_input_to_number
? It should hopefully tidy up your control logic a lot more.
lib/smart_answer/money.rb
Outdated
[raw_input, nil] | ||
else | ||
raw_input = raw_input.to_s.delete(',').gsub(/\s/, '') | ||
if raw_input.to_f.infinite? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per my previous comment, you could avoid having to check infinite?
multiple times if you just call it once when you know you have a Numeric
(probably outside this method).
9a923c5
to
6d1f6f2
Compare
@alecgibson ok please check it now |
6d1f6f2
to
30b431d
Compare
lib/smart_answer/money.rb
Outdated
end | ||
end | ||
|
||
def self.valid_raw_input(raw_input) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice to keep the validation predicate separate from the validation method. So, I'd expect this to be two methods:
valid_raw_input?
, which returns a booleanvalidate_raw_input
, which callsvalid_raw_input?
and willraise
if it's invalid. You could also move theinfinite?
check into the validation, because it's part of what makes the input valid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alecgibson ok. I think if could be tidy also if done in 1 method. Have a look to my latest changes. Thanks
lib/smart_answer/money.rb
Outdated
if raw_input.is_a?(Numeric) || raw_input.is_a?(Money) || raw_input =~ /\A *[0-9]+(\.[0-9]{1,2})? *\z/ | ||
raw_input | ||
else | ||
raise InvalidResponse, "Sorry, I couldn't understand that number. Please try again.", caller |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I know this isn't part of your change, but while we're here, do you think "I couldn't understand that number" is a normal sort of error message to return? I don't think I'd expect a validation message to use the first person. Maybe more like "The entered value is not a valid number. Please try again."?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok.
lib/smart_answer/money.rb
Outdated
def self.raw_input_to_number(raw_input) | ||
raw_input = raw_input.is_a?(Numeric) ? raw_input : raw_input.to_s.delete(',').gsub(/\s/, '') | ||
if raw_input.to_f.infinite? | ||
raise InvalidResponse, "Sorry, that number is too big. Please try again.", caller |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still find it a bit untidy how our validation logic is tangled up in the casting logic.
I basically expect these three separate steps to happen:
- Attempt to coerce the raw input into a number
- Validate the number (and raise if appropriate)
- Cast the number to a
BigDecimal
I'd expect these three steps to be represented at a top-level function (probably initialize
), and to each have a single line of code, calling to methods if required. This way you get proper separation of concerns.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alecgibson ok.
lib/smart_answer/money.rb
Outdated
@value = BigDecimal.new(raw_input.to_s) | ||
end | ||
number = self.class.raw_input_to_number(raw_input) | ||
@value = BigDecimal.new(number.to_s) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we call .to_s
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alecgibson that should be useless indeed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alecgibson indeed without the .to_s
call it is returning ArgumentError: can't omit precision for a Float.
. It probably guesses the precision needed if the argument is a String. That's a little bit weird. Anyway I would leave it as it is for moment, if it is ok for you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh, weird. Yeah fine to leave then.
30b431d
to
f13c906
Compare
f13c906
to
88bb0e1
Compare
lib/smart_answer/money.rb
Outdated
@value = BigDecimal.new(raw_input.to_s) | ||
end | ||
input = self.class.parse(raw_input) | ||
number = self.class.valid?(input) && input |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why bother assigning number
? Given that valid?
raises, couldn't you just do something like:
input = self.class.parse(raw_input)
self.class.validate(input)
@value = BigDecimal.new(input.to_s)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
88bb0e1
to
7472fe5
Compare
7472fe5
to
5ee0737
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! 🚀
Raise an InvalidResponse error if the user tries to insert numbers too big to be represented in a Float variable.
5ee0737
to
1913b57
Compare
Trello
https://trello.com/c/y3bOwPKj/293-2-floatdomainerror-infinity-in-multiple-smart-answers
Problem
We noticed some
Float::Infinity
errors were happening in production, leading to a500 Error
. That’s probably due to some users trying to input very big numbers.Staging
https://smart-answers-preview-pr-3189.herokuapp.com/am-i-getting-minimum-wage/y/current_payment/not_an_apprentice/111/23/2.0/12.0/2.4132314123431646e+273/111,1111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222225555555555555555555555555555555555555555555555555555555555222222222222222222222211111111111111111133333333333333333333555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555444444444444444444444444444448888888888888888888888888888888888888888888888888888888888887777777777777778888888888888889999999999.0
Production
https://www.gov.uk/am-i-getting-minimum-wage/y/current_payment/not_an_apprentice/111/23/2.0/12.0/2.4132314123431646e+273/1111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222225555555555555555555555555555555555555555555555555555555555222222222222222222222211111111111111111133333333333333333333555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555444444444444444444444444444448888888888888888888888888888888888888888888888888888888888887777777777777778888888888888889999999999.0
Solution
Raise an
InvalidResponse
error if the value for money is toobig to be represented in a
Float
variable.