diff options
| -rw-r--r-- | CHANGELOG | 6 | ||||
| -rw-r--r-- | README.markdown | 44 | ||||
| -rw-r--r-- | hcl.gemspec | 6 | ||||
| -rw-r--r-- | lib/hcl/app.rb | 66 | ||||
| -rw-r--r-- | lib/hcl/commands.rb | 56 | ||||
| -rw-r--r-- | lib/hcl/day_entry.rb | 9 | ||||
| -rw-r--r-- | lib/hcl/task.rb | 9 | ||||
| -rw-r--r-- | test/day_entry_test.rb | 4 | ||||
| -rw-r--r-- | test/test_helper.rb | 2 |
9 files changed, 137 insertions, 65 deletions
@@ -1,5 +1,11 @@ = Recent Changes in HCl +== v0.4.0 + +* start a timer or add a note without having to specify the sub-command +* aliases can be specified with "@" anywhere on the command line +* added alias and unalias to simplify setting task aliases + == v0.3.2 * fixed support for modern Rubies diff --git a/README.markdown b/README.markdown index b444237..d3e5da6 100644 --- a/README.markdown +++ b/README.markdown @@ -40,51 +40,53 @@ or you can install from source using jeweler: hcl stop [msg] hcl resume -### Starting a Timer +### Available Projects and Tasks To start a new timer you need to identify the project and task. After you've used the show command you can use the tasks command to view a cached list of -available tasks. The first two numbers in each row are the project and task -IDs. You need both values to start a timer: +available tasks. - $ hcl show - ------------- - 0:00 total $ hcl tasks - 1234 5678 ClientX Software Development - 1234 9876 ClientX Admin - $ hcl start 1234 5678 adding a new feature -### Task Aliases +### Starting a Timer Since it's not practical to enter two long numbers every time you want to -identify a task, HCl supports task aliases. The task alias must start with 'task.', followed by your alias. +identify a task, HCl supports task aliases: - $ hcl set task.xdev 1234 5678 - $ hcl start xdev adding a new feature + $ hcl alias xdev 1234 5678 + $ hcl @xdev Adding a new feature! ### Starting a Timer with Initial Time You can also provide an initial time when starting a new timer. This can be expressed in floating-point or HH:MM. The following two -commands are identical: +commands are equivalent: - $ hcl start xdev +0:15 adding a new feature - $ hcl start +.25 xdev adding a new feature + $ hcl @xdev +0:15 Adding a new feature! + $ hcl +.25 @xdev Adding a new feature! ### Adding Notes to a Running Task -While a task is running you can append strings to the note for that task: +While a task is running you can append lines to the task notes. +Providing the note command is optional, just the bare message will work. +These two commands are equivalent: - $ hcl note Found a good time - $ hcl note or not, whatever... + $ hcl Found a good time! + $ hcl note Found a good time! ### Stopping a Timer The following command will stop a running timer (currently only one timer at -a time is supported): +a time is supported). You can provide a message when stopping a timer as +well: + + $ hcl stop All done! + +### Resuming a Timer + +You can easily resume the last stopped timer: - $ hcl stop + $ hcl resume ### Date Formats diff --git a/hcl.gemspec b/hcl.gemspec index c6221ee..1694376 100644 --- a/hcl.gemspec +++ b/hcl.gemspec @@ -5,14 +5,14 @@ Gem::Specification.new do |s| s.name = %q{hcl} - s.version = "0.3.1" + s.version = "0.4.0" s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["Zack Hobson"] - s.date = %q{2011-07-13} + s.date = %q{2013-11-18} s.default_executable = %q{hcl} s.description = %q{HCl is a command-line client for manipulating Harvest time sheets.} - s.email = %q{zack@opensourcery.com} + s.email = %q{zack@zackhobson.com} s.executables = ["hcl"] s.extra_rdoc_files = [ "LICENSE", diff --git a/lib/hcl/app.rb b/lib/hcl/app.rb index 9569dc5..6b3e428 100644 --- a/lib/hcl/app.rb +++ b/lib/hcl/app.rb @@ -3,6 +3,7 @@ require 'yaml' require 'rexml/document' require 'net/http' require 'net/https' +require 'fileutils' ## gem dependencies require 'chronic' @@ -36,10 +37,13 @@ module HCl include HCl::Utility include HCl::Commands - SETTINGS_FILE = "#{ENV['HOME']}/.hcl_settings" - CONFIG_FILE = "#{ENV['HOME']}/.hcl_config" + SETTINGS_FILE = "#{ENV['HOME']}/.hcl/settings.yml" + CONFIG_FILE = "#{ENV['HOME']}/.hcl/config.yml" + OLD_SETTINGS_FILE = "#{ENV['HOME']}/.hcl_settings" + OLD_CONFIG_FILE = "#{ENV['HOME']}/.hcl_config" def initialize + FileUtils.mkdir_p(File.join(ENV['HOME'], ".hcl")) read_config read_settings end @@ -57,6 +61,14 @@ module HCl Commands.method_defined? command end + def start_or_note *args + if DayEntry.with_timer + note *args + else + start *args + end + end + # Start the application. def run begin @@ -71,8 +83,7 @@ module HCl end end else - STDERR.puts "unrecognized command `#{@command}'" - exit 1 + start_or_note @command, *@args end else show @@ -97,23 +108,38 @@ module HCl HCl is a command-line client for manipulating Harvest time sheets. Commands: - hcl show [date] + # show all available tasks hcl tasks + + # set a task alias + hcl alias <task> <project_id> <task_id> + + # list task aliases hcl aliases - hcl set <key> <value ...> - hcl unset <key> - hcl start <task> [msg] - hcl stop [msg] + + # start a task using an alias + hcl @<task> [+time] [message] + + # display the daily timesheet + hcl show [date] + + # stop a running timer + hcl stop [message] + + # resume the last stopped timer hcl resume - hcl note <msg> + + # add a line to your running timer + hcl note <message> Examples: - $ hcl tasks - $ hcl start 1234 4567 this is my log message - $ hcl set task.mytask 1234 4567 - $ hcl start mytask this is my next log message - $ hcl show yesterday - $ hcl show last tuesday + hcl alias mytask 1234 4567 + hcl @mytask +:15 Doing a thing that I started 15 minutes ago. + hcl Adding a note to my running task. + hcl stop That's enough for now. + hcl resume + hcl show yesterday + hcl show last tuesday Options: EOM @@ -129,8 +155,8 @@ EOM if File.exists? CONFIG_FILE config = YAML::load File.read(CONFIG_FILE) TimesheetResource.configure config - elsif File.exists? old_conf = File.dirname(__FILE__) + "/../hcl_conf.yml" - config = YAML::load File.read(old_conf) + elsif File.exists? OLD_CONFIG_FILE + config = YAML::load File.read(OLD_CONFIG_FILE) TimesheetResource.configure config write_config config else @@ -150,11 +176,15 @@ EOM File.open(CONFIG_FILE, 'w') do |f| f.write config.to_yaml end + FileUtils.chmod 0400, CONFIG_FILE end def read_settings if File.exists? SETTINGS_FILE @settings = YAML.load(File.read(SETTINGS_FILE)) + elsif File.exists? OLD_SETTINGS_FILE + @settings = YAML.load(File.read(OLD_SETTINGS_FILE)) + write_settings else @settings = {} end diff --git a/lib/hcl/commands.rb b/lib/hcl/commands.rb index fdc50a3..2ca3742 100644 --- a/lib/hcl/commands.rb +++ b/lib/hcl/commands.rb @@ -29,8 +29,23 @@ module HCl write_settings end + def unalias task + unset "task.#{task}" + puts "Removed task alias @#{task}." + end + + def alias task_name, *value + task = Task.find *value + if task + set "task.#{task_name}", *value + puts "Added alias @#{task_name} for #{task}." + else + puts "Unrecognized project and task ID: #{value.inspect}" + end + end + def aliases - @settings.keys.select { |s| s =~ /^task\./ }.map { |s| s.slice(5..-1) } + @settings.keys.select { |s| s =~ /^task\./ }.map { |s| "@"+s.slice(5..-1) } end def start *args @@ -39,19 +54,33 @@ module HCl args.delete(starting_time) starting_time = time2float starting_time end - ident = args.shift - task_ids = if @settings.key? "task.#{ident}" - @settings["task.#{ident}"].split(/\s+/) - else - [ident, args.shift] + ident = args.detect {|a| a[0] == '@' } + if ident + args.delete(ident) + ident = ident.slice(1..-1) + else + ident = args.shift + end + if ident + task_ids = if @settings.key? "task.#{ident}" + @settings["task.#{ident}"].split(/\s+/) + else + [ident, args.shift] + end + task = Task.find *task_ids + if task.nil? + puts "Unknown task alias, try one of the following: ", aliases.join(', ') + exit 1 end - task = Task.find *task_ids - if task.nil? - puts "Unknown project/task alias, try one of the following: #{aliases.join(', ')}." + timer = task.start( + :starting_time => starting_time, + :note => args.join(' ') + ) + puts "Started timer for #{timer} (at #{current_time})" + else + puts "You must provide a task alias to start a timer:", aliases.join(', ') exit 1 end - timer = task.start(:starting_time => starting_time, :note => args.join(' ')) - puts "Started timer for #{timer} (at #{current_time})" end def stop *args @@ -70,7 +99,7 @@ module HCl entry = DayEntry.with_timer if entry entry.append_note message - puts "Added note '#{message}' to #{entry}." + puts "Added note to #{entry}." else puts "No running timers found." end @@ -81,7 +110,8 @@ module HCl total_hours = 0.0 DayEntry.all(date).each do |day| running = day.running? ? '(running) ' : '' - puts "\t#{day.formatted_hours}\t#{running}#{day.project} #{day.notes}"[0..78] + columns = HighLine::SystemExtensions.terminal_size[0] + puts "\t#{day.formatted_hours}\t#{running}#{day.project}: #{day.notes.lines.last}"[0..columns-1] total_hours = total_hours + day.hours.to_f end puts "\t" + '-' * 13 diff --git a/lib/hcl/day_entry.rb b/lib/hcl/day_entry.rb index c274126..afcdf08 100644 --- a/lib/hcl/day_entry.rb +++ b/lib/hcl/day_entry.rb @@ -31,12 +31,9 @@ module HCl def append_note new_notes # If I don't include hours it gets reset. # This doens't appear to be the case for task and project. - DayEntry.post("daily/update/#{id}", <<-EOD) - <request> - <notes>#{notes << " #{new_notes}"}</notes> - <hours>#{hours}</hours> - </request> - EOD + (self.notes << "\n#{new_notes}").lstrip! + DayEntry.post "daily/update/#{id}", + %{<request><notes>#{notes}</notes><hours>#{hours}</hours></request>} end def self.with_timer diff --git a/lib/hcl/task.rb b/lib/hcl/task.rb index 3922600..8a5a833 100644 --- a/lib/hcl/task.rb +++ b/lib/hcl/task.rb @@ -1,3 +1,5 @@ +require 'fileutils' + module HCl class Task < TimesheetResource def self.cache_tasks doc @@ -9,6 +11,7 @@ module HCl end) end unless tasks.empty? + FileUtils.mkdir_p(cache_dir) File.open(cache_file, 'w') do |f| f.write tasks.uniq.to_yaml end @@ -16,7 +19,11 @@ module HCl end def self.cache_file - File.join(ENV['HOME'],'.hcl_tasks') + File.join(cache_dir, 'tasks.yml') + end + + def self.cache_dir + File.join(ENV['HOME'],'.hcl/cache') end def self.all diff --git a/test/day_entry_test.rb b/test/day_entry_test.rb index c27ffda..a0b49a4 100644 --- a/test/day_entry_test.rb +++ b/test/day_entry_test.rb @@ -37,13 +37,13 @@ class DayEntryTest < Test::Unit::TestCase entry = HCl::DayEntry.new(:id => '1', :notes => 'yourmom.', :hours => '1.0') HCl::DayEntry.stubs(:post) entry.append_note('hi world') - assert_equal 'yourmom. hi world', entry.notes + assert_equal "yourmom.\nhi world", entry.notes end should "append to an undefined note" do entry = HCl::DayEntry.new(:id => '1', :notes => nil, :hours => '1.0') HCl::DayEntry.stubs(:post) entry.append_note('hi world') - assert_equal ' hi world', entry.notes + assert_equal 'hi world', entry.notes end end diff --git a/test/test_helper.rb b/test/test_helper.rb index fa69a86..9e12bac 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -4,4 +4,4 @@ require 'rubygems' require 'test/unit' require 'hcl/app' require 'shoulda' -require 'mocha' +require 'mocha/setup' |
