module Timecard

Constants

DEBUG

Public Class Methods

ask_task() click to toggle source
# File lib/timecard.rb, line 26
def ask_task
  $stderr.print "What will you work on now? "

  @task_name = $stdin.readline.chomp
  @tasks << {:name => @task_name, :time_pairs => []}

  :time_loop
end
dump_tasks() click to toggle source
# File lib/timecard.rb, line 103
def dump_tasks
  if @tasks.any?
    puts "\n"
    i = 0
    @tasks.each do |task|
      i += 1
      puts "Task ##{i}: #{task[:name]} #{format_seconds(sum_time_pairs(task))}"
      task[:time_pairs].each { |pair| puts "  " + pair.inspect + " " + format_seconds(pair.last - pair.first) }
    end
  end
end
finish_and_quit() click to toggle source
# File lib/timecard.rb, line 96
def finish_and_quit
  $stderr.print clearline
  $stderr.puts ">#{format_seconds(sum_time_pairs(@tasks.last))} #{@tasks.last[:name]}"
  dump_tasks
  exit(0)
end
finish_task() click to toggle source
# File lib/timecard.rb, line 90
def finish_task
  $stderr.print clearline
  $stderr.puts ">#{format_seconds(sum_time_pairs(@tasks.last))} #{@tasks.last[:name]}"
  return :ask_task
end
invoke() click to toggle source
# File lib/timecard.rb, line 5
def invoke
  $stderr.puts "=== Welcome to Ruby Timecard! ==="

  @tasks = []
  @new_input = nil

  Signal.trap("INT") do
    dump_tasks
    exit(0)
  end

  state_loop(:ask_task)
end
pause_task() click to toggle source
# File lib/timecard.rb, line 77
def pause_task
  $stderr.print "#{clearline}*#{format_seconds(sum_time_pairs(@tasks.last))} * #{@task_name} (paused, any key to resume)"

  await_input

  loop do
    next_state = process_input { |input| :time_loop }
    return next_state if next_state

    sleep(0.1)
  end
end
state_loop(state) click to toggle source
# File lib/timecard.rb, line 19
def state_loop(state)
  loop do
    $stderr.puts "Entering state #{state}..." if DEBUG
    state = send(state)
  end
end
time_loop() click to toggle source
# File lib/timecard.rb, line 35
def time_loop
  @start_time = @last_time = Time.now

  await_input

  loop do
    if (Time.now - @last_time) > 5.0
      $stderr.puts "#{clearline}Detected Sleep Auto-Pausing for #{format_seconds(Time.now - @last_time)}"
      @tasks.last[:time_pairs] << [@start_time, @last_time]
      @start_time = Time.now
    end

    next_state = process_input { |input| time_loop_transition(input) }
    return next_state if next_state

    elapsed = Time.now - @start_time
    $stderr.print "#{clearline}*#{format_seconds(sum_time_pairs(@tasks.last) + elapsed)} * #{@task_name} (p: pause   f: next task   q: finish & quit)"
    @last_time = Time.now
    sleep(0.5)
  end
end
time_loop_transition(command) click to toggle source
# File lib/timecard.rb, line 57
def time_loop_transition(command)
  valid_command = case command
  when 'p'
    :pause_task
  when 'f'
    :finish_task
  when 'q'
    :finish_and_quit
  end

  if ! valid_command
    await_input
  else
    @tasks.last[:time_pairs] << [@start_time, Time.now]
    @start_time = nil
  end

  valid_command
end

Private Class Methods

await_input() click to toggle source
# File lib/timecard.rb, line 126
def await_input
  Thread.start do
    @new_input = get_char
    $stdin.flush
  end
end
clearline() click to toggle source
# File lib/timecard.rb, line 141
def clearline
  "\r\e[0K"
end
format_seconds(seconds) click to toggle source
# File lib/timecard.rb, line 117
def format_seconds(seconds)
  seconds = seconds.to_i
  minutes = seconds / 60
  seconds = seconds % 60
  hours = minutes / 60
  minutes = minutes % 60
  sprintf("%2d:%02d:%02d", hours, minutes, seconds)
end
get_char() click to toggle source
# File lib/timecard.rb, line 133
def get_char
  system("stty raw -echo") #=> Raw mode, no echo
  char = $stdin.getc
  system("stty -raw echo") #=> Reset terminal mode
  Process.kill("INT", $$) if char == "\u0003"
  char
end
process_input() { |input| ... } click to toggle source
# File lib/timecard.rb, line 149
def process_input
  if @new_input
    input = @new_input
    @new_input = nil
    yield(input)
  end
end
sum_time_pairs(task) click to toggle source
# File lib/timecard.rb, line 145
def sum_time_pairs(task)
  task[:time_pairs].inject(0){ |sum,p| sum + (p.last - p.first) }
end