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.
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.
Brought to you by CEUHelper - Simple, inexpensive, and paperless CEU management for conferences.