tracking 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.travis.yml +5 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +20 -0
- data/README.md +106 -0
- data/Rakefile +44 -0
- data/VERSION +1 -0
- data/bin/tracking +11 -0
- data/lib/tracking/cli.rb +204 -0
- data/lib/tracking/config.rb +63 -0
- data/lib/tracking/list.rb +78 -0
- data/lib/tracking.rb +21 -0
- data/test/helper.rb +39 -0
- data/test/test_cli.rb +47 -0
- metadata +131 -0
data/.document
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
# Add dependencies required to use your gem here.
|
4
|
+
|
5
|
+
# Add dependencies to develop your gem here.
|
6
|
+
# Include everything needed to run rake, tests, features, etc.
|
7
|
+
group :development do
|
8
|
+
gem "shoulda", ">= 0"
|
9
|
+
gem "rdoc", "~> 3.12"
|
10
|
+
gem "bundler", ">= 1.0.0"
|
11
|
+
gem "jeweler", "~> 1.8.4"
|
12
|
+
gem "simplecov", ">= 0"
|
13
|
+
gem "yard", ">= 0"
|
14
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Nicolas McCurdy
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
#tracking [![Build Status](https://secure.travis-ci.org/thenickperson/tracking.png?branch=master)](http://travis-ci.org/thenickperson/tracking)
|
2
|
+
A simple and configurable command line time tracker.
|
3
|
+
|
4
|
+
##Installation
|
5
|
+
|
6
|
+
`gem install tracking`
|
7
|
+
|
8
|
+
If you're on Windows, you should set up [Ruby Installer](http://rubyinstaller.org/downloads/) and [DevKit](https://github.com/oneclick/rubyinstaller/wiki/Development-Kit) first.
|
9
|
+
|
10
|
+
Also, tracking does not work on Ruby 1.8 (yet). Please upgrade to Ruby 1.9.3.
|
11
|
+
|
12
|
+
##Features
|
13
|
+
- concise and configurable display
|
14
|
+
- simple syntax
|
15
|
+
- portable data
|
16
|
+
|
17
|
+
##Ideas (future)
|
18
|
+
- hashtag-like tags for organizing tasks
|
19
|
+
- different ways to view your data
|
20
|
+
|
21
|
+
##Tutorial
|
22
|
+
```
|
23
|
+
$ tracking essay for English class
|
24
|
+
+-------+------------------------------------------+----------+
|
25
|
+
| start | task | elapsed |
|
26
|
+
+-------+------------------------------------------+----------+
|
27
|
+
| 12:00 | essay for English class | 00:00:00 |
|
28
|
+
+-------+------------------------------------------+----------+
|
29
|
+
$ tracking getting distracted on Reddit
|
30
|
+
+-------+------------------------------------------+----------+
|
31
|
+
| start | task | elapsed |
|
32
|
+
+-------+------------------------------------------+----------+
|
33
|
+
| 12:00 | essay for English class | 00:00:20 |
|
34
|
+
| 12:20 | getting distracted on Reddit | 00:00:00 |
|
35
|
+
+-------+------------------------------------------+----------+
|
36
|
+
$ tracking
|
37
|
+
+-------+------------------------------------------+----------+
|
38
|
+
| start | task | elapsed |
|
39
|
+
+-------+------------------------------------------+----------+
|
40
|
+
| 12:00 | essay for English class | 00:00:20 |
|
41
|
+
| 12:20 | getting distracted on Reddit | 00:00:05 |
|
42
|
+
+-------+------------------------------------------+----------+
|
43
|
+
$ tracking back to work
|
44
|
+
+-------+------------------------------------------+----------+
|
45
|
+
| start | task | elapsed |
|
46
|
+
+-------+------------------------------------------+----------+
|
47
|
+
| 12:00 | essay for English class | 00:00:20 |
|
48
|
+
| 12:20 | getting distracted on Reddit | 00:00:10 |
|
49
|
+
| 12:30 | back to work | 00:00:00 |
|
50
|
+
+-------+------------------------------------------+----------+
|
51
|
+
```
|
52
|
+
|
53
|
+
##Usage
|
54
|
+
```
|
55
|
+
Usage: tracking [mode]
|
56
|
+
display all tasks
|
57
|
+
<task> start a new task with the given text
|
58
|
+
-c, --clear delete all tasks
|
59
|
+
-d, --delete delete the last task
|
60
|
+
-h, --help displays this help information
|
61
|
+
```
|
62
|
+
|
63
|
+
##Configuration
|
64
|
+
The config file for tracking is located in `~/.tracking/config.yml`.
|
65
|
+
|
66
|
+
The default settings are listed below, along with a description of each setting.
|
67
|
+
```ruby
|
68
|
+
# path to the data file (string, ~ can be used)
|
69
|
+
:data_file: ~/.tracking/data.csv
|
70
|
+
# number of lines to be displayed at once by default (integer)
|
71
|
+
:lines: 10
|
72
|
+
# width of the task name column, in characters (integer)
|
73
|
+
:task_width: 40
|
74
|
+
# format to use for elapsed time display (:colons or :letters)
|
75
|
+
:elapsed_format: :colons
|
76
|
+
# toggle header describing tracking's display columns (true or false)
|
77
|
+
:show_header: true
|
78
|
+
# toggle display of seconds in elapsed time (true of false)
|
79
|
+
:show_elapsed_seconds: false
|
80
|
+
```
|
81
|
+
|
82
|
+
##Elapsed Time Formats
|
83
|
+
Elapsed times are displayed in this order: days, hours, minutes, seconds (if enabled)
|
84
|
+
- hide elapsed seconds
|
85
|
+
- colons: `01:02:03` (default)
|
86
|
+
- letters: `01d 02h 03m`
|
87
|
+
- show elapsed seconds
|
88
|
+
- colons: `01:02:03:04`
|
89
|
+
- letters: `01d 02h 03m 04s`
|
90
|
+
|
91
|
+
##Contributing to tracking
|
92
|
+
- Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
93
|
+
- Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
94
|
+
- Fork the project.
|
95
|
+
- Start a feature/bugfix branch.
|
96
|
+
- Commit and push until you are happy with your contribution.
|
97
|
+
- Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
98
|
+
- Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
99
|
+
|
100
|
+
##Similar Projects
|
101
|
+
- [timetrap](https://github.com/samg/timetrap)
|
102
|
+
- [d-time-tracker](https://github.com/DanielVF/d-time-tracker)
|
103
|
+
|
104
|
+
##Copyright
|
105
|
+
Copyright (c) 2012 Nicolas McCurdy. See LICENSE.txt for
|
106
|
+
further details.
|
data/Rakefile
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "tracking"
|
18
|
+
gem.homepage = "http://github.com/thenickperson/tracking"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = "A simple and configurable command line time tracker."
|
21
|
+
gem.description = "See README for more information."
|
22
|
+
gem.email = "thenickperson@gmail.com"
|
23
|
+
gem.authors = ["Nicolas McCurdy"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "Code coverage detail"
|
36
|
+
task :simplecov do
|
37
|
+
ENV['COVERAGE'] = "true"
|
38
|
+
Rake::Task['spec'].execute
|
39
|
+
end
|
40
|
+
|
41
|
+
task :default => :test
|
42
|
+
|
43
|
+
require 'yard'
|
44
|
+
YARD::Rake::YardocTask.new
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
data/bin/tracking
ADDED
data/lib/tracking/cli.rb
ADDED
@@ -0,0 +1,204 @@
|
|
1
|
+
#tracking's command line interface (view)
|
2
|
+
|
3
|
+
#imports
|
4
|
+
require "optparse"
|
5
|
+
|
6
|
+
#view module methods
|
7
|
+
module Tracking
|
8
|
+
module CLI
|
9
|
+
|
10
|
+
extend self
|
11
|
+
|
12
|
+
#displays the entire list
|
13
|
+
def display
|
14
|
+
#length of strings produced by the current elapsed time format
|
15
|
+
elapsed_time_length = List.get_elapsed_time(Time.now, Time.now).length
|
16
|
+
#horizontal border for the top or bottom of tracking's display
|
17
|
+
horizontal_border = "+-------+-#{"-"*Config[:task_width]}-+-#{"-"*elapsed_time_length}-+"
|
18
|
+
#header row describing tracking's display columns
|
19
|
+
header = "| start | #{pad("task", Config[:task_width], :center)} | #{pad("elapsed", elapsed_time_length, :center)} |"
|
20
|
+
#intro message, displayed when no valid tasks are found
|
21
|
+
introduction = <<EOF
|
22
|
+
+---------------------------------------+
|
23
|
+
| You haven't started any tasks yet! :( |
|
24
|
+
| |
|
25
|
+
| Run this to begin your first task: |
|
26
|
+
| tracking starting some work |
|
27
|
+
+---------------------------------------+
|
28
|
+
EOF
|
29
|
+
#read data file
|
30
|
+
data = []
|
31
|
+
valid_lines = 0
|
32
|
+
invalid_lines = 0
|
33
|
+
data_file = CSV.open($data_file, "r", $csv_options)
|
34
|
+
file_length = data_file.readlines.size
|
35
|
+
data_file.seek(0)
|
36
|
+
data_file.each_with_index do |line, index|
|
37
|
+
if index+1 > file_length - Config[:lines]
|
38
|
+
data << line
|
39
|
+
end
|
40
|
+
end
|
41
|
+
#display data
|
42
|
+
for i in 0..data.length-1
|
43
|
+
if data[i].length == 2
|
44
|
+
begin
|
45
|
+
#grab and reformat data
|
46
|
+
time_string = Time.parse(data[i][0]).strftime("%H:%M")
|
47
|
+
task_string = data[i][1].chomp
|
48
|
+
start_time = Time.parse(data[i][0])
|
49
|
+
end_time = i<data.length-1 ? Time.parse(data[i+1][0]) : Time.now
|
50
|
+
elapsed_string = List.get_elapsed_time(start_time,end_time)
|
51
|
+
#format data into lines
|
52
|
+
lines = []
|
53
|
+
split_task(task_string).each_with_index do |task_line, i|
|
54
|
+
col_1 = pad(i==0 ? time_string : nil, 5)
|
55
|
+
col_2 = pad(task_line, Config[:task_width])
|
56
|
+
col_3 = pad(i==0 ? elapsed_string : nil, elapsed_time_length)
|
57
|
+
lines << "| #{col_1} | #{col_2} | #{col_3} |"
|
58
|
+
end
|
59
|
+
#display lines
|
60
|
+
if valid_lines == 0
|
61
|
+
puts horizontal_border
|
62
|
+
if Config[:show_header]
|
63
|
+
puts header
|
64
|
+
puts horizontal_border
|
65
|
+
end
|
66
|
+
end
|
67
|
+
lines.each { |line| puts line }
|
68
|
+
valid_lines += 1
|
69
|
+
rescue
|
70
|
+
invalid_lines += 1
|
71
|
+
end
|
72
|
+
else
|
73
|
+
invalid_lines += 1
|
74
|
+
end
|
75
|
+
end
|
76
|
+
#display intro, if needed
|
77
|
+
if valid_lines > 0
|
78
|
+
puts horizontal_border
|
79
|
+
else
|
80
|
+
puts introduction
|
81
|
+
end
|
82
|
+
#display a warning, if needed
|
83
|
+
if invalid_lines > 0
|
84
|
+
warn "Error: #{invalid_lines} invalid line#{"s" if invalid_lines > 1} found in data file."
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
#pads tasks with whitespace to align them for display
|
89
|
+
def pad(string, length, align=:left)
|
90
|
+
if string == nil
|
91
|
+
return " " * length
|
92
|
+
elsif string.length >= length
|
93
|
+
return string
|
94
|
+
else
|
95
|
+
difference = (length - string.length).to_f
|
96
|
+
case align
|
97
|
+
when :left
|
98
|
+
return string + " " * difference
|
99
|
+
when :right
|
100
|
+
return " " * difference + string
|
101
|
+
when :center
|
102
|
+
return " "*(difference/2).floor + string + " "*(difference/2).ceil
|
103
|
+
else
|
104
|
+
return string
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
#word wraps tasks for display
|
110
|
+
def split_task task
|
111
|
+
|
112
|
+
#if the task fits
|
113
|
+
if task.length <= Config[:task_width]
|
114
|
+
return [task]
|
115
|
+
|
116
|
+
#if the task needs to be split
|
117
|
+
else
|
118
|
+
words = task.split(" ")
|
119
|
+
split = []
|
120
|
+
line = ""
|
121
|
+
words.each do |word|
|
122
|
+
|
123
|
+
#if the word needs to be split
|
124
|
+
if word.length > Config[:task_width]
|
125
|
+
#add the start of the word onto the first line (even if it has already started)
|
126
|
+
while line.length < Config[:task_width]
|
127
|
+
line += word[0]
|
128
|
+
word = word[1..-1]
|
129
|
+
end
|
130
|
+
split << line
|
131
|
+
#split the rest of the word up onto new lines
|
132
|
+
split_word = word.scan(%r[.{1,#{Config[:task_width]}}])
|
133
|
+
split_word[0..-2].each do |word_section|
|
134
|
+
split << word_section
|
135
|
+
end
|
136
|
+
line = split_word.last
|
137
|
+
|
138
|
+
#if the word would fit on a new line
|
139
|
+
elsif (line + word).length > Config[:task_width]
|
140
|
+
split << line.chomp
|
141
|
+
line = word
|
142
|
+
|
143
|
+
#if the word can be added to this line
|
144
|
+
else
|
145
|
+
line += word
|
146
|
+
end
|
147
|
+
|
148
|
+
#add a space to the end of the last word, if it would fit
|
149
|
+
line += " " if line.length != Config[:task_width]
|
150
|
+
|
151
|
+
end
|
152
|
+
split << line
|
153
|
+
return split
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
#use option parser to parse command line arguments
|
158
|
+
def parse
|
159
|
+
#options = {}
|
160
|
+
done = false
|
161
|
+
|
162
|
+
OptionParser.new do |opts|
|
163
|
+
#setup
|
164
|
+
version_path = File.expand_path("../../VERSION", File.dirname(__FILE__))
|
165
|
+
opts.version = File.exist?(version_path) ? File.read(version_path) : ""
|
166
|
+
#start of help text
|
167
|
+
opts.banner = "Usage: tracking [mode]"
|
168
|
+
opts.separator " display tasks"
|
169
|
+
opts.separator " <task description> start a new task with the given text (spaces allowed)"
|
170
|
+
#modes
|
171
|
+
opts.on("-c", "--clear", "delete all tasks") do
|
172
|
+
List.clear
|
173
|
+
puts "List cleared."
|
174
|
+
done = true
|
175
|
+
return
|
176
|
+
end
|
177
|
+
opts.on("-d", "--delete", "delete the last task") do
|
178
|
+
List.delete
|
179
|
+
display
|
180
|
+
done = true
|
181
|
+
return
|
182
|
+
end
|
183
|
+
opts.on("-h", "--help", "display this help information") do
|
184
|
+
puts opts
|
185
|
+
done = true
|
186
|
+
return
|
187
|
+
end
|
188
|
+
end.parse!
|
189
|
+
|
190
|
+
#basic modes (display and add)
|
191
|
+
if not done
|
192
|
+
if ARGV.count == 0
|
193
|
+
#display all tasks
|
194
|
+
display
|
195
|
+
else
|
196
|
+
#start a new task
|
197
|
+
List.add ARGV.join(" ").gsub("\t","")
|
198
|
+
display
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
#utilities for tracking's config file
|
2
|
+
|
3
|
+
#imports
|
4
|
+
require "yaml"
|
5
|
+
|
6
|
+
#config module methods
|
7
|
+
module Tracking
|
8
|
+
# similar to Sam Goldstein's config.rb for timetrap
|
9
|
+
# @see https://github.com/samg/timetrap/
|
10
|
+
module Config
|
11
|
+
|
12
|
+
extend self
|
13
|
+
|
14
|
+
PATH = File.join(ENV["HOME"], ".tracking", "config.yml")
|
15
|
+
|
16
|
+
#default values
|
17
|
+
def defaults
|
18
|
+
{
|
19
|
+
# path to the data file (string, ~ can be used)
|
20
|
+
:data_file => "~/.tracking/data.csv",
|
21
|
+
# number of lines to be displayed at once by default (integer)
|
22
|
+
:lines => 10,
|
23
|
+
# width of the task name column, in characters (integer)
|
24
|
+
:task_width => 40,
|
25
|
+
# format to use for elapsed time display (:colons or :letters)
|
26
|
+
:elapsed_format => :colons,
|
27
|
+
# toggle header describing tracking's display columns (true or false)
|
28
|
+
:show_header => true,
|
29
|
+
# toggle display of seconds in elapsed time (true of false)
|
30
|
+
:show_elapsed_seconds => false
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
#accessor for values in the config
|
35
|
+
def [] key
|
36
|
+
data = YAML.load_file PATH
|
37
|
+
defaults.merge(data)[key]
|
38
|
+
end
|
39
|
+
|
40
|
+
#setter for keys in config
|
41
|
+
def []= key, value
|
42
|
+
data = YAML.load_file PATH
|
43
|
+
configs = defaults.merge(data)
|
44
|
+
configs[key] = value
|
45
|
+
File.open(PATH, "w") do |fh|
|
46
|
+
fh.puts(configs.to_yaml)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
#writes the config file path
|
51
|
+
def write
|
52
|
+
configs = if File.exist? PATH
|
53
|
+
defaults.merge(YAML.load_file PATH)
|
54
|
+
else
|
55
|
+
defaults
|
56
|
+
end
|
57
|
+
File.open(PATH, "w") do |fh|
|
58
|
+
fh.puts configs.to_yaml
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
#tracking's core
|
2
|
+
|
3
|
+
#imports
|
4
|
+
require "yaml"
|
5
|
+
require "time"
|
6
|
+
require "csv"
|
7
|
+
|
8
|
+
#model/controller module methods
|
9
|
+
module Tracking
|
10
|
+
module List
|
11
|
+
|
12
|
+
extend self
|
13
|
+
|
14
|
+
Config = YAML.load_file(ENV["HOME"] + "/.tracking/config.yml")
|
15
|
+
$data_file = File.expand_path(Config[:data_file])
|
16
|
+
$csv_options = { :col_sep => "\t" }
|
17
|
+
|
18
|
+
#adds an item to the list
|
19
|
+
def add item
|
20
|
+
date = Time.now.to_s
|
21
|
+
File.open($data_file, "a") do |file|
|
22
|
+
file << [ date, item ].to_csv($csv_options)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
#deletes an item from the list
|
27
|
+
def delete
|
28
|
+
lines = File.readlines $data_file
|
29
|
+
lines.pop #or delete specific lines in the future
|
30
|
+
File.open($data_file, "w") do |file|
|
31
|
+
lines.each do |line|
|
32
|
+
file << line
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
#clears the entire list
|
38
|
+
def clear
|
39
|
+
FileUtils.rm $data_file
|
40
|
+
FileUtils.touch $data_file
|
41
|
+
end
|
42
|
+
|
43
|
+
#gets and formats the amount of time passed between two times
|
44
|
+
def get_elapsed_time(time1, time2)
|
45
|
+
#calculate the elapsed time and break it down into different units
|
46
|
+
seconds = (time2 - time1).floor
|
47
|
+
minutes = hours = days = 0
|
48
|
+
if seconds >= 60
|
49
|
+
minutes = seconds / 60
|
50
|
+
seconds = seconds % 60
|
51
|
+
if minutes >= 60
|
52
|
+
hours = minutes / 60
|
53
|
+
minutes = minutes % 60
|
54
|
+
if hours >= 24
|
55
|
+
days = hours / 24
|
56
|
+
hours = hours % 24
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
#return a string of the formatted elapsed time
|
61
|
+
case Config[:elapsed_format]
|
62
|
+
when :colons
|
63
|
+
if Config[:show_elapsed_seconds]
|
64
|
+
return "%02d:%02d:%02d:%02d" % [days, hours, minutes, seconds]
|
65
|
+
else
|
66
|
+
return "%02d:%02d:%02d" % [days, hours, minutes]
|
67
|
+
end
|
68
|
+
when :letters
|
69
|
+
if Config[:show_elapsed_seconds]
|
70
|
+
return "%02dd %02dh %02dm %02ds" % [days, hours, minutes, seconds]
|
71
|
+
else
|
72
|
+
return "%02dd %02dh %02dm" % [days, hours, minutes]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
data/lib/tracking.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
|
3
|
+
require File.join(File.dirname(__FILE__), "tracking", "config")
|
4
|
+
|
5
|
+
#create ~/.tracking
|
6
|
+
if not File.exist? File.join(ENV["HOME"], ".tracking")
|
7
|
+
Dir.mkdir File.join(ENV["HOME"], ".tracking")
|
8
|
+
end
|
9
|
+
|
10
|
+
#create config file
|
11
|
+
if not File.exist? File.join(ENV["HOME"], ".tracking", "config.yml")
|
12
|
+
Tracking::Config.write
|
13
|
+
end
|
14
|
+
|
15
|
+
#create data file
|
16
|
+
if not File.exist? File.expand_path Tracking::Config[:data_file]
|
17
|
+
FileUtils.touch File.expand_path Tracking::Config[:data_file]
|
18
|
+
end
|
19
|
+
|
20
|
+
require File.join(File.dirname(__FILE__), "tracking", "list")
|
21
|
+
require File.join(File.dirname(__FILE__), "tracking", "cli")
|
data/test/helper.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'test/unit'
|
11
|
+
require 'shoulda'
|
12
|
+
|
13
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
15
|
+
require 'tracking'
|
16
|
+
|
17
|
+
class Test::Unit::TestCase
|
18
|
+
|
19
|
+
@tracking_path = File.join(File.dirname(__FILE__), "..", "bin", "tracking")
|
20
|
+
@tracking_command = "ruby #{@tracking_path}"
|
21
|
+
|
22
|
+
def capture_output &block
|
23
|
+
original_stdout = $stdout
|
24
|
+
$stdout = fake = StringIO.new
|
25
|
+
begin
|
26
|
+
yield
|
27
|
+
ensure
|
28
|
+
$stdout = original_stdout
|
29
|
+
end
|
30
|
+
fake.string
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_command args=""
|
34
|
+
capture_output do
|
35
|
+
system "#{@tracking_command} #{args}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
data/test/test_cli.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
require 'helper'
|
3
|
+
|
4
|
+
class TestCLI < Test::Unit::TestCase
|
5
|
+
context "Tracking's CLI" do
|
6
|
+
|
7
|
+
should "clear list (to prepare for other tests)" do
|
8
|
+
#test_command "-c"
|
9
|
+
capture_output { Tracking::List.clear }
|
10
|
+
end
|
11
|
+
|
12
|
+
should "display empty list" do
|
13
|
+
#test_command
|
14
|
+
capture_output { Tracking::CLI.display }
|
15
|
+
end
|
16
|
+
|
17
|
+
should "add a task" do
|
18
|
+
#test_command "first task"
|
19
|
+
capture_output { Tracking::List.add "first task" }
|
20
|
+
end
|
21
|
+
|
22
|
+
should "add another task" do
|
23
|
+
#test_command "second task"
|
24
|
+
capture_output { Tracking::List.add "second task" }
|
25
|
+
end
|
26
|
+
|
27
|
+
should "display list with two items" do
|
28
|
+
#test_command
|
29
|
+
capture_output { Tracking::CLI.display }
|
30
|
+
end
|
31
|
+
|
32
|
+
should "delete the second task" do
|
33
|
+
#test_command "-d"
|
34
|
+
capture_output { Tracking::List.delete }
|
35
|
+
end
|
36
|
+
|
37
|
+
should "clear list" do
|
38
|
+
#test_command "-c"
|
39
|
+
capture_output { Tracking::List.clear }
|
40
|
+
end
|
41
|
+
|
42
|
+
should "display help information (run from the system's shell)" do
|
43
|
+
test_command "-h"
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
metadata
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tracking
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Nicolas McCurdy
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-03 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: shoulda
|
16
|
+
requirement: &16314220 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *16314220
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rdoc
|
27
|
+
requirement: &16311500 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '3.12'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *16311500
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: bundler
|
38
|
+
requirement: &16252580 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.0.0
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *16252580
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: jeweler
|
49
|
+
requirement: &16248240 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.8.4
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *16248240
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: simplecov
|
60
|
+
requirement: &16261600 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *16261600
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: yard
|
71
|
+
requirement: &16260300 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *16260300
|
80
|
+
description: See README for more information.
|
81
|
+
email: thenickperson@gmail.com
|
82
|
+
executables:
|
83
|
+
- tracking
|
84
|
+
extensions: []
|
85
|
+
extra_rdoc_files:
|
86
|
+
- LICENSE.txt
|
87
|
+
- README.md
|
88
|
+
files:
|
89
|
+
- .document
|
90
|
+
- .travis.yml
|
91
|
+
- Gemfile
|
92
|
+
- LICENSE.txt
|
93
|
+
- README.md
|
94
|
+
- Rakefile
|
95
|
+
- VERSION
|
96
|
+
- bin/tracking
|
97
|
+
- lib/tracking.rb
|
98
|
+
- lib/tracking/cli.rb
|
99
|
+
- lib/tracking/config.rb
|
100
|
+
- lib/tracking/list.rb
|
101
|
+
- test/helper.rb
|
102
|
+
- test/test_cli.rb
|
103
|
+
homepage: http://github.com/thenickperson/tracking
|
104
|
+
licenses:
|
105
|
+
- MIT
|
106
|
+
post_install_message:
|
107
|
+
rdoc_options: []
|
108
|
+
require_paths:
|
109
|
+
- lib
|
110
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
111
|
+
none: false
|
112
|
+
requirements:
|
113
|
+
- - ! '>='
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
segments:
|
117
|
+
- 0
|
118
|
+
hash: 1085094097203907066
|
119
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
|
+
none: false
|
121
|
+
requirements:
|
122
|
+
- - ! '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
requirements: []
|
126
|
+
rubyforge_project:
|
127
|
+
rubygems_version: 1.8.15
|
128
|
+
signing_key:
|
129
|
+
specification_version: 3
|
130
|
+
summary: A simple and configurable command line time tracker.
|
131
|
+
test_files: []
|