Creating a Simple Data Collection App: Part 3
Unhappy with the current data collection apps out there? Then create your own!
Ruby Code Review: Data Collection Program
Other parts in this series
Check back later for the other parts of this tutorial
This is Part 3 of a multi-part code sample and review for creating a simple data collection system using Ruby. Future parts will be released soon!
Part 3 Overview
Check out the Ruby Data Collector on Github
Or check out the Relevant Commit for this step in the review.
Part 1 of this series involved creating some user stories that defined some of the key aspects of the Data Collection System I want to build. In Part 2, I added tests and then built out the app for the console.
Part 3 is in reference to a particular challenge I faced when building this app in the console.
Dealing with Timers in the Console
In this particular console app, I want the user to be able to continually enter behavior codes to track behavior. At no point should the app become unresponsive to user input. However, I also want a timer to run to track the moment at which each behavior is entered. This can cause trouble if you try to put the timer and the user input on a single thread.
I had initially found the Timers gem that allows you to call a specific function at each interval of time. This initially made sense. I could call a function that would update the current time every second and end the session when I exceeded the specified session duration. However, while this code was running, the user could not input any information. The code was essentially “sleeping” (similar to sleep(1)
) between each function call.
Fortunately, as always, Ruby has a helper to put the timer on a separate thread. Concurrent::TimerTask allows you to specify an interval and a code block that will run at that interval.
In our current implementation, that code looks like this:
@task = Concurrent::TimerTask.new(execution_interval: 1, timeout_interval: duration_in_seconds) do
@current_seconds += 1
monitor_session(@current_seconds)
end
This runs the code in the block every second until the session duration (in seconds) is reached. I assign it to an instance variable (@task
) so that I can start and stop the timer from other pieces of our code. For example, if the user decides to stop the session early, I can call @task.shutdown
to stop our timer loop.
The Concurrent::TimerTask
allows us to have a timer, but not interfere with the user’s ability to enter input because the timer runs on a separate thread.
In the next part of this tutorial, I will share how I converted this console app into a javascript app for the browser.