zenhob-hcl 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ *.sw[nop]
2
+ pkg
data/.gitmodules ADDED
@@ -0,0 +1,3 @@
1
+ [submodule "ext/harvest"]
2
+ path = ext/harvest
3
+ url = git://github.com/aiaio/harvest.git
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2009 Zack Hobson <zack@opensourcery.com>, OpenSourcery LLC
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,95 @@
1
+ # hcl
2
+
3
+ HCl is a command-line tool for interacting with Harvest time sheets using the
4
+ [Harvest time tracking API][htt].
5
+
6
+ [htt]: http://www.getharvest.com/api/time_tracking
7
+
8
+ ## Quick Start
9
+
10
+ $ gem install zenhob-hcl --source=http://gems.github.com
11
+ $ hcl show [date]
12
+
13
+ ### Prerequisites
14
+
15
+ * Ruby (tested with 1.8.7)
16
+ * Ruby OpenSSL support (in debian/ubuntu: apt-get install libopenssl-ruby)
17
+ * RubyGems
18
+ * Trollop option-parsing library (gem install trollop)
19
+ * Chronic date-parsing library (gem install chronic)
20
+ * HighLine console input library (gem install highline)
21
+ * Jeweler packaging tool (needed to build the gem)
22
+
23
+ ## Usage
24
+
25
+ NOTE that *add* and *rm* are not yet implemented.
26
+
27
+ hcl show [date]
28
+ hcl tasks
29
+ hcl set <key> <value ...>
30
+ hcl unset <key>
31
+ hcl start (<task_alias> | <project_id> <task_id>) [msg ...]
32
+ hcl note <msg ...>
33
+ hcl stop
34
+ hcl add (<task_alias> | <project_id> <task_id>) <duration> [msg ...]
35
+ hcl rm [entry_id]
36
+
37
+ ### Starting a Timer
38
+
39
+ To start a new timer you need to identify the project and task. After you've
40
+ used the show command you can use the tasks command to view a cached list of
41
+ available tasks. The first two numbers in each row are the project and task
42
+ IDs. You need both values to start a timer:
43
+
44
+ $ hcl show
45
+ -------------
46
+ 0:00 total
47
+ $ hcl tasks
48
+ 1234 5678 ClientX Software Development
49
+ 1234 9876 ClientX Admin
50
+ $ hcl start 1234 5678 adding a new feature
51
+
52
+ ### Task Aliases
53
+
54
+ Since it's not practical to enter two long numbers every time you want to
55
+ identify a task, HCl supports task aliases:
56
+
57
+ $ hcl set task.xdev 1234 5678
58
+ $ hcl start xdev adding a new feature
59
+
60
+ ### Adding Notes to a Running Task
61
+
62
+ While a task is running you can append strings to the note for that task:
63
+
64
+ $ hcl note Found a good time
65
+ $ hcl note or not, whatever...
66
+
67
+ ### Stopping a Timer
68
+
69
+ The following command will stop a running timer (currently only one timer at
70
+ a time is supported):
71
+
72
+ $ hcl stop
73
+
74
+ ### Date Formats
75
+
76
+ Dates can be expressed in a variety of ways. See the [Chronic documentation][cd]
77
+ for more information about available date input formats. The following
78
+ commands show the timesheet for the specified day:
79
+
80
+ $ hcl show yesterday
81
+ $ hcl show last friday
82
+ $ hcl show 2 days ago
83
+ $ hcl show 1 week ago
84
+
85
+ [cd]: http://chronic.rubyforge.org/
86
+
87
+ ## Author
88
+
89
+ [Zack Hobson][zgh], [OpenSourcery LLC][os]
90
+
91
+ See LICENSE for copyright details.
92
+
93
+ [zgh]: mailto:zack@opensourcery.com
94
+ [os]: http://www.opensourcery.com/
95
+
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gem|
4
+ gem.name = "hcl"
5
+ gem.summary = "Harvest timesheets from the command-line"
6
+ gem.description = "HCl is a command-line client for manipulating Harvest time sheets."
7
+ gem.email = "zack@opensourcery.com"
8
+ gem.homepage = "http://github.com/zenhob/hcl"
9
+ gem.authors = ["Zack Hobson"]
10
+ gem.add_dependency "termios"
11
+ gem.add_dependency "trollop", ">= 1.10.2"
12
+ gem.add_dependency "chronic", ">= 0.2.3"
13
+ gem.add_dependency "highline", ">= 1.5.1"
14
+ end
15
+ rescue LoadError
16
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
17
+ end
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 1
4
+ :patch: 1
data/bin/hcl ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.dirname(__FILE__) + '/../lib'
4
+ $:.unshift File.dirname(__FILE__) + '/../ext/harvest/lib'
5
+
6
+ require 'hcl'
7
+
8
+ HCl.command *ARGV
9
+
data/hcl.gemspec ADDED
@@ -0,0 +1,62 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{hcl}
5
+ s.version = "0.1.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Zack Hobson"]
9
+ s.date = %q{2009-07-24}
10
+ s.default_executable = %q{hcl}
11
+ s.description = %q{HCl is a command-line client for manipulating Harvest time sheets.}
12
+ s.email = %q{zack@opensourcery.com}
13
+ s.executables = ["hcl"]
14
+ s.extra_rdoc_files = [
15
+ "LICENSE",
16
+ "README.markdown"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ ".gitmodules",
21
+ "LICENSE",
22
+ "README.markdown",
23
+ "Rakefile",
24
+ "VERSION.yml",
25
+ "bin/hcl",
26
+ "hcl.gemspec",
27
+ "hcl_conf.yml.example",
28
+ "lib/hcl.rb",
29
+ "lib/hcl/day_entry.rb",
30
+ "lib/hcl/project.rb",
31
+ "lib/hcl/task.rb",
32
+ "lib/hcl/timesheet_resource.rb"
33
+ ]
34
+ s.has_rdoc = true
35
+ s.homepage = %q{http://github.com/zenhob/hcl}
36
+ s.rdoc_options = ["--charset=UTF-8"]
37
+ s.require_paths = ["lib"]
38
+ s.rubygems_version = %q{1.3.1}
39
+ s.summary = %q{Harvest timesheets from the command-line}
40
+
41
+ if s.respond_to? :specification_version then
42
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
43
+ s.specification_version = 2
44
+
45
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
46
+ s.add_runtime_dependency(%q<termios>, [">= 0"])
47
+ s.add_runtime_dependency(%q<trollop>, [">= 1.10.2"])
48
+ s.add_runtime_dependency(%q<chronic>, [">= 0.2.3"])
49
+ s.add_runtime_dependency(%q<highline>, [">= 1.5.1"])
50
+ else
51
+ s.add_dependency(%q<termios>, [">= 0"])
52
+ s.add_dependency(%q<trollop>, [">= 1.10.2"])
53
+ s.add_dependency(%q<chronic>, [">= 0.2.3"])
54
+ s.add_dependency(%q<highline>, [">= 1.5.1"])
55
+ end
56
+ else
57
+ s.add_dependency(%q<termios>, [">= 0"])
58
+ s.add_dependency(%q<trollop>, [">= 1.10.2"])
59
+ s.add_dependency(%q<chronic>, [">= 0.2.3"])
60
+ s.add_dependency(%q<highline>, [">= 1.5.1"])
61
+ end
62
+ end
@@ -0,0 +1,3 @@
1
+ subdomain: mycompany
2
+ login: me@mycompany.com
3
+ password: secret
@@ -0,0 +1,53 @@
1
+
2
+ class HCl
3
+ class DayEntry < TimesheetResource
4
+ # Get the time sheet entries for a given day. If no date is provided
5
+ # defaults to today.
6
+ def self.all date = nil
7
+ url = date.nil? ? 'daily' : "daily/#{date.strftime '%j/%Y'}"
8
+ from_xml get(url)
9
+ end
10
+
11
+ def to_s
12
+ "#{client} #{project} #{task} (#{hours})"
13
+ end
14
+
15
+ def self.from_xml xml
16
+ doc = REXML::Document.new xml
17
+ Task.cache_tasks doc
18
+ doc.root.elements.collect('*/day_entry') do |day|
19
+ new xml_to_hash(day)
20
+ end
21
+ end
22
+
23
+ # Append a string to the notes for this task.
24
+ def append_note new_notes
25
+ # If I don't include hours it gets reset.
26
+ # This doens't appear to be the case for task and project.
27
+ DayEntry.post("daily/update/#{id}", <<-EOD)
28
+ <request>
29
+ <notes>#{notes << " #{new_notes}"}</notes>
30
+ <hours>#{hours}</hours>
31
+ </request>
32
+ EOD
33
+ end
34
+
35
+ def self.with_timer
36
+ all.detect {|t| t.running? }
37
+ end
38
+
39
+ def running?
40
+ !@data[:timer_started_at].nil? && !@data[:timer_started_at].empty?
41
+ end
42
+
43
+ def initialize *args
44
+ super
45
+ # TODO cache client/project names and ids
46
+ end
47
+
48
+ def toggle
49
+ DayEntry.get("daily/timer/#{id}")
50
+ self
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,3 @@
1
+ class HCl::Project < HCl::TimesheetResource
2
+ end
3
+
data/lib/hcl/task.rb ADDED
@@ -0,0 +1,44 @@
1
+ class HCl
2
+ class Task < TimesheetResource
3
+ def self.cache_tasks doc
4
+ tasks = []
5
+ doc.root.elements.collect('projects/project') do |project_elem|
6
+ project = Project.new xml_to_hash(project_elem)
7
+ tasks.concat(project_elem.elements.collect('tasks/task') do |task|
8
+ new xml_to_hash(task).merge(:project => project)
9
+ end)
10
+ end
11
+ File.open(File.join(ENV['HOME'],'.hcl_tasks'), 'w') do |f|
12
+ f.write tasks.uniq.to_yaml
13
+ end
14
+ end
15
+
16
+ def self.all
17
+ YAML.load File.read(File.join(ENV['HOME'],'.hcl_tasks'))
18
+ end
19
+
20
+ def self.find project_id, id
21
+ all.detect {|t| t.project.id == project_id && t.id == id }
22
+ end
23
+
24
+ def to_s
25
+ "#{project.name} #{name}"
26
+ end
27
+
28
+ def start *args
29
+ notes = args.join ' '
30
+ days = DayEntry.from_xml Task.post("daily/add", <<-EOT)
31
+ <request>
32
+ <notes>#{notes}</notes>
33
+ <hours></hours>
34
+ <project_id type="integer">#{project.id}</project_id>
35
+ <task_id type="integer">#{id}</task_id>
36
+ <spent_at type="date">#{Date.today}</spent_at>
37
+ </request>
38
+ EOT
39
+ days.first
40
+ end
41
+
42
+ end
43
+ end
44
+
@@ -0,0 +1,72 @@
1
+ class HCl
2
+ class TimesheetResource
3
+ def self.configure opts = nil
4
+ if opts
5
+ self.login = opts['login']
6
+ self.password = opts['password']
7
+ self.subdomain = opts['subdomain']
8
+ else
9
+ yield self
10
+ end
11
+ end
12
+
13
+ # configuration accessors
14
+ %w[ login password subdomain ].each do |config_var|
15
+ class_eval <<-EOC
16
+ def self.#{config_var}= arg
17
+ @@#{config_var} = arg
18
+ end
19
+ def self.#{config_var}
20
+ @@#{config_var}
21
+ end
22
+ EOC
23
+ end
24
+
25
+ def initialize params
26
+ @data = params
27
+ end
28
+
29
+ def self.get action
30
+ https_do Net::HTTP::Get, action
31
+ end
32
+
33
+ def self.post action, data
34
+ https_do Net::HTTP::Post, action, data
35
+ end
36
+
37
+ def self.https_do method_class, action, data = nil
38
+ https = Net::HTTP.new "#{subdomain}.harvestapp.com", 443
39
+ request = method_class.new "/#{action}"
40
+ https.use_ssl = true
41
+ request.basic_auth login, password
42
+ request.content_type = 'application/xml'
43
+ request['Accept'] = 'application/xml'
44
+ response = https.request request, data
45
+ return response.body
46
+ if response.kind_of? Net::HTTPSuccess
47
+ response.body
48
+ else
49
+ raise 'failure'
50
+ end
51
+ end
52
+
53
+ def id
54
+ @data[:id]
55
+ end
56
+
57
+ def method_missing method, *args
58
+ if @data.key? method.to_sym
59
+ @data[method]
60
+ else
61
+ super
62
+ end
63
+ end
64
+
65
+ def self.xml_to_hash elem
66
+ elem.elements.map { |e| e.name }.inject({}) do |a, f|
67
+ a[f.to_sym] = elem.elements[f].text if elem.elements[f]
68
+ a
69
+ end
70
+ end
71
+ end
72
+ end
data/lib/hcl.rb ADDED
@@ -0,0 +1,219 @@
1
+ require 'yaml'
2
+ require 'rexml/document'
3
+ require 'net/http'
4
+ require 'net/https'
5
+
6
+ require 'chronic'
7
+ require 'trollop'
8
+ require 'highline/import'
9
+
10
+ require 'hcl/timesheet_resource'
11
+ require 'hcl/project'
12
+ require 'hcl/task'
13
+ require 'hcl/day_entry'
14
+
15
+ # Workaround for annoying SSL warning:
16
+ # >> warning: peer certificate won't be verified in this SSL session
17
+ # http://www.5dollarwhitebox.org/drupal/node/64
18
+ class Net::HTTP
19
+ alias_method :old_initialize, :initialize
20
+ def initialize(*args)
21
+ old_initialize(*args)
22
+ @ssl_context = OpenSSL::SSL::SSLContext.new
23
+ @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
24
+ end
25
+ end
26
+
27
+ class HCl
28
+ VERSION_FILE = File.dirname(__FILE__) + '/../VERSION.yml'
29
+ SETTINGS_FILE = "#{ENV['HOME']}/.hcl_settings"
30
+ CONFIG_FILE = "#{ENV['HOME']}/.hcl_config"
31
+
32
+ class UnknownCommand < StandardError; end
33
+
34
+ def self.command *args
35
+ hcl = new.process_args(*args).run
36
+ end
37
+
38
+ def run
39
+ if @command
40
+ if respond_to? @command
41
+ send @command, *@args
42
+ else
43
+ raise UnknownCommand, "unrecognized command `#{@command}'"
44
+ end
45
+ else
46
+ show
47
+ end
48
+ end
49
+
50
+ def initialize
51
+ @version = YAML::load(File.read(VERSION_FILE))
52
+ read_config
53
+ read_settings
54
+ end
55
+
56
+ def version
57
+ [:major, :minor, :patch].map { |v| @version[v] }.join('.')
58
+ end
59
+
60
+ def process_args *args
61
+ version_string = version
62
+ Trollop::options(args) do
63
+ version "HCl #{version_string}"
64
+ stop_on %w[ show tasks set unset note add rm start stop ]
65
+ banner <<-EOM
66
+ HCl is a command-line client for manipulating Harvest time sheets.
67
+
68
+ Commands:
69
+ hcl show [date]
70
+ hcl tasks
71
+ hcl set <key> <value ...>
72
+ hcl start <task> [msg]
73
+ hcl stop [msg]
74
+ hcl note <msg>
75
+ hcl add <task> <duration> [msg]
76
+ hcl rm [entry_id]
77
+
78
+ Examples:
79
+ $ hcl tasks
80
+ $ hcl start 1234 4567 this is my log message
81
+ $ hcl set task.mytask 1234 4567
82
+ $ hcl start mytask this is my next log message
83
+ $ hcl show yesterday
84
+ $ hcl show last tuesday
85
+
86
+ Options:
87
+ EOM
88
+ end
89
+ @command = args.shift
90
+ @args = args
91
+ self
92
+ end
93
+
94
+ def tasks
95
+ Task.all.each do |task|
96
+ # TODO more information and formatting options
97
+ puts "#{task.project.id} #{task.id}\t#{task}"
98
+ end
99
+ end
100
+
101
+ def read_config
102
+ if File.exists? CONFIG_FILE
103
+ config = YAML::load File.read(CONFIG_FILE)
104
+ TimesheetResource.configure config
105
+ elsif File.exists? old_conf = File.dirname(__FILE__) + "/../hcl_conf.yml"
106
+ config = YAML::load File.read(old_conf)
107
+ TimesheetResource.configure config
108
+ write_config config
109
+ else
110
+ config = {}
111
+ puts "Please specify your Harvest credentials.\n"
112
+ config['login'] = ask("Email Address: ")
113
+ config['password'] = ask("Password: ") { |q| q.echo = false }
114
+ config['subdomain'] = ask("Subdomain: ")
115
+ TimesheetResource.configure config
116
+ write_config config
117
+ end
118
+ end
119
+
120
+ def write_config config
121
+ puts "Writing configuration to #{CONFIG_FILE}."
122
+ File.open(CONFIG_FILE, 'w') do |f|
123
+ f.write config.to_yaml
124
+ end
125
+ end
126
+
127
+ def read_settings
128
+ if File.exists? SETTINGS_FILE
129
+ @settings = YAML.load(File.read(SETTINGS_FILE))
130
+ else
131
+ @settings = {}
132
+ end
133
+ end
134
+
135
+ def write_settings
136
+ File.open(SETTINGS_FILE, 'w') do |f|
137
+ f.write @settings.to_yaml
138
+ end
139
+ end
140
+
141
+ def set key = nil, *args
142
+ if key.nil?
143
+ @settings.each do |k, v|
144
+ puts "#{k}: #{v}"
145
+ end
146
+ else
147
+ value = args.join(' ')
148
+ @settings ||= {}
149
+ @settings[key] = value
150
+ write_settings
151
+ end
152
+ end
153
+
154
+ def unset key
155
+ @settings.delete key
156
+ write_settings
157
+ end
158
+
159
+ def start *args
160
+ ident = args.shift
161
+ task = if @settings["task.#{ident}"]
162
+ Task.find *@settings["task.#{ident}"].split(/\s+/)
163
+ else
164
+ Task.find ident, args.shift
165
+ end
166
+ task.start(*args)
167
+ puts "Started timer for #{task}"
168
+ end
169
+
170
+ def stop
171
+ entry = DayEntry.with_timer
172
+ if entry
173
+ entry.toggle
174
+ puts "Stopped #{entry}"
175
+ else
176
+ puts "No running timers found."
177
+ end
178
+ end
179
+
180
+ def note *args
181
+ message = args.join ' '
182
+ entry = DayEntry.with_timer
183
+ if entry
184
+ entry.append_note message
185
+ puts "Added note '#{message}' to #{entry}."
186
+ else
187
+ puts "No running timers found."
188
+ end
189
+ end
190
+
191
+ def show *args
192
+ date = args.empty? ? nil : Chronic.parse(args.join(' '))
193
+ total_hours = 0.0
194
+ DayEntry.all(date).each do |day|
195
+ # TODO more information and formatting options
196
+ running = day.running? ? '(running) ' : ''
197
+ puts "\t#{as_hours day.hours}\t#{running}#{day.project} #{day.notes}"[0..78]
198
+ total_hours = total_hours + day.hours.to_f
199
+ end
200
+ puts "\t" + '-' * 13
201
+ puts "\t#{as_hours total_hours}\ttotal"
202
+ end
203
+
204
+ # Convert from decimal to a string of the form HH:MM.
205
+ def as_hours hours
206
+ minutes = hours.to_f * 60.0
207
+ sprintf "%d:%02d", (minutes / 60).to_i, (minutes % 60).to_i
208
+ end
209
+
210
+ def not_implemented *args
211
+ puts "not yet implemented"
212
+ end
213
+
214
+ # TODO implement the following commands
215
+ alias add not_implemented
216
+ alias rm not_implemented
217
+
218
+ end
219
+
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zenhob-hcl
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Zack Hobson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-07-24 00:00:00 -07:00
13
+ default_executable: hcl
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: termios
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: trollop
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.10.2
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: chronic
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.2.3
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: highline
47
+ type: :runtime
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 1.5.1
54
+ version:
55
+ description: HCl is a command-line client for manipulating Harvest time sheets.
56
+ email: zack@opensourcery.com
57
+ executables:
58
+ - hcl
59
+ extensions: []
60
+
61
+ extra_rdoc_files:
62
+ - LICENSE
63
+ - README.markdown
64
+ files:
65
+ - .gitignore
66
+ - .gitmodules
67
+ - LICENSE
68
+ - README.markdown
69
+ - Rakefile
70
+ - VERSION.yml
71
+ - bin/hcl
72
+ - hcl.gemspec
73
+ - hcl_conf.yml.example
74
+ - lib/hcl.rb
75
+ - lib/hcl/day_entry.rb
76
+ - lib/hcl/project.rb
77
+ - lib/hcl/task.rb
78
+ - lib/hcl/timesheet_resource.rb
79
+ has_rdoc: true
80
+ homepage: http://github.com/zenhob/hcl
81
+ post_install_message:
82
+ rdoc_options:
83
+ - --charset=UTF-8
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: "0"
91
+ version:
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: "0"
97
+ version:
98
+ requirements: []
99
+
100
+ rubyforge_project:
101
+ rubygems_version: 1.2.0
102
+ signing_key:
103
+ specification_version: 2
104
+ summary: Harvest timesheets from the command-line
105
+ test_files: []
106
+