I would like to run a block for 60 seconds. What I have come up with thus far does not break out of the block as desired.
#start_time = Time.now
stream_some_data do |x|
# .. do something ...
break if Time.now == #start_time + 60
end
Ruby's stdlib already has a Timeout module for this:
begin
require "timeout"
Timeout::timeout(60) do
# all of the things...
end
rescue Timeout::Error => e
end
Since you're unlikely to get to that line at exactly 60 seconds past the start, try:
break if Time.now > #start_time + 60
Related
I am trying to solve the timer problem from TestFirst Ruby.
I got the first two criteria correctly but the third one when tested for time = 12 seconds does not work. It does not look like Ruby is reading the time = 12 secs.
The codes are pretty lazy and not optimized, apparently. Also I did tried out the padded method but the test never worked. I had my padded method defined as
def padded(num)
if num<=9
return "0"<<num.to_s
else
return num.to_s
end
end
It would be great if someone can show me how to set that up correctly since that might have been the problems.
Here are my complete codes:
class Timer
#initialization of seconds
def seconds
return 0
end
#seconds= method
def seconds=(time)
#seconds = time_string(time)
end
#time_string method
def time_string(time=0)
#format of hour:minute:second
#minute must be less than 59 (or 59*60 seconds), otherwise it will convert to hour
minute = time/60 #note that this is integer math, so it will take the minute and not the remainder
hour = minute/60
remainder_seconds = time%60
if time<=9
return "00:00:0" << time.to_s
elsif time>9 && time<=60
return "00:00:" << time.to_s
elsif time>60 && time<=9*60 #9 minutes and greater than 1 min
#ensuring double XX seconds or 0X seconds (this would be easier to use the padded method)
if remainder_seconds >9
remainder_seconds_sd = remainder_seconds.to_s
else
remainder_seconds_sd = "0" << remainder_seconds.to_s
end
return "00:0" << minute.to_s << ":" << remainder_seconds_sd
end
end
end
RSpec below:
require '09_timer'
describe "Timer" do
before(:each) do
#timer = Timer.new
end
it "should initialize to 0 seconds" do
#timer.seconds.should == 0
end
describe 'time_string' do
it "should display 0 seconds as 00:00:00" do
#timer.seconds = 0
#timer.time_string.should == "00:00:00"
end
it "should display 12 seconds as 00:00:12" do
#timer.seconds = 12
#timer.time_string.should == "00:00:12"
end
it "should display 66 seconds as 00:01:06" do
#timer.seconds = 66
#timer.time_string.should == "00:01:06"
end
it "should display 4000 seconds as 01:06:40" do
#timer.seconds = 4000
#timer.time_string.should == "01:06:40"
end
end
# One way to implement the Timer is with a helper method.
# Uncomment these specs if you want to test-drive that
# method, then call that method from inside of time_string.
#
=begin
describe 'padded' do
it 'pads zero' do
#timer.padded(0).should == '00'
end
it 'pads one' do
#timer.padded(1).should == '01'
end
it "doesn't pad a two-digit number" do
#timer.padded(12).should == '12'
end
end
=end
end
The problem with your tests and Timer is that, in your tests you are setting the value of #timer.seconds, but the Timer#time_string does not rely on the #seconds variable set. Your time_string method is implemented the way it accepts the amount of seconds as an argument, not an attribute of Timer.
Try changing your tests as follows:
describe "Timer" do
# rest of your code
describe 'time_string' do
it "should display 0 seconds as 00:00:00" do
#timer.time_string(0).should == "00:00:00"
end
it "should display 12 seconds as 00:00:12" do
#timer.time_string(12).should == "00:00:12"
end
it "should display 66 seconds as 00:01:06" do
#timer.time_string(66).should == "00:01:06"
end
it "should display 4000 seconds as 01:06:40" do
#timer.time_string(4000).should == "01:06:40"
end
end
end
You might be wondering okay, but why the first test - 00:00:00 - did work in first place?. Well, this is, because your time_string method argument defaults to 0:
def time_string(time=0)
# Rest of the code
end
and because you were not passing any other value, the 0 has been used.
If you have any questions - I'm happy to help!
Good luck!
Edit
If you want to make it the other way around - make the class to work for your tests, change your Timer class:
class Timer
def initialize
#seconds = 0
end
def seconds
#seconds
end
def seconds=(time)
#seconds = time
end
def time_string
#format of hour:minute:second
#minute must be less than 59 (or 59*60 seconds), otherwise it will convert to hour
minute = #seconds/60 #note that this is integer math, so it will take the minute and not the remainder
hour = minute/60
remainder_seconds = #seconds%60
if #seconds<=9
return "00:00:0" << #seconds.to_s
elsif #seconds>9 && #seconds<=60
return "00:00:" << #seconds.to_s
elsif #seconds>60 && #seconds<=9*60 #9 minutes and greater than 1 min
#ensuring double XX seconds or 0X seconds (this would be easier to use the padded method)
if remainder_seconds >9
remainder_seconds_sd = remainder_seconds.to_s
else
remainder_seconds_sd = "0" << remainder_seconds.to_s
end
return "00:0" << minute.to_s << ":" << remainder_seconds_sd
end
end
end
We have added initialize method, we have changed def seconds=(time) method, and we have changed all occurrences of time in your time_string method.
If that works for you, consider posting the code to https://codereview.stackexchange.com/. There is a lot in the code to improve, and codereview is a great place to ask for help!
A cleaner version:
class Timer
attr_accessor :seconds
def initialize
#seconds = 0
end
def time_string
seconds = #seconds % 60
minutes = (#seconds / 60) % 60
hours = #seconds / (60**2)
"#{padded(hours)}:#{padded(minutes)}:#{padded(seconds)}"
end
def padded(num)
return '0' + num.to_s if num < 10
return num.to_s if num >= 10
end
end
I have the following code to block until all threads have finished (Gist):
ThreadsWait.all_waits(*threads)
What's the simplest way to set a timeout here, ie kill any threads if they are still running after e.g. 3 seconds?
Thread#join accepts an argument after which it will time out. Try this, for example:
5.times.map do |i|
Thread.new do
1_000_000_000.times { |i| i } # takes more than a second
puts "Finished" # will never print
end
end.each { |t| t.join(1) } # times out after a second
p 'stuff I want to execute after finishing the threads' # will print
If you have some things you want to execute before joining, you can do:
5.times.map do |i|
Thread.new do
1_000_000_000.times { |i| i } # takes more than a second
puts "Finished" # will never print
end
end.each do |thread|
puts 'Stuff I want to do before join' # Will print, multiple times
thread.join(1)
end
Please see the following code (taken from Learning Ruby book):
def timer(start)
puts "Minutes: " + start.to_s
start_time = Time.now
puts start_time.strftime("Start time: %I:%M:%S: %p")
start.downto(1) { |i| sleep 60 }
end_time = Time.now
print end_time.strftime("Elapsed time: %I:%M:%S: %p\n")
end
timer 10
Why would there be a need to change the start variable into a string on the puts line? Couldn't I, for example, simply put in puts "Minutes: #{start}"?
Also, the start.downto(1) line: Is the block {|i| sleep 60} specifying how many seconds a minute should be?
Yes, you can also say:
puts "Mintues: #{start}"
It's one of many nice Ruby choices. :) In this case, it doesn't make much difference.
Regarding the loop:
start.downto(1) { |i| sleep 60 }
Yes, this is counting minutes down to 1 and each time is sleeping 60 seconds. So it will sleep for start minutes. If start isn't too large, you could just use sleep 60*start.
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
How do you tell a Ruby program to wait an arbitrary amount of time before moving on to the next line of code?
Like this:
sleep(num_secs)
The num_secs value can be an integer or float.
Also, if you're writing this within a Rails app, or have included the ActiveSupport library in your project, you can construct longer intervals using the following convenience syntax:
sleep(4.minutes)
# or, even longer...
sleep(2.hours); sleep(3.days) # etc., etc.
# or shorter
sleep(0.5) # half a second
Use sleep like so:
sleep 2
That'll sleep for 2 seconds.
Be careful to give an argument. If you just run sleep, the process will sleep forever. (This is useful when you want a thread to sleep until it's woken.)
I find until very useful with sleep. example:
> time = Time.now
> sleep 2.seconds until Time.now > time + 10.seconds # breaks when true
> # or
> sleep 2 and puts 'still sleeping' until Time.now > time + 10
> # or
> sleep 1.seconds until !req.loading # suggested by ohsully
Like this
sleep(no_of_seconds)
Or you may pass other possible arguments like:
sleep(5.seconds)
sleep(5.minutes)
sleep(5.hours)
sleep(5.days)
Implementation of seconds/minutes/hours, which are rails methods. Note that implicit returns aren't needed, but they look cleaner, so I prefer them. I'm not sure Rails even has .days or if it goes further, but these are the ones I need.
class Integer
def seconds
return self
end
def minutes
return self * 60
end
def hours
return self * 3600
end
def days
return self * 86400
end
end
After this, you can do:
sleep 5.seconds to sleep for 5 seconds. You can do sleep 5.minutes to sleep for 5 min. You can do sleep 5.hours to sleep for 5 hours. And finally, you can do sleep 5.days to sleep for 5 days... You can add any method that return the value of self * (amount of seconds in that timeframe).
As an exercise, try implementing it for months!
sleep 6 will sleep for 6 seconds. For a longer duration, you can also use sleep(6.minutes) or sleep(6.hours).
This is an example of using sleep with sidekiq
require 'sidekiq'
class PlainOldRuby
include Sidekiq::Worker
def perform(how_hard="super hard", how_long=10)
sleep how_long
puts "Workin' #{how_hard}"
end
end
sleep for 10 seconds and print out "Working super hard" .