I created Float class in lib folder:
class Float
def precision(p = 2)
# Make sure the precision level is actually an integer and > 0
raise ArgumentError, "#{p} is an invalid precision level. Valid ranges are integers > 0." unless p.class == Fixnum or p < 0
# Special case for 0 precision so it returns a Fixnum and thus doesn't have a trailing .0
return self.round if p == 0
# Standard case
(self * 10**p).round.to_f / 10**p
end
end
In rspec tests, works. But when the application is running, this error is raised:
undefined method `precision' for 5128.5:Float
How to make this override work?
Ruby already implements a round method for Float. There is no need for your implementation.
0.12345.round(2) # => 0.12
0.12345.round(3) # => 0.123
I think this should do it.
module MyFloatMod
def precision(p = 2)
# Make sure the precision level is actually an integer and > 0
raise ArgumentError, "#{p} is an invalid precision level. Valid ranges are integers > 0." unless p.class == Fixnum or p < 0
# Special case for 0 precision so it returns a Fixnum and thus doesn't have a trailing .0
return self.round if p == 0
# Standard case
(self * 10**p).round.to_f / 10**p
end
end
Float.send(:include, MyFloatMod)
EDIT: Almost forgot you also need to make sure this all gets included somewhere during your app's start up.
Related
Let's say I have a method
def check_discrepancy(a, b)
if a < b-5 || a > b+5
return 'discrepancy between values!'
end
end
The if statement will be true if the difference between A and B is greater than 5, the code works as intended, but I want to know if there is a different/cleaner way to do it in Ruby
I want to check if number divided by 0 will rise ZeroDivisionError from below method
def progress_percent
(activity_reads.size / journey.cms_activity_ids.size.to_f)
rescue ZeroDivisionError
0
end
My specs are:
context 'when journey does not have activity' do
let!(:activity_read) { ['test', 'test'] }
let(:journey) { build(:journey, :without_activity) }
it 'raise the ZeroDivisionError' do
expect { call }.to raise_error(ZeroDivisionError)
end
end
When I put binding.pry to progress_percent method I'm getting what I was expected so activity_read = 2 and journey.cms_activity_ids.size.to_f = 0 but in my test I've got an error:
Failure/Error: expect { call }.to raise_error(ZeroDivisionError)
expected ZeroDivisionError but nothing was raised
EDIT:
It's a part of bigger class
def call
Progres.update(
user_id: current_user.id,
percent_progress: progress_percent,
)
end
private
def progress_percent
(activity_reads.size / journey.cms_activity_ids.size.to_f)
rescue ZeroDivisionError
0
end
why not an early return instead?:
def progress_percent
return 0 unless journey.cms_activity_ids.size > 0
activity_reads.size / journey.cms_activity_ids.size
end
also, you don't need to cast to float or integer, the "assoc_ids" AR method will return an object that responds to :size
As Spickermann says, if you're handling the error, you don't test for it.
What you should do is confirm that the method returns 0 in that scenario.
Having said that... you are converting journey.cms_activity_ids.size to a float so that won't raise an error anyway. Dividing by 0.0 will give a result of Infinity instead of raising an error.
You can test for infinity with...
expect(subject.send(:progress_percent)).to eq BigDecimal('Infinity')
If the non-zero numerator is negative, and the denominator is zero, you'll get negative infinity. Test for that with...
expect(subject.send(:progress_percent)).to eq BigDecimal('-Infinity')
Note, however, that if you have a "negative zero", it's slightly different: -1.0 / -0.0 returns Infinity, not negative Infinity.
If the numerator and denominator are both zero and at least one is a float, you will get NaN returned. Test for that with...
expect(subject.send(:progress_percent).nan?).to be true
NaN never equals anything, not even another NaN
It's not raised by call, because you catch/rescue it.
expect(call).to eq(0)
Should pass.
I am working on a Ruby challenge for work, and I am unable to create a working method. Every method I try keeps returning "nil".
Here is the question:
Create a method that passes an integer argument to a single parameter. If the integer is greater than 0 print the numbers from the integer to 0. If the number is less than 0 simply print the integer. Use a for loop, while loop, or unless loop to print the range of numbers from the integer to 0.
For example:
sample(4)
output = 3, 2, 1
sample(-1)
output = -1
Here is the code I tried to use
def countdown(n)
loop do
n -= 1
print "#{n}"
break if n <= 0
end
countdown(4)
A method returns the results of the last statement executed. Your loop is returning nil:
def countdown(n)
x = loop do
n -= 1
puts "#{n}"
break if n <= 0
end
x
end
countdown(4)
3
2
1
0
=> nil
Now let's return something:
def countdown(n)
loop do
puts "#{n}"
break if n <= 0
n -= 1
end
"okay we're done"
end
countdown(4)
4
3
2
1
0
=> "okay we're done"
It's not necessary to print inside the function and also outside it - this will cause duplicate printing. Also you are calling print on the positive numbers but not calling print if they are negative or zero. Additionally, you are using print "#{n}" which is the same as print n.
As far as the title of your question goes - "keeps returning nil" - you can change your approach a bit to do the print calls outside the function.
def countdown(n)
n <= 1 ? [n] : (n-1).downto(1).to_a
end
print countdown(n).join(", ")
Try this:
def countdown(n)
n.downto(n > 0 ? 0 : n) { |i| puts i }
end
countdown(4)
# 4
# 3
# 2
# 1
# 0
countdown(-4)
# -4
countdown(0)
# 0
You didn't mention what is to be done if the argument is zero. I've assumed it's treated as a positive or negative number.
Admittedly, this is cheating, as it does not "Use a for loop, while loop, or unless loop...", but Ruby is designed mainly to use iterators and blocks. This, or something like it, is the way to go. I just had a thought: treat that as a suggestion, not a requirement.
By the way, among loops, Kernel#loop was not mentioned, which is strange, as it is quite useful. As for "for loops", who uses them? I never have, not even once.
If you must use a loop, you could do the following.
def countdown(n)
while n > 0
puts n
n-= 1
end
puts n
end
countdown(4)
# 4
# 3
# 2
# 1
# 0
countdown(-4)
# -4
countdown(0)
# 0
You may try this...
def sample (a)
if a > 0
(1..a).to_a.reverse
else
a
end
end
Hope this will work for you
I'm trying to run a few calculations in order to represent a particular price (ie 20.30).
I have tried the Float#round method, but the instance variables holding these values eventually start representing numbers that look like 24.43418 after a few calculations.
This is just a method I created to turn a users input into a percentage
class Fixnum
def percentage
self.to_f / 100
end
end
The #bankroll_amount and #risk_amount values should be evaluating to two decimal points
class Client
def initialize(bankroll, unit)
#bankroll_amount = bankroll.to_i.round(2)
#unit_percentage = unit.to_i.percentage
default_risk_amount.round(2)
evaluate_default_unit!.round(2)
end
def default_risk_amount
#risk_amount = #unit_percentage * #bankroll_amount
#risk_amount.round(2)
end
# simulates what an updated bankroll looks like after a win based on clients "unit" amount
def risk_win
#bankroll_amount = #bankroll_amount + #risk_amount
#bankroll_amount.round(2)
evaluate_default_unit!.round(2)
end
# simulates what a clients updated bankroll looks like after a loss based on clients "unit" amount
def risk_loss
#bankroll_amount = #bankroll_amount - #risk_amount
evaluate_default_unit!
end
def evaluate_default_unit!
#risk_amount = #unit_percentage * #bankroll_amount.round(2)
end
end
Im not sure if this has anything to do with the fact that I am initializing these instance variables or not, but the #risk_amount returns the correct two decimal value, but when I return the object, the instance variable inside has running decimals.
c = Client.new 2000, 1
<Client:0x000001018956a0 #bankroll_amount=2000.0, #unit_percentage=0.01, #risk_amount=20.0>
c.risk_win
=> 20.2
When I run c.risk_win enough, it eventually returns
c
<Client:0x000001018956a0 #bankroll_amount=2440.3802, #unit_percentage=0.01, #risk_amount=24.4038>
This is one way to show only two decimal points.
price = 20.21340404
"%.2f" % price
# => 20.23
Also see RAILS number_to_currency helpers ActionView::Helpers::NumberHelper
http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#method-i-number_to_currency
Use number_with_precision to format the display of your floating point numbers to 2 decimal places:
number_with_precision(#bankroll_amount.to_f, precision: 2)
Usage in rails console:
[1] pry(main)> include ActionView::Helpers::NumberHelper
=> Object
[2] pry(main)> number_with_precision(2440.3802, precision: 2)
=> "2440.38"
I have a calculation that generates what appears to be the Float 22.23, and a literal 22.23 like so:
some_object.total => 22.23
some_object.total.class => Float
22.23 => 22.23
22.23.class => Float
But for some reason, the following is false:
some_object.total == 22.23 ? true : false
Wacky, right?
Is there some kind of precision mechanism being used that maybe isn't completely transparent through the some_object.total call?
Floating-point numbers cannot precisely represent all decimal numbers within their range. For example, 0.9 is not exactly 0.9, it's a number really close to 0.9 that winds up being printed as it in most cases. As you do floating-point calculations, these errors can accumulate and you wind up with something very close to the right number but not exactly equal to it. For example, 0.3 * 3 == 0.9 will return false. This is the case in every computer language you will ever use — it's just how binary floating-point math works. See, for example, this question about Haskell.
To test for floating point equality, you generally want to test whether the number is within some tiny range of the target. So, for example:
def float_equal(a, b)
if a + 0.00001 > b and a - 0.00001 < b
true
else
false
end
end
You can also use the BigDecimal class in Ruby to represent arbitrary decimal numbers.
If this is a test case, you can use assert_in_delta:
def test_some_object_total_is_calculated_correctly
assert_in_delta 22.23, some_object.total, 0.01
end
Float#to_s and Float#inspect round. Try "%.30f" % some_object.total and you will see that it's not quite 22.23.
there is something else going on here. this is from a 1.8.7 irb
irb(main):001:0> class Test
irb(main):002:1> attr_accessor :thing
irb(main):003:1> end
=> nil
irb(main):004:0> t = Test.new
=> #<Test:0x480ab78>
irb(main):005:0> t.thing = 22.5
=> 22.5
irb(main):006:0> t.thing == 22.5
=> true