resque worker error when forking [Ruby, Redis] - ruby

I'm having a difficulty with processing resque tasks which were enqueued.
The whole enqueuing part goes well - I can see it in Redis and also Resque.info shows the pending tasks number incrementing as it should.
If I run the perform method of the Job class explicitly - everything works fine.
Once the worker come alive - all the tasks fail.
This is the Job class:
class TestJob
#queue = :test1_queue
def self.perform(str)
begin
f = open('/tmp/test.txt', 'a')
f.write("#{Time.now.to_s} #{str} in self.perform\n")
rescue Exception => e
f.write("#{Time.now.to_s} #{str} in self.perform\n#{e.message}\n#{e.backtrace}\n")
ensure
f.close
end
end
end
The resque.rb initializer:
require 'resque'
require 'redis'
Dir['../../jobs'].each { |file| require file }
Resque.redis = $resque_redis
Resque.logger.level = Logger::WARN
Resque.after_fork do |_|
$resque_redis.client.reconnect
end
Redis initializer:
require 'redis'
$resque_redis = Redis.new(:host => REDIS_BITMAP_HOST, :port => REDIS_PORT, :db => 0, :timeout => 30)
config file to start the worker with god:
require_relative './common.rb'
watch_resque_process('test1', 1)
God definitions:
$home_dir = ENV["HOME"]
$rack_env = ENV["ETL_ENV"] || "development"
def create_deafult_monitoring_scheme(watch)
# Restart if memory is above 150 Megabytes or CPU is above 50% for 5 consecutive intervals
watch.restart_if do |restart|
restart.condition(:memory_usage) do |c|
c.above = 150.megabytes
c.times = [3, 5] # 3 out of 5 intervals
end
restart.condition(:cpu_usage) do |c|
c.above = 50.percent
c.times = 5
end
end
# The :flapping condition guards against the edge case wherein god rapidly starts or restarts your application.
# If this watch is started or restarted five times withing 5 minutes, then unmonitor it for 10 minutes.
# After 10 minutes, monitor it again to see if it was just a temporary problem; if the process is seen to be flapping five times within two hours, then give up completely.
watch.lifecycle do |on|
on.condition(:flapping) do |c|
c.to_state = [:start, :restart]
c.times = 5
c.within = 5.minute
c.transition = :unmonitored
c.retry_in = 10.minute
c.retry_times = 5
c.retry_within = 30.minute
end
end
end
def watch_resque_process(resque_process_name, worker_count=8)
God.watch do |w|
w.name = "resque_work-#{resque_process_name}"
w.start = "cd #{$home_dir}/rtb-etl && COUNT=#{worker_count} QUEUE='#{resque_process_name}_queue' RACK_ENV=#{$rack_env} rake resque:workers"
w.interval = 30.seconds
w.log = File.join($home_dir, 'logs', 'resque', "resque_#{resque_process_name}.log")
w.err_log = File.join($home_dir, 'logs', 'resque', "resque_#{resque_process_name}.log")
w.env = { 'PIDFILE' => "#{$home_dir}/pids/#{w.name}.pid" }
# Check if the process is still up every 5 seconds
w.start_if do |start|
start.condition(:process_running) do |c|
c.interval = 5.seconds
c.running = false
end
end
create_deafult_monitoring_scheme(w)
end
end
def watch_rake_task(rake_task_name, interval=30.seconds)
God.watch do |w|
w.name = "rake_#{rake_task_name}"
# w.start = "cd #{$home_dir}/rtb-etl && RACK_ENV=#{$rack_env} bundle exec rake #{rake_task_name}"
w.start = "cd #{$home_dir}/rtb-etl && RACK_ENV=#{$rack_env} rake #{rake_task_name}"
w.interval = interval
w.log = File.join($home_dir, 'logs', 'resque', "rake_#{rake_task_name}.log")
w.err_log = File.join($home_dir, 'logs', 'resque', "rake_#{rake_task_name}.log")
w.env = { 'PIDFILE' => "#{$home_dir}/pids/#{w.name}.pid" }
# Check if the process is still up every 30 seconds
w.start_if do |start|
start.condition(:process_running) do |c|
c.interval = interval
c.running = false
end
end
create_deafult_monitoring_scheme(w)
end
end
when I run the follwoing:
irb(main):004:0> Resque.enqueue(TestJob, 'foo')
=> true
In order to check what went wrong, I run in irb the following:
Resque::Failure.all(0,20).each { |job|
puts "#{job["exception"]} #{job["backtrace"]}"
}
and get this result:
[{"failed_at"=>"2015/08/26 17:35:00 UTC",
"payload"=>{"class"=>"TestJob", "args"=>["foo"]},
"exception"=>"NoMethodError",
"error"=>"undefined method `client' for nil:NilClass",
"backtrace"=>[], "worker"=>"ip-172-31-11-211:5006:test1_queue",
"queue"=>"test1_queue"}]
Any ideas ?

Related

How do I pass in code to functions?

I have this piece of code that executes a series of commands and keep retrying until it reaches MAX_RETRIES. I don't want to repeat this over and over again for different commands. Is there an elegant way of doing so?
retries = 0
ex = true
MAX_RETRIES = 10
while(retries <= MAX_RETRIES and ex)
begin
#MY CODE HERE
ex = false
rescue
ex = true
end
retries = retries + 1
end
Something like this?
execute_with_retries do
#CODE HERE
end
execute_with_retries do
#DIFFERENT CODE HERE
end
Define a function and execute the block
def execute_with_retries
retries = 0
ex = true
max_retries = 10
while(retries <= max_retries and ex)
begin
yield
ex = false
rescue
ex = true
end
retries = retries + 1
end
end
execute_with_retries { puts "hello" }
execute_with_retries { puts 1/0 }
I renamed one of your variable and you can read here why.
It's also worth noting that something like this exists in Ruby and you can read about it here.
MAX_RETRIES = 10
def execute_with_retries(meth)
retries = 0
ex = true
while(retries <= MAX_RETRIES and ex)
begin
public_send(meth)
ex = false
rescue
ex = true
end
retries = retries + 1
end
retries
end
require 'time.h'
def meth
sleep(1)
puts "Time.now in m = #{Time.now}"
puts Time.now - #start < 5 ? "cat"/9 : "dog"
end
#start = Time.now
#=> 2017-12-30 13:39:20 -0800
execute_with_retries(:meth)
Time.now in m = 2017-12-30 13:39:21 -0800
Time.now in m = 2017-12-30 13:39:22 -0800
Time.now in m = 2017-12-30 13:39:23 -0800
Time.now in m = 2017-12-30 13:39:24 -0800
Time.now in m = 2017-12-30 13:39:25 -0800
"dog"
#=> 5

Ruby Watir: cannot launch browser in a thread in Linux

I'm trying to run this code in Red Hat Linux, and it won't launch a browser. The only way I can get it to work is if i ALSO launch a browser OUTSIDE of the thread, which makes no sense to me. Here is what I mean:
require 'watir-webdriver'
$alphabet = ["A", "B", "C"]
$alphabet.each do |z|
puts "pshaw"
Thread.new{
Thread.current["testPuts"] = "ohai " + z.to_s
Thread.current["myBrowser"] = Watir::Browser.new :ff
puts Thread.current["testPuts"] }
$browser = Watir::Browser.new :ff
end
the output is:
pshaw
(launches browser)
ohai A
(launches browser)
pshaw
(launches browser)
ohai B
(launches browser)
pshaw
(launches browser)
ohai C
(launches browser)
However, if I remove the browser launch that is outside of the thread, as so:
require 'watir-webdriver'
$alphabet = ["A", "B", "C"]
$alphabet.each do |z|
puts "pshaw"
Thread.new{
Thread.current["testPuts"] = "ohai " + z.to_s
Thread.current["myBrowser"] = Watir::Browser.new :ff
puts Thread.current["testPuts"] }
end
The output is:
pshaw
pshaw
pshaw
What is going on here? How do I fix this so that I can launch a browser inside a thread?
EDIT TO ADD:
The solution Justin Ko provided worked on the psedocode above, but it's not helping with my actual code:
require 'watir-webdriver'
require_relative 'Credentials'
require_relative 'ReportGenerator'
require_relative 'installPageLayouts'
require_relative 'PackageHandler'
Dir[(Dir.pwd.to_s + "/bmx*")].each {|file| require_relative file } #this includes all the files in the directory with names starting with bmx
module Runner
def self.runTestCases(orgType, *caseNumbers)
$testCaseArray = Array.new
caseNumbers.each do |thisCaseNum|
$testCaseArray << thisCaseNum
end
$allTestCaseResults = Array.new
$alphabet = ["A", "B", "C"]
#count = 0
#multiOrg = 0
#peOrg = 0
#eeOrg = 0
#threads = Array.new
$testCaseArray.each do |thisCase|
$alphabet[#count] = Thread.new {
puts "working one"
Thread.current["tBrowser"] = Watir::Browser.new :ff
puts "working two"
if ((thisCase.declareOrg().downcase == "multicurrency") || (thisCase.declareOrg().downcase == "mc"))
currentOrg = $multicurrencyOrgArray[#multiOrg]
#multiOrg += 1
elsif ((thisCase.declareOrg().downcase == "enterprise") || (thisCase.declareOrg().downcase == "ee"))
currentOrg = $eeOrgArray[#eeOrg]
#eeOrg += 1
else #default to single currency PE
currentOrg = $peOrgArray[#peOrg]
#peOrg += 1
end
setupOrg(currentOrg, thisCase.testCaseID, currentOrg.layoutDirectory)
runningTest = thisCase.actualTest()
if runningTest.crashed != "crashed" #changed this to read the attr_reader isntead of the deleted caseStatus method from TestCase.rb
cleanupOrg(thisCase.testCaseID, currentOrg.layoutDirectory)
end
#threads << Thread.current
}
#count += 1
end
#threads.each do |thisThread|
thisThread.join
end
writeReport($allTestCaseResults)
end
def self.setupOrg(thisOrg, caseID, layoutPath)
begin
thisOrg.logIn
pkg = PackageHandler.new
basicInstalled = "false"
counter = 0
until ((basicInstalled == "true") || (counter == 5))
pkg.basicInstaller()
if Thread.current["tBrowser"].text.include? "You have attempted to access a page"
thisOrg.logIn
else
basicInstalled = "true"
end
counter +=1
end
if !((caseID.include? "bmxb") || (caseID.include? "BMXB"))
moduleInstalled = "false"
counter2 = 0
until ((moduleInstalled == "true") || (counter == 5))
pkg.packageInstaller(caseID)
if Thread.current["tBrowser"].text.include? "You have attempted to access a page"
thisOrg.logIn
else
moduleInstalled = "true"
end
counter2 +=1
end
end
installPageLayouts(layoutPath)
rescue
$allTestCaseResults << TestCaseResult.new(caseID, caseID, 1, "SETUP FAILED!" + "<p>#{$!}</p><p>#{$#}</p>").hashEmUp
writeReport($allTestCaseResults)
end
end
def self.cleanupOrg(caseID, layoutPath)
begin
uninstallPageLayouts(layoutPath)
pkg = PackageHandler.new
pkg.packageUninstaller(caseID)
Thread.current["tBrowser"].close
rescue
$allTestCaseResults << TestCaseResult.new(caseID, caseID, 1, "CLEANUP FAILED!" + "<p>#{$!}</p><p>#{$#}</p>").hashEmUp
writeReport($allTestCaseResults)
end
end
end
The output it's generating is:
working one
working one
working one
It's not opening a browser or doing any of the subsequent code.
It looks like the code is having the problem mentioned in the Thread class documentation:
If we don't call thr.join before the main thread terminates, then all
other threads including thr will be killed.
Basically your main thread is finishing pretty instantaneously. However, the threads, which create browsers, take a lot longer than that. As result the threads get terminated before the browser opens.
By adding a long sleep at the end, you can see that your browsers can be opened by your code:
require 'watir-webdriver'
$chunkythread = ["A", "B", "C"]
$chunkythread.each do |z|
puts "pshaw"
Thread.new{
Thread.current["testwords"] = "ohai " + z.to_s
Thread.current["myBrowser"] = Watir::Browser.new :ff
puts Thread.current["testwords"] }
end
sleep(300)
However, for more reliability, you should join all the threads at the end:
require 'watir-webdriver'
threads = []
$chunkythread = ["A", "B", "C"]
$chunkythread.each do |z|
puts "pshaw"
threads << Thread.new{
Thread.current["testwords"] = "ohai " + z.to_s
Thread.current["myBrowser"] = Watir::Browser.new :ff
puts Thread.current["testwords"] }
end
threads.each { |thr| thr.join }
For the actual code example, putting #threads << Thread.current will not work. The join will be evaluating like #threads is empty. You could try doing the following:
$testCaseArray.each do |thisCase|
#threads << Thread.new {
puts "working one"
Thread.current["tBrowser"] = Watir::Browser.new :ff
# Do your other thread stuff
}
$alphabet[#count] = #threads.last
#count += 1
end
#threads.each do |thisThread|
thisThread.join
end
Note that I am not sure why you want to store the threads in $alphabet. I put in the $alphabet[#count] = #threads.last, but could be removed if not in use.
I uninstalled Watir 5.0.0 and installed Watir 4.0.2, and now it works fine.

Ruby script error

I've got a ruby script
#!/usr/bin/ruby
require 'rubygems'
require 'mechanize'
require 'nokogiri'
require 'highline/import'
require 'stringio'
#Change based on Semester
$term = '09'
$year = '2012'
$frequency = 4 #Number of Seconds between check requests
$agent = Mechanize.new
$agent.redirect_ok = true
$agent.user_agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.11 Safari/535.19"
$agent.verify_mode = OpenSSL::SSL::VERIFY_NONE
#Uber simway to colorize outputin
class String
def color(c)
colors = {
:black => 30,
:red => 31,
:green => 32,
:yellow => 33,
:blue => 34,
:magenta => 35,
:cyan => 36,
:white => 37
}
return "\e[#{colors[c] || c}m#{self}\e[0m"
end
end
#Logins, Gets the Courses, Returns Courses Obj with Name/URL/Tools for each
def login(username, password)
#Login to the system!
page = $agent.get("https://auth.vt.edu/login?service=https://webapps.banner.vt.edu/banner-cas-prod/authorized/banner/SelfService")
login = page.forms.first
login.set_fields({
:username => username,
:password => password
})
if (login.submit().body.match(/Invalid username or password/)) then
return false
else
return true
end
end
#Gets Course Information
def getCourse(crn)
begin
courseDetails = Nokogiri::HTML( $agent.get(
"https://banweb.banner.vt.edu/ssb/prod/HZSKVTSC.P_ProcComments?CRN=#{crn}&TERM=#{$term}&YEAR=#{$year}"
).body)
rescue
return false #Failed to get course
end
#Flatten table to make it easier to work with
course = {}
dataSet = false
course[:title] = courseDetails.css('td.title').last.text.gsub(/-\ +/, '')
course[:crn] = crn
courseDetails.css('table table tr').each_with_index do |row|
#If we have a dataSet
case dataSet
when :rowA
[ :i, :days, :end, :begin, :end, :exam].each_with_index do |el, i|
if row.css('td')[i] then
course[el] = row.css('td')[i].text
end
end
when :rowB
[ :instructor, :type, :status, :seats, :capacity ].each_with_index do |el, i|
course[el] = row.css('td')[i].text
end
end
dataSet = false
#Is there a dataset?
row.css('td').each do |cell|
case cell.text
when "Days"
dataSet = :rowA
when "Instructor"
dataSet = :rowB
end
end
end
return course
end
#Registers you for the given CRN, returns true if successful, false if not
def registerCrn(crn)
#Follow Path
$agent.get("https://banweb.banner.vt.edu/ssb/prod/twbkwbis.P_GenMenu?name=bmenu.P_MainMnu")
reg = $agent.get("https://banweb.banner.vt.edu/ssb/prod/hzskstat.P_DispRegStatPage")
dropAdd = reg.link_with(:href => "/ssb/prod/bwskfreg.P_AddDropCrse?term_in=#{$year}#{$term}").click
#Fill in CRN Box and Submit
crnEntry = dropAdd.form_with(:action => '/ssb/prod/bwckcoms.P_Regs')
crnEntry.fields_with(:id => 'crn_id1').first.value = crn
crnEntry['CRN_IN'] = crn
add = crnEntry.submit(crnEntry.button_with(:value => 'Submit Changes')).body
if add =~ /#{crn}/ && !(add =~ /Registration Errors/) then
return true
else
return false
end
end
#Main loop that checks the availaibility of each courses and fires to registerCrn on availaibility
def checkCourses(courses)
requestCount = 0
startTime = Time.new
loop do
system("clear")
requestCount += 1
nowTime = Time.new
puts "Checking Availaibility of CRNs".color(:yellow)
puts "--------------------------------\n"
puts "Started:\t#{startTime.asctime}".color(:magenta)
puts "Now: \t#{nowTime.asctime}".color(:cyan)
puts "Request:\t#{requestCount} (Once every #{$frequency} seconds)".color(:green)
puts "--------------------------------\n\n"
courses.each_with_index do |c, i|
puts "#{c[:crn]} - #{c[:title]}".color(:blue)
course = getCourse(c[:crn])
next unless course #If throws error
puts "Availaibility: #{course[:seats]} / #{course[:capacity]}".color(:red)
if (course[:seats] =~ /Full/) then
else
if (registerCrn(c[:crn])) then
puts "CRN #{c[:crn]} Registration Sucessfull"
courses.slice!(i)
else
puts "Couldn't Register"
end
end
print "\n"
end
sleep $frequency
end
end
#Add courses to be checked
def addCourses
crns = []
loop do
system("clear")
puts "Your CRNs:".color(:red)
crns.each do |crn|
puts " -> #{crn[:title]} (CRN: #{crn[:crn]})".color(:magenta)
end
#Prompt for CRN
alt = (crns.length > 0) ? " (or just type 'start') " : " "
input = ask("\nEnter a CRN to add it#{alt}".color(:green) + ":: ") { |q| q.echo = true }
#Validate CRN to be 5 Digits
if (input =~ /^\d{5}$/) then
#Display CRN Info
c = getCourse(input.to_s)
puts "\nCourse: #{c[:title]} - #{c[:crn]}".color(:red)
puts "--> Time: #{c[:begin]}-#{c[:end]} on #{c[:days]}".color(:cyan)
puts "--> Teacher: #{c[:instructor]}".color(:cyan)
puts "--> Type: #{c[:type]} || Status: #{c[:status]}".color(:cyan)
puts "--> Availability: #{c[:seats]} / #{c[:capacity]}\n".color(:cyan)
#Add Class Prompt
add = ask("Add This Class? (yes/no)".color(:yellow) + ":: ") { |q| q.echo = true }
crns.push(c) if (add =~ /yes/)
elsif (input == "start") then
checkCourses(crns)
end
end
end
def main
system("clear")
puts "Welcome to CourseAdd by mil".color(:blue)
username = ask("PID ".color(:green) + ":: ") { |q| q.echo = true }
password = ask("Password ".color(:green) + ":: " ) { |q| q.echo = "*" }
system("clear")
if login(username, password) then
addCourses
else
puts "Invalid PID/Password"
exit
end
end
main
but when I run ruby Untitled.rb it give me this error.
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require': no such file to load -- mechanize (LoadError)
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/rubygems/custom_require.rb:31:in `require'
from /Users/user/Desktop/Untitled.rb:3
What does this mean and how can I fix it? I'm not sure if I need to be doing this through an IDE or if terminal works. I'm brand new to ruby so I honestly have not a clue what the issue could be.
You need to install mechanize. In your terminal, type:
gem install mechanize
Retry your script when it finishes installing. If you have other gems that are missing, you can use the same command to install them.
gem install <gem name>

God - starting new process while existing process is still stopping

Using God (godrb.com) I'm trying to write a recipe that starts up a new process regardless of the status of an existing process when deploying an application. The existing process needs to have a long running timeout for it to finish current tasks, but the new process should start immediately using the newly deployed code.
What I currently have now sets a timeout of 300 seconds on the stop but waits the whole 300 seconds before starting up the new process.
God.watch do |w|
w.name = "sidekiq"
w.interval = 30.seconds
w.start = "bash -lc 'cd /path/to/current && ./bin/sidekiq -P /path/to/shared/pids/sidekiq.pid'"
w.stop = "bash -lc 'kill -USR1 `cat /path/to/shared/pids/sidekiq.pid`'"
w.stop_timeout = 300.seconds
w.pid_file = "/path/to/shared/pids/sidekiq.pid"
w.behavior(:clean_pid_file)
end
In this case, the kill -USR1 tells sidekiq to finish processing any current jobs, but to not take anymore work.
I'd like to keep the 300 second timeout on the existing worker but start up the new process as soon as the kill command is run.
I think you have to define some transitions.
This is my god.rb:
# I'm using Rails
rails_env = ENV['RAILS_ENV'] || 'development'
rails_root = '/path/to/current'
pid_file = "#{rails_root}/tmp/pids/sidekiq.pid"
God.watch do |w|
w.dir = rails_root
w.name = "sidekiq"
w.interval = 30.seconds
w.env = {'RAILS_ENV' => rails_env, 'BUNDLE_GEMFILE' => "#{rails_root}/Gemfile"}
w.uid = 'deployer'
w.gid = 'staff'
w.start = "cd #{rails_root}; bundle exec sidekiq -e #{rails_env} -C #{rails_root}/config/sidekiq.yml -i #{i} -P #{pid_file}&"
w.stop = "cd #{rails_root}; bundle exec sidekiqctl stop #{pid_file} 10"
w.restart = "#{w.stop} && #{w.start}"
w.start_grace = 15.seconds
w.restart_grace = 15.seconds
w.pid_file = pid_file
w.log = "#{rails_root}/log/sidekiq.log"
# clean pid files before start if necessary
w.behavior(:clean_pid_file)
# determine the state on startup
w.transition(:init, {true => :up, false => :start}) do |on|
on.condition(:process_running) do |c|
c.running = true
end
end
# determine when process has finished starting
w.transition([:start, :restart], :up) do |on|
on.condition(:process_running) do |c|
c.running = true
end
# failsafe
on.condition(:tries) do |c|
c.times = 5
c.transition = :start
end
end
# start if process is not running
w.transition(:up, :start) do |on|
on.condition(:process_exits)
end
# lifecycle
w.lifecycle do |on|
on.condition(:flapping) do |c|
c.to_state = [:start, :restart]
c.times = 5
c.within = 5.minute
c.transition = :unmonitored
c.retry_in = 10.minutes
c.retry_times = 5
c.retry_within = 2.hours
end
end
end

How to get a stopwatch program running?

I borrowed some code from a site, but I don't know how to get it to display.
class Stopwatch
def start
#accumulated = 0 unless #accumulated
#elapsed = 0
#start = Time.now
#mybutton.configure('text' => 'Stop')
#mybutton.command { stop }
#timer.start
end
def stop
#mybutton.configure('text' => 'Start')
#mybutton.command { start }
#timer.stop
#accumulated += #elapsed
end
def reset
stop
#accumulated, #elapsed = 0, 0
#mylabel.configure('text' => '00:00:00.00.000')
end
def tick
#elapsed = Time.now - #start
time = #accumulated + #elapsed
h = sprintf('%02i', (time.to_i / 3600))
m = sprintf('%02i', ((time.to_i % 3600) / 60))
s = sprintf('%02i', (time.to_i % 60))
mt = sprintf('%02i', ((time - time.to_i)*100).to_i)
ms = sprintf('%04i', ((time - time.to_i)*10000).to_i)
ms[0..0]=''
newtime = "#{h}:#{m}:#{s}.#{mt}.#{ms}"
#mylabel.configure('text' => newtime)
end
end
How would I go about getting this running?
Thanks
Based upon the additional code rkneufeld posted, this class requires a timer that is specific to Tk. To do it on the console, you could just create a loop that calls tick over and over. Of course, you have to remove all the code that was related to the GUI:
class Stopwatch
def start
#accumulated = 0 unless #accumulated
#elapsed = 0
#start = Time.now
# #mybutton.configure('text' => 'Stop')
# #mybutton.command { stop }
# #timer.start
end
def stop
# #mybutton.configure('text' => 'Start')
# #mybutton.command { start }
# #timer.stop
#accumulated += #elapsed
end
def reset
stop
#accumulated, #elapsed = 0, 0
# #mylabel.configure('text' => '00:00:00.00.000')
end
def tick
#elapsed = Time.now - #start
time = #accumulated + #elapsed
h = sprintf('%02i', (time.to_i / 3600))
m = sprintf('%02i', ((time.to_i % 3600) / 60))
s = sprintf('%02i', (time.to_i % 60))
mt = sprintf('%02i', ((time - time.to_i)*100).to_i)
ms = sprintf('%04i', ((time - time.to_i)*10000).to_i)
ms[0..0]=''
newtime = "#{h}:#{m}:#{s}.#{mt}.#{ms}"
# #mylabel.configure('text' => newtime)
end
end
watch = Stopwatch.new
watch.start
1000000.times do
puts watch.tick
end
You'll end up with output like this:
00:00:00.00.000
00:00:00.00.000
00:00:00.00.000
...
00:00:00.00.000
00:00:00.00.000
00:00:00.01.160
00:00:00.01.160
...
Not particularly useful, but there it is. Now, if you're looking to do something similar in Shoes, try this tutorial that is very similar.
I believe you have found the example on this site
I'm repeating what is already on the site but you are missing:
require 'tk'
as well as initialization code:
def initialize
root = TkRoot.new { title 'Tk Stopwatch' }
menu_spec = [
[
['Program'],
['Start', lambda { start } ],
['Stop', lambda { stop } ],
['Exit', lambda { exit } ]
],
[
['Reset'], ['Reset Stopwatch', lambda { reset } ]
]
]
#menubar = TkMenubar.new(root, menu_spec, 'tearoff' => false)
#menubar.pack('fill'=>'x', 'side'=>'top')
#myfont = TkFont.new('size' => 16, 'weight' => 'bold')
#mylabel = TkLabel.new(root)
#mylabel.configure('text' => '00:00:00.0', 'font' => #myfont)
#mylabel.pack('padx' => 10, 'pady' => 10)
#mybutton = TkButton.new(root)
#mybutton.configure('text' => 'Start')
#mybutton.command { start }
#mybutton.pack('side'=>'left', 'fill' => 'both')
#timer = TkAfter.new(1, -1, proc { tick })
Tk.mainloop
end
end
Stopwatch.new
I would suggest reading through the rest of the site to understand what is all going on.
I was searching for a quick and dirty stop watch class to avoid coding such and came upon the site where the original code was posted and this site as well.
In the end, I modified the code until it met what I think that I was originally searching for.
In case anyone is interested, the version that I have ended up thus far with is as follows (albeit that I have yet to apply it in the application that I am currently updating and for which I want to make use of such functionality).
# REFERENCES
# 1. http://stackoverflow.com/questions/858970/how-to-get-a-stopwatch-program-running
# 2. http://codeidol.com/other/rubyckbk/User-Interface/Creating-a-GUI-Application-with-Tk/
# 3. http://books.google.com.au/books?id=bJkznhZBG6gC&pg=PA806&lpg=PA806&dq=ruby+stopwatch+class&source=bl&ots=AlH2e7oWWJ&sig=KLFR-qvNfBfD8WMrUEbVqMbN_4o&hl=en&ei=WRjOTbbNNo2-uwOkiZGwCg&sa=X&oi=book_result&ct=result&resnum=8&ved=0CEsQ6AEwBw#v=onepage&q=ruby%20stopwatch%20class&f=false
# 4. http://4loc.wordpress.com/2008/09/24/formatting-dates-and-floats-in-ruby/
module Utilities
class StopWatch
def new()
#watch_start_time = nil #Time (in seconds) when the stop watch was started (i.e. the start() method was called).
#lap_start_time = nil #Time (in seconds) when the current lap started.
end #def new
def start()
myCurrentTime = Time.now() #Current time in (fractional) seconds since the Epoch (January 1, 1970 00:00 UTC)
if (!running?) then
#watch_start_time = myCurrentTime
#lap_start_time = #watch_start_time
end #if
myCurrentTime - #watch_start_time;
end #def start
def lap_time_seconds()
myCurrentTime = Time.now()
myLapTimeSeconds = myCurrentTime - #lap_start_time
#lap_start_time = myCurrentTime
myLapTimeSeconds
end #def lap_time_seconds
def stop()
myTotalSecondsElapsed = Time.now() - #watch_start_time
#watch_start_time = nil
myTotalSecondsElapsed
end #def stop
def running?()
!#watch_start_time.nil?
end #def
end #class StopWatch
end #module Utilities
def kill_time(aRepeatCount)
aRepeatCount.times do
#just killing time
end #do
end #def kill_time
elapsed_time_format_string = '%.3f'
myStopWatch = Utilities::StopWatch.new()
puts 'total time elapsed: ' + elapsed_time_format_string % myStopWatch.start() + ' seconds'
kill_time(10000000)
puts 'lap time: ' + elapsed_time_format_string % myStopWatch.lap_time_seconds() + ' seconds'
kill_time(20000000)
puts 'lap time: ' + elapsed_time_format_string % myStopWatch.lap_time_seconds() + ' seconds'
kill_time(30000000)
puts 'lap time: ' + elapsed_time_format_string % myStopWatch.lap_time_seconds() + ' seconds'
puts 'total time elapsed: ' + elapsed_time_format_string % myStopWatch.stop() + ' seconds'
Simple stopwatch script:
# pass the number of seconds as the parameter
seconds = eval(ARGV[0]).to_i
start_time = Time.now
loop do
elapsed = Time.now - start_time
print "\e[D" * 17
print "\033[K"
if elapsed > seconds
puts "Time's up!"
exit
end
print Time.at(seconds - elapsed).utc.strftime('%H:%M:%S.%3N')
sleep(0.05)
end
Run like this in your terminal (to mark a lap, just tap enter):
# 10 is the number of seconds
ruby script.rb 10
# you can even do this:
ruby script.rb "20*60" # 20 minutes

Resources