How to correct Ruby countdown timer? - ruby

import time
def countdown(time_sec):
while time_sec:
mins, secs = divmod(time_sec, 60)
timeformat = "{:02d}:{:02d}".format(mins, secs)
print(timeformat, end='\r')
time.sleep(1)
time_sec -= 1
print("Time ended.")
This Python codes you see above work smootly and count down from given time_sec.
This codes also clean screen every second. I would like to write codes which are
work exactly same in Ruby.
#timer.rb(Ruby codes) here
def countdown time_sec
while time_sec
mins, secs = time_sec.divmod(60)[0], time_sec.divmod(60)[1]
timeformat = "%02d:%02d" % [mins, secs]
puts "#{timeformat}\r"
sleep(1)
time_sec -= 1
end
puts "Time is ended."
end
This Ruby codes you see above work in wrong way. Firstly seconds are printed one after the other. But I would like to update single line like above Python codes do. Secondly when this Ruby codes is run and countdown reached 00:00, It keeps counting down from -01:59. How can I correct this code?

You are using puts, which adds a line ending and messes up the\r.
Futhermore, the Python code runs until time_sec is zero, which evaluates as false and causes the loop to stop. In Ruby zero does not evaluate as
false.
def countdown(time_sec)
time_sec.downto(0) do |t|
print "%02d:%02d\r" % t.divmod(60)
sleep 1
end
end

Related

Performing action every few seconds, no matter what happens in running loop

I have a little problem. I want to iterate loop every time there is new line from socket, so I use this loop:
until #socket.eof? do
#something
end
And I also want to perform one action every 10 seconds, so I did something like this:
until #socket.eof? do
do_something
if time_last+10 < Time.now
time_last = Time.now
do_something_else
end
end
And my problem is that, that do_something_else action is performed only after there is a new line on socket. There is no problem if there is 100 lines / second. But if there is no new lines, nothing happens.
And if I get this whole if outside of until loop it woks only once on program start. Can I run two loops at once somehow?
You don't have to block on the socket. In this loop, it tries to read for 0.2 seconds and then checks if a task has to be launched. $guest is an array of sockets.
loop do
if task_time + 0.1 < now = Time.now.to_f
Task.execute(task_time = now)
end
next unless ready = select($guest, nil, nil, 0.2)
now = Time.now.to_f
for sock in ready[0]
// [...]
end
end
This is an extract of some legacy code of mine, but it sure works:
https://github.com/rosedomini/MystMUD/blob/master/MystMUD/myst/myst/mud.rb
You can schedule tasks, the best way is to use a scheduler.
You can do this using OpenWFEru and then:
scheduler.schedule_every('10s') { do_something }
See more in the link below:
https://www.igvita.com/2007/03/29/scheduling-tasks-in-ruby-rails/

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

What does fake_time += 60 in rspec mean?

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.

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