zenhob-hcl 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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
+