Spiking Out a GUI Employee Time Clock

Spiking Out a GUI Employee Time Clock

Using Tk Backed by Square’s Ruby SDK

As small companies get bigger, they sometimes decide to start using time clocks to track employee hours. Stakeholders often ask engineering for a solution that’s easy to use. As Rubyists, we usually create command line apps or web apps, but sometimes we get asked to make something for mobile or desktop. Below I’ll spike out the start of a desktop GUI employee time clock app with Ruby and the Tk graphical user interface toolkit backed by Square’s API to track the details.

First Install Ruby and a Few Gems

I’ll show these examples on macOS, so we’ll use use brew to install the most recent version of Ruby first. (If you already have Ruby installed, skip this step.)

brew install ruby

Tk previously shipped with Ruby, but it was cut into a gem and removed from the standard library with the release of Ruby 2.4 back in 2016. So first we’ll need to install both the Tk and Square SDK gems.

gem install tk && gem install square_connect

Next we can use the IRB REPL that ships with Ruby to check our current employees with the SDK, and make sure everything so far is working.

To be able to see our employees we’ll need an API token at this point. To get a token, sign up for a Square developer account. For the purposes of these examples, set your API token as an environment variable on the terminal that you’re going to use.

export SQUARE_CONNECT_TOKEN="*YOUR ACCESS TOKEN HERE*"

You can now use irb to check that there aren’t any employees yet. First type irb in the command line then input the following code into the IRB terminal.

require 'square_connect'

SquareConnect.configure { |config| config.access_token = ENV.fetch 'SQUARE_CONNECT_TOKEN' }

api_instance = SquareConnect::V1EmployeesApi.new
api_instance.list_employees
#=> []

Next we can create an employee in your browser, and re-run list_employees to see the newly created employee in the REPL.

api_instance.list_employees
#=> [#<SquareConnect::V1Employee:...>]

Okay, the SDK works! Let’s move on to the GUI app.

Spiking out a Tk GUI App

I love command line apps. If all your employees are engineers, maybe you can get away with making a command line employee time card app. Today, let’s start a graphical interface app with Tk. The Ruby Tk gem wraps the toolkit and provides us a nice DSL in Ruby.

This is my first time using Tk, so let’s muddle through some examples as best we can. First, let’s create a little Hello World type app that shows us today’s menu. We’ll just need two Tk classes to get started, TkRoot and TkLabel.

##
# A GUI for Submitting Timecards
app_title = 'Submit a Timecard'

root = TkRoot.new
root.title = app_title

##
# Fonts
header_label_font = TkFont.new size: 20, weight: 'bold'
response_label_font = TkFont.new size: 19
regular_font = TkFont.new size: 15

That wasn’t so bad! Now we have a little window to work with. If we save this snippet in a file called menu.rb, we can run it from the command line with ruby menu.rb.

Let’s move on and try to integrate the GUI with our employee time sheet SDK.

First we’ll require Date and Pretty Print from the Ruby standard library and the Tk and Square SDK gems.

require 'date'
require 'pp'
require 'tk'
require 'square_connect'

Then we’re ready to fetch our list of employees and pull out the parts we care about — the employee name and id.

SquareConnect.configure { |config| config.access_token = ENV.fetch 'SQUARE_CONNECT_TOKEN' }
api_instance = SquareConnect::V1EmployeesApi.new

employees = api_instance.list_employees.map { |employee| [employee.first_name, employee.id] }.to_h

Next we have some app-wide settings, like the title and fonts.

##
# A GUI for Submitting Timecards
app_title = 'Submit a Timecard'

root = TkRoot.new
root.title = app_title

##
# Fonts
header_label_font = TkFont.new size: 20, weight: 'bold'
response_label_font = TkFont.new size: 19
regular_font = TkFont.new size: 15

At this point we’re ready to define the labels and fields that we’ll be showing in our interface.

##
# Labels and Fields
header_label = TkLabel.new root, text: app_title, font: header_label_font
option_menu = TkOptionMenubutton.new root, values: employees.keys, font: regular_font
clockin_field_label = TkLabel.new root, text: 'Clock-in', font: regular_font
clockin_field = TkEntry.new root
clockin_field.value = DateTime.now.iso8601
clockout_field_label = TkLabel.new root, text: 'Clock-out', font: regular_font
clockout_field = TkEntry.new root
response_label = TkLabel.new root, text: '⚗️', font: response_label_font

That’s it for the boilerplate. Now we’re on to the guts of our app — everything that happens when you press the “Submit” button.

##
# Submit Button
submit_button = TkButton.new root do
  text 'Submit'
  font regular_font
  command -> do
    response_label.text = begin
      options = {employee_id: employees[option_menu.value]}
      options.merge!(clockin_time: clockin_field.value) unless clockin_field.value.empty?
      options.merge!(clockout_time: clockout_field.value) unless clockout_field.value.empty?

      timecard = SquareConnect::V1Timecard.new options

      result = api_instance.create_timecard timecard
      pp result

      "Success! Confirmation code: #{result.id}"
    rescue SquareConnect::ApiError => e
      warn "#{e.class}: #{e.message}"

      e.class.name.rpartition('::').last
    end
  end
end

clockin_field.bind 'Return', ->{ submit_button.invoke }

The above code fetches the values from the relevant input fields and then creates a time card using Square’s API.

Finally, we need to pick a layout for our window. I chose a grid since that seemed the simplest to get started with.

##
# The View Grid
padding = {padx: 20, pady: 20}

Tk.grid header_label, columnspan: 4, **padding
Tk.grid option_menu,  columnspan: 4
Tk.grid clockin_field_label, clockin_field, columnspan: 2, **padding
Tk.grid clockout_field_label, clockout_field, columnspan: 2, **padding
Tk.grid response_label, columnspan: 4, **padding
Tk.grid submit_button, sticky: 'we', columnspan: 4, **padding

Tk.mainloop

Okay, that was a lot of Tk DSL… But it works! We can save the code as time_card.rb and when we run it with ruby time_card.rb the GUI app appears.

Now that we have a working GUI app, let’s add a shebang, make the file executable and remove the .rb extension so it can be run directly. First, add the following shebang to the first line of the time_card.rb file.

#!/usr/bin/env ruby

Next, let’s rename the file to remove the .rb extension and make it executable.

mv time_card.rb time_card
chmod +x time_card

Now we can run our app directly from the console with just.

./time_card

We’re now ready to add an icon to our very basic GUI app! The steps for adding an icon are platform-dependent so I’ll leave that as an exercise for the reader.

Conclusion

This interface is far from polished but I’m happy with it as a partial day spike towards creating a GUI with Tk. My first impression using Tk is that it’s fairly easy to use for simple projects. I look forward to trying something more complex. Next it would be fun to add a better date picker and more features, like adding employees and managing time cards.

If you’re a Rubyist but that was too much boilerplate for your fancy, you might want to consider Shoes as an alternative to Tk. Shoes was created by _why as a super simple way to make GUIs in Ruby. These days there are a variety of types of Shoes in Ruby, but Shoes4 is one nice modern pair that works with JRuby.

We use Ruby for lots of things here at Square — including our Square Connect Ruby SDKs and open source Ruby projects. We’re eagerly awaiting the release of Ruby 2.6!

The Ruby logo is Copyright © 2006, Yukihiro Matsumoto, distributed under CC BY-SA 2.5.The Ruby logo is Copyright © 2006, Yukihiro Matsumoto, distributed under CC BY-SA 2.5.

Want more? Sign up for your monthly developer newsletter or drop by the Square dev Slack channel and say “hi!”

Table Of Contents
View More Articles ›