I would like to implement a code that calls specific function at specified time intervals i.e. each 5 seconds. Something similar to window.setInterval() method in JavaScript.
There is game_loop() function at the main thread that prints data to STDOUT and waits for keyboard key input from the user. Simplified version of it below:
def game_loop
k = ""
UI.refresh
while not k == "q"
k = UI.read_key # STDIN.getch() is implemented inside of this function
case k
when "p"
# do something
when "f"
# do something else
end
end
end
Now I would like to add a code which calls another function at per-defined time interval if condition is met. Example:
sleep 5
Production.make if ... # some condition is met
# return to game_loop()
I assume this should be done using threads, but I do not have an idea how to do it.
Thank you for your help.
You can try using this gem:
rufus-scheduler
It would be something like:
require 'rufus-scheduler'
scheduler = Rufus::Scheduler.new
scheduler.in '5s' do
Production.make if ... # some condition is met
game_loop()
end
Related
I am doing android app test automation using Cucumber-Appium in ruby.
I want to write a wait function which should wait till the next page is shown.
I tried few ways but it is not working.
The code:
And (/^I wait till '(.*)' appears$/) do |next_page|
$i=1
while $i==1 do
if ObjectSpace.const_get('next_page').new.identity?
$current_page = $current_page.change_page('next_page')
$i=0
else
wait_secs(1)
end
end
Can any one suggest something?
Whenever I write a timeout method, I like to pass the block until it times out or resolves to true. Basically, I want it to look like this:
timer { ObjectSpace.const_get('NextPage').new.identity? }
So my method looks like:
def timer(t = 5, &block)
timeout = Time.now + t
until Time.now > timeout
result = yield
return result if result
sleep 0.5
end
raise "Timer failed after #{t} seconds"
end
This method loops over my block until yield returns truthy. It will sleep for a half a second and timeout after t seconds, which defaults to 5. If you wanted it to return false instead of raising an error, you just need to change the last line to false.
I'm not familiar enough with appium to write the block to check for your next page, but I imagine it would look similar to my first code example above.
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/
For example, if I want to make a timer, how do I make a delay in the loop so it counts in seconds and do not just loop through it in a millisecond?
The 'comment' above is your answer, given the very simple direct question you have asked:
1.upto(5) do |n|
puts n
sleep 1 # second
end
It may be that you want to run a method periodically, without blocking the rest of your code. In this case, you want to use a Thread (and possibly create a mutex to ensure that two pieces of code are not attempting to modify the same data structure at the same time):
require 'thread'
items = []
one_at_a_time = Mutex.new
# Show the values every 5 seconds
Thread.new do
loop do
one_at_a_time.synchronize do
puts "Items are now: #{items.inspect}"
sleep 5
end
end
end
1000.times do
one_at_a_time.synchronize do
new_items = fetch_items_from_web
a.concat( new_items )
end
end
Somehow, many people think that putting a sleep method with a constant time interval as its argument will work. However, note that no method takes zero time. If you put sleep(1) within a loop, the cycle will surely be more than 1 second as long as you have some other content in the loop. What is worse, it does not always take the same time processing each iteration of a loop. Each cycle will take more than 1 second, with the error being random. As the loop keeps running, this error will contaminate and grow always toward positive. Especially if you want a timer, where the cycle is important, you do not want to do that.
The correct way to loop with constant specified time interval is to do it like this:
loop do
t = Time.now
#... content of the loop
sleep(t + 1 - Time.now)
end
For example, if I want to make a timer, how do I make a delay in the loop so it counts in seconds and do not just loop through it in a millisecond?
The 'comment' above is your answer, given the very simple direct question you have asked:
1.upto(5) do |n|
puts n
sleep 1 # second
end
It may be that you want to run a method periodically, without blocking the rest of your code. In this case, you want to use a Thread (and possibly create a mutex to ensure that two pieces of code are not attempting to modify the same data structure at the same time):
require 'thread'
items = []
one_at_a_time = Mutex.new
# Show the values every 5 seconds
Thread.new do
loop do
one_at_a_time.synchronize do
puts "Items are now: #{items.inspect}"
sleep 5
end
end
end
1000.times do
one_at_a_time.synchronize do
new_items = fetch_items_from_web
a.concat( new_items )
end
end
Somehow, many people think that putting a sleep method with a constant time interval as its argument will work. However, note that no method takes zero time. If you put sleep(1) within a loop, the cycle will surely be more than 1 second as long as you have some other content in the loop. What is worse, it does not always take the same time processing each iteration of a loop. Each cycle will take more than 1 second, with the error being random. As the loop keeps running, this error will contaminate and grow always toward positive. Especially if you want a timer, where the cycle is important, you do not want to do that.
The correct way to loop with constant specified time interval is to do it like this:
loop do
t = Time.now
#... content of the loop
sleep(t + 1 - Time.now)
end
I'm looking to create a Ruby (MRI 1.9.3) loop that runs at most for a certain amount of time, and once that time is up it goes to the next iteration of the loop.
For example, this is what I'm hoping to achieve:
timer = Timer.new
while foo
timer.after 5 do # The loop on foo only gets to run for 5 seconds
next
end
# Do some work here
end
So far, I've found tarcieri's gem called Timers (https://github.com/tarcieri/timers) which is what I'm trying to emulate in the code above, but my implementation doesn't give the behavior I expect, which is for the loop to go to the next iteration after 5 seconds if my work takes longer than that. Any ideas?
require 'timeout'
timeout_in_seconds = 5
while foo
begin
Timeout.timeout(timeout_in_seconds) do
# Do some work here
end
rescue Timeout::Error
next
end
end
It's been awhile since I brushed off my Ruby skills, but I believe you can do this with the timeout library.
require 'timeout'
while foo
Timeout.timeout(5) do
work()
end
end
You can also try this:
time = 60
time_start = Time.now
begin
time_running = Time.now - time_start
#You_code_goes_here
end until (time_running.to_i >= time)
That loop will happen until the time_running var is equal or greater than "60".