summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorZack Hobson2009-08-09 18:53:44 -0700
committerZack Hobson2009-08-09 18:53:44 -0700
commit09513e8446030d508fbf22daf926ec0d9647ea5d (patch)
treec53edd78b8f6ced5933381ebe3eb72fa6cd95636 /lib
parent3c224404e03412c9e13a7aa161bba55d2c1a6b7a (diff)
downloadhcl-09513e8446030d508fbf22daf926ec0d9647ea5d.tar.bz2
Code cleanup and documentation.
Diffstat (limited to 'lib')
-rw-r--r--lib/hcl/app.rb249
-rw-r--r--lib/hcl/commands.rb90
-rw-r--r--lib/hcl/utility.rb7
3 files changed, 185 insertions, 161 deletions
diff --git a/lib/hcl/app.rb b/lib/hcl/app.rb
index 1afe2b3..a3f3153 100644
--- a/lib/hcl/app.rb
+++ b/lib/hcl/app.rb
@@ -11,6 +11,7 @@ require 'highline/import'
## app dependencies
require 'hcl/utility'
+require 'hcl/commands'
require 'hcl/timesheet_resource'
require 'hcl/project'
require 'hcl/task'
@@ -30,50 +31,61 @@ end
module HCl
class App
- include HCl::Utility
-
- SETTINGS_FILE = "#{ENV['HOME']}/.hcl_settings"
- CONFIG_FILE = "#{ENV['HOME']}/.hcl_config"
-
- class UnknownCommand < StandardError; end
+ include HCl::Utility
+ include HCl::Commands
+
+ SETTINGS_FILE = "#{ENV['HOME']}/.hcl_settings"
+ CONFIG_FILE = "#{ENV['HOME']}/.hcl_config"
+
+ class UnknownCommand < StandardError; end
+
+ def initialize
+ read_config
+ read_settings
+ end
- def self.command *args
- hcl = new.process_args(*args).run
- end
+ # Run the given command and arguments.
+ def self.command *args
+ hcl = new.process_args(*args).run
+ end
- def run
- begin
- if @command
- if respond_to? @command
- result = send @command, *@args
- if not result.nil?
- if result.respond_to? :to_a
- puts result.to_a.join(', ')
- elsif result.respond_to? :to_s
- puts result
+ # Return true if the string is a known command, false otherwise.
+ #
+ # @param [#to_s] command name of command
+ # @return [true, false]
+ def command? command
+ Commands.instance_methods.include? command.to_s
+ end
+
+ # Start the application.
+ def run
+ begin
+ if @command
+ if command? @command
+ result = send @command, *@args
+ if not result.nil?
+ if result.respond_to? :to_a
+ puts result.to_a.join(', ')
+ elsif result.respond_to? :to_s
+ puts result
+ end
end
+ else
+ raise UnknownCommand, "unrecognized command `#{@command}'"
end
else
- raise UnknownCommand, "unrecognized command `#{@command}'"
+ show
end
- else
- show
+ rescue TimesheetResource::Failure => e
+ puts "Internal failure. #{e}"
+ exit 1
end
- rescue TimesheetResource::Failure => e
- puts "Internal failure. #{e}"
- exit 1
end
- end
-
- def initialize
- read_config
- read_settings
- end
-
- def process_args *args
- Trollop::options(args) do
- stop_on %w[ show tasks set unset note add rm start stop ]
- banner <<-EOM
+
+ def process_args *args
+ Trollop::options(args) do
+ stop_on Commands.instance_methods
+ banner <<-EOM
HCl is a command-line client for manipulating Harvest time sheets.
Commands:
@@ -81,6 +93,7 @@ Commands:
hcl tasks
hcl aliases
hcl set <key> <value ...>
+ hcl unset <key>
hcl start <task> [msg]
hcl stop [msg]
hcl note <msg>
@@ -95,140 +108,54 @@ Examples:
Options:
EOM
- end
- @command = args.shift
- @args = args
- self
- end
-
- def tasks
- tasks = Task.all
- if tasks.empty?
- puts "No cached tasks. Run `hcl show' to populate the cache and try again."
- else
- tasks.each { |task| puts "#{task.project.id} #{task.id}\t#{task}" }
- end
- nil
- end
-
- def read_config
- 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)
- TimesheetResource.configure config
- write_config config
- else
- config = {}
- puts "Please specify your Harvest credentials.\n"
- config['login'] = ask("Email Address: ")
- config['password'] = ask("Password: ") { |q| q.echo = false }
- config['subdomain'] = ask("Subdomain: ")
- TimesheetResource.configure config
- write_config config
- end
- end
-
- def write_config config
- puts "Writing configuration to #{CONFIG_FILE}."
- File.open(CONFIG_FILE, 'w') do |f|
- f.write config.to_yaml
- end
- end
-
- def read_settings
- if File.exists? SETTINGS_FILE
- @settings = YAML.load(File.read(SETTINGS_FILE))
- else
- @settings = {}
- end
- end
-
- def write_settings
- File.open(SETTINGS_FILE, 'w') do |f|
- f.write @settings.to_yaml
- end
- nil
- end
-
- def set key = nil, *args
- if key.nil?
- @settings.each do |k, v|
- puts "#{k}: #{v}"
end
- else
- value = args.join(' ')
- @settings ||= {}
- @settings[key] = value
- write_settings
+ @command = args.shift
+ @args = args
+ self
end
- nil
- end
- def unset key
- @settings.delete key
- write_settings
- end
-
- def aliases
- @settings.keys.select { |s| s =~ /^task\./ }.map { |s| s.slice(5..-1) }
- end
-
- def start *args
- starting_time = args.detect {|x| x =~ /^\+\d*(\.|:)\d+$/ }
- if starting_time
- 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+/)
+ protected
+
+ def read_config
+ 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)
+ TimesheetResource.configure config
+ write_config config
else
- [ident, args.shift]
+ config = {}
+ puts "Please specify your Harvest credentials.\n"
+ config['login'] = ask("Email Address: ")
+ config['password'] = ask("Password: ") { |q| q.echo = false }
+ config['subdomain'] = ask("Subdomain: ")
+ TimesheetResource.configure config
+ write_config config
end
- task = Task.find *task_ids
- if task.nil?
- puts "Unknown project/task alias, try one of the following: #{aliases.join(', ')}."
- exit 1
end
- timer = task.start(:starting_time => starting_time, :note => args.join(' '))
- puts "Started timer for #{timer}."
- end
-
- def stop
- entry = DayEntry.with_timer
- if entry
- entry.toggle
- puts "Stopped #{entry}."
- else
- puts "No running timers found."
+
+ def write_config config
+ puts "Writing configuration to #{CONFIG_FILE}."
+ File.open(CONFIG_FILE, 'w') do |f|
+ f.write config.to_yaml
+ end
end
- end
-
- def note *args
- message = args.join ' '
- entry = DayEntry.with_timer
- if entry
- entry.append_note message
- puts "Added note '#{message}' to #{entry}."
- else
- puts "No running timers found."
+
+ def read_settings
+ if File.exists? SETTINGS_FILE
+ @settings = YAML.load(File.read(SETTINGS_FILE))
+ else
+ @settings = {}
+ end
end
- end
-
- def show *args
- date = args.empty? ? nil : Chronic.parse(args.join(' '))
- 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]
- total_hours = total_hours + day.hours.to_f
+
+ def write_settings
+ File.open(SETTINGS_FILE, 'w') do |f|
+ f.write @settings.to_yaml
+ end
+ nil
end
- puts "\t" + '-' * 13
- puts "\t#{as_hours total_hours}\ttotal"
- end
-
end
end
diff --git a/lib/hcl/commands.rb b/lib/hcl/commands.rb
new file mode 100644
index 0000000..5bc564f
--- /dev/null
+++ b/lib/hcl/commands.rb
@@ -0,0 +1,90 @@
+module HCl
+ module Commands
+ def tasks
+ tasks = Task.all
+ if tasks.empty?
+ puts "No cached tasks. Run `hcl show' to populate the cache and try again."
+ else
+ tasks.each { |task| puts "#{task.project.id} #{task.id}\t#{task}" }
+ end
+ nil
+ end
+
+ def set key = nil, *args
+ if key.nil?
+ @settings.each do |k, v|
+ puts "#{k}: #{v}"
+ end
+ else
+ value = args.join(' ')
+ @settings ||= {}
+ @settings[key] = value
+ write_settings
+ end
+ nil
+ end
+
+ def unset key
+ @settings.delete key
+ write_settings
+ end
+
+ def aliases
+ @settings.keys.select { |s| s =~ /^task\./ }.map { |s| s.slice(5..-1) }
+ end
+
+ def start *args
+ starting_time = args.detect {|x| x =~ /^\+\d*(\.|:)\d+$/ }
+ if starting_time
+ 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]
+ end
+ task = Task.find *task_ids
+ if task.nil?
+ puts "Unknown project/task alias, try one of the following: #{aliases.join(', ')}."
+ exit 1
+ end
+ timer = task.start(:starting_time => starting_time, :note => args.join(' '))
+ puts "Started timer for #{timer}."
+ end
+
+ def stop
+ entry = DayEntry.with_timer
+ if entry
+ entry.toggle
+ puts "Stopped #{entry}."
+ else
+ puts "No running timers found."
+ end
+ end
+
+ def note *args
+ message = args.join ' '
+ entry = DayEntry.with_timer
+ if entry
+ entry.append_note message
+ puts "Added note '#{message}' to #{entry}."
+ else
+ puts "No running timers found."
+ end
+ end
+
+ def show *args
+ date = args.empty? ? nil : Chronic.parse(args.join(' '))
+ 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]
+ total_hours = total_hours + day.hours.to_f
+ end
+ puts "\t" + '-' * 13
+ puts "\t#{as_hours total_hours}\ttotal"
+ end
+ end
+end
diff --git a/lib/hcl/utility.rb b/lib/hcl/utility.rb
index 18782f6..bbd5065 100644
--- a/lib/hcl/utility.rb
+++ b/lib/hcl/utility.rb
@@ -1,11 +1,18 @@
module HCl
module Utility
# Convert from decimal to a string of the form HH:MM.
+ #
+ # @param [#to_f] hours number of hours in decimal
+ # @return [String] of the form "HH:MM"
def as_hours hours
minutes = hours.to_f * 60.0
sprintf "%d:%02d", (minutes / 60).to_i, (minutes % 60).to_i
end
+ # Convert from a time span in hour or decimal format to a float.
+ #
+ # @param [String] time_string either "M:MM" or decimal
+ # @return [#to_f] converted to a floating-point number
def time2float time_string
if time_string =~ /:/
hours, minutes = time_string.split(':')