What does fake_time += 60 in rspec mean? - ruby

I'm going through an rspec tutorial on using "Time." Can someone explain what the following code means?
it "takes exactly 1 second to run a block that sleeps for 1 second (with stubs)" do
fake_time = #eleven_am
Time.stub(:now) { fake_time }
elapsed_time = measure do
fake_time += 60 # adds one minute to fake_time
end
elapsed_time.should == 60
end
I get it's suppose to take 60 seconds, but technically I'm just adding 60 seconds to the time variable fake_time, which should be instantaneous. Why does it take 60 seconds?
And here's the code for the measure function, which I wrote. It's suppose to measure how long it takes to run a block of code.
def measure
m1 = Time.now
num.times { yield }
m2 = Time.now
m2 - m1
end

This test is using basic stubbing. It doesn't make the method take extra time - it simply modifies the 'stubbed' value that would be returned by Time.now. This is useful for testing to ensure that after a certain amount of time 'would have elapsed', the expected values will be returned.

Related

Benchmarking Ruby Code

I'm doing the following Ruby Tutorial https://rubymonk.com/learning/books/4-ruby-primer-ascent/chapters/50-debugging/lessons/124-benchmarking_ruby_code. One of the exercises asks me to:
use Ruby's super-awesome blocks to create a method which takes in a
block, executes it, and returns the time it took.
The exercise looks like this:
def benchmark
# your code here!
end
time_taken = benchmark do
sleep 0.1
end
puts "Time taken #{time_taken}"
there is a hint (Need a hint?) below the exercise:
Ruby Blocks - Introduction to Blocks in Ruby (Ruby Primer)
and i did so:
def benchmark(time)
begin_time = Time.now
end_time = Time.now
time.benchmark {|time| yield time}
end
time_taken = benchmark do
sleep 0.1
end
puts "Time taken #{time_taken}
but received an error.
i am interested in: why is local variable - 'time_taken', suggested without representing an element after 'do'? or is it not necessary? Can anyone tell me how to write code to get the positive result.
You should do it much easier:
def benchmark
begin_time = Time.now
yield
end_time = Time.now
end_time - begin_time
end
time_taken = benchmark do
sleep 0.1
end
puts "Time taken #{time_taken}"
First you collect the time and store in in variable begin_time, then yield - so run the block, then collect the end time. Return the difference. That's it.
That's pretty far off, and not really at all salvagible.
Your benchmark method should look like this pseudo code:
def benchmark
let begin_time -> current time
execute the block
let end_time -> current_time
return endtime - begintime
end
As far as executing the block being passed in, there is no time.benchmark method, I'm not sure where that came from, and you do not need to pass anything into the block. You want a single, simple yield.

Ruby/Rspec Performance Monitor

So I'm working on Rspec problems, and this is the last one I have left. For whatever reason, it's been much harder than all of the others. The three Rspec tests that are in question are as folows:
it "runs a block N times" do
n = 0
measure(4) do
n += 1
end
n.should == 4
end
it "returns the average time, not the total time, when running multiple times" do
run_times = [8,6,5,7]
fake_time = #eleven_am
Time.stub(:now) { fake_time }
average_time = measure(4) do
fake_time += run_times.pop
end
average_time.should == 6.5
end
it "returns the average time when running a random number of times for random lengths of time" do
fake_time = #eleven_am
Time.stub(:now) { fake_time }
number_of_times = rand(10) + 2
average_time = measure(number_of_times) do
delay = rand(10)
fake_time += delay
end
average_time.should == (fake_time - #eleven_am).to_f/number_of_times
end
And my code is as follows:
require 'time'
def measure(pass = 0)
start_time = Time.now
if pass == 0
yield
else
pass.times {|current| result = yield(current)}
end
Time.now - start_time
end
(The if/else is present, as an earlier test requires that the code takes one second to execute a program that sleeps for 1 second. In that case, pass would be 0, so the program would jump straight to the yield.)
Full Rspec here
Now, the code DOES pass the 'it "runs a block N times" do' test, but I feel that the way I have it set up prevents the other two tests from being able to pass. (At the same time, a simple yield won't allow it to pass, because it will get an error from trying to + 1 to nil)
I'm not looking for a copy/paste answer, but moreso whether or not I'm on the right track (Or if my pass.times should be reworked.)
If you have any examples that may be able to lead me in the right direction, I'd be more than happy to see them!
You say you "feel" it won't let the other tests pass, do you know this? I just ran it, and here's the results:
Performance Monitor
takes about 0 seconds to run an empty block
takes exactly 0 seconds to run an empty block (with stubs)
takes about 1 second to run a block that sleeps for 1 second
takes exactly 1 second to run a block that sleeps for 1 second (with stubs)
runs a block N times
returns the average time, not the total time, when running multiple times (FAILED - 1)
So the last spec fails. Looks like it's just returning the wrong value, it doesn't look at whether there were multiple passes. So updating that:
require 'time'
def measure(pass = 0)
start_time = Time.now
if pass == 0
yield
else
pass.times {|current| result = yield(current)}
end
(Time.now - start_time) / (pass == 0 ? 1 : pass)
end
Now running the specs shows me:
(in /Users/nick/learn_ruby)
Performance Monitor
takes about 0 seconds to run an empty block
takes exactly 0 seconds to run an empty block (with stubs)
takes about 1 second to run a block that sleeps for 1 second
takes exactly 1 second to run a block that sleeps for 1 second (with stubs)
runs a block N times
returns the average time, not the total time, when running multiple times
returns the average time when running a random number of times for random lengths of time
Finished in 1.01 seconds
7 examples, 0 failures
Great part about testing first is you can just find out if what you are doing is wrong, and then fix it.
def measure(pass = 1)
start_time = Time.now
pass.times {yield}
(Time.now - start_time) / (pass)
end
for any future passerbys, this could be written more efficiently like so, using a default value of 1 for pass

ping function every x amount seconds

How would I do a specific task every x amount of seconds in ruby? I've tried using Time.now.to_i for epoch then once a Time.now_i hits that task second it executes, but I have not successfuly done this, can someone show me a small example on how to execute a function every x amount of seconds?
Attempt:
def interval(timeout,function,*data)
now = Time.now.to_i
tasktime = Time.now.to_i + timeout
taskfunction = function
taskdata = data
end
I stopped the code there because I do not know how/what to do next in ruby, so what it should do for example if someone can generate a code that can do something like this example,
def say(word)
puts word
end
If you set a interval for the function would be say, the data would be the "word" then it would execute that function every x amount of seconds
If you simply sleep for a constant amount of time as suggested in other answers, the error will contaminate as it keeps running, and will not be accurate. In fact, each iteration would take longer than the given interval.
The answer shown below adjusts the lag each time per iteration.
module Kernel
def tick_every sec, &pr
Thread.new do loop do
pr.call
t = Time.now.to_f
frac = t.modulo(sec.to_f)
sleep(sec - frac)
end end
end
end
thread = tick_every(2) do
puts "foo"
end
...
some_other_tasks
...
thread.kill
You can use Kernel#sleep method for the same.
Here is the post
Ruby sleep or delay less than a second?
Tell Ruby Program to Wait some amount of time
This method would puts the word every 2 seconds endless, synchronously (means other ruby code has to wait until this execution is finished (..endless..:)).
def say(word)
while true do
t = Time.now.to_f
puts word
frac = t.modulo(2.to_f)
sleep(2 - frac)
end
end

Access a variable from within a method when being passed a block that contains the variable

From what I understand every time it runs though run_times it pops off one of the run_times values and adds it to fake_time. And with my measure method I'm supposed to gather those and get the average. But I can't seem to get the values from within the method.
It needs to pass the following rspec:
it "returns the average time, not the total time, when running multiple times" do
run_times = [8,6,5,7]
fake_time = #eleven_am
Time.stub(:now) { fake_time }
average_time = measure(4) do
fake_time += run_times.pop
end
average_time.should == 6.5
end
Here's my code:
def measure (n = 1)
if n > 1
n.times { yield }
else
start = Time.now
yield
elapsed_time = Time.now - start
elapsed_time
end
end
Thank you in advance! I've been stuck on this for a while and can't seem to get anything working.
n.times returns n, does not it? that is, measure returns 4 if n is 4, so average_time is 4, not 6.5.

how to invoke a method for every second in ruby

I wanted to create a stopwatch program in ruby so I googled it and found this SO Q.
But over there, the author calls the tick function with 1000xxx.times. I wanted to know how I can do it using something like (every second).times or for each increment of second do call the tick function.
This function:
def every_so_many_seconds(seconds)
last_tick = Time.now
loop do
sleep 0.1
if Time.now - last_tick >= seconds
last_tick += seconds
yield
end
end
end
When used like this:
every_so_many_seconds(1) do
p Time.now
end
Results in this:
# => 2012-09-20 16:43:35 -0700
# => 2012-09-20 16:43:36 -0700
# => 2012-09-20 16:43:37 -0700
The trick is to sleep for less than a second. That helps to keep you from losing ticks. Note that you cannot guarantee you'll never lose a tick. That's because the operating system cannot guarantee that your unprivileged program gets processor time when it wants it.
Therefore, make sure your clock code does not depend on the block getting called every second. For example, this would be bad:
every_so_many_seconds(1) do
#time += 1
display_time(#time)
end
This would be fine:
every_so_many_seconds(1) do
display_time(Time.now)
end
Thread.new do
while true do
puts Time.now # or call tick function
sleep 1
end
end

Resources