ReinH-track 1.0.2 → 1.1.0
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/Rakefile +1 -1
- data/bin/track +6 -6
- data/lib/track.rb +43 -37
- data/lib/track/entry.rb +46 -0
- data/lib/track/store.rb +30 -0
- data/spec/spec_helper.rb +2 -2
- data/spec/track/entry_spec.rb +50 -0
- data/spec/track/store_spec.rb +29 -0
- data/spec/track_spec.rb +96 -64
- metadata +8 -2
data/Rakefile
CHANGED
data/bin/track
CHANGED
@@ -3,8 +3,7 @@
|
|
3
3
|
require 'yaml'
|
4
4
|
require File.join(File.dirname(__FILE__),'..','lib','track')
|
5
5
|
|
6
|
-
|
7
|
-
config_file = "#{home}/.track.yml"
|
6
|
+
config_file = File.join(ENV['HOME'], ".track.yml")
|
8
7
|
|
9
8
|
banner = <<-BANNER
|
10
9
|
Usage: track command
|
@@ -12,7 +11,8 @@ Usage: track command
|
|
12
11
|
Available commands:
|
13
12
|
|
14
13
|
<project> <task> => start a task on a project
|
15
|
-
stop
|
14
|
+
stop => stop a started task
|
15
|
+
cat => print the current timesheet
|
16
16
|
|
17
17
|
BANNER
|
18
18
|
|
@@ -20,8 +20,8 @@ if ARGV.empty?
|
|
20
20
|
puts banner
|
21
21
|
exit 0
|
22
22
|
else
|
23
|
-
options = nil
|
24
23
|
options = YAML.load_file(config_file) if File.size?(config_file)
|
25
|
-
|
26
|
-
|
24
|
+
Store.open do |track|
|
25
|
+
track.run(ARGV,options)
|
26
|
+
end
|
27
27
|
end
|
data/lib/track.rb
CHANGED
@@ -1,50 +1,33 @@
|
|
1
1
|
require 'date'
|
2
2
|
|
3
|
+
$:.unshift File.dirname(__FILE__)
|
4
|
+
require 'track/store'
|
5
|
+
require 'track/entry'
|
6
|
+
|
3
7
|
class Track
|
4
|
-
attr_reader :options, :projects
|
5
|
-
|
6
|
-
@options = options
|
7
|
-
@projects = @options['projects'] || {}
|
8
|
-
end
|
8
|
+
attr_reader :options, :projects, :entries
|
9
|
+
attr_writer :options
|
9
10
|
|
10
|
-
def
|
11
|
-
|
11
|
+
def initialize
|
12
|
+
@entries = []
|
12
13
|
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
def start(*args)
|
17
|
-
stop
|
18
|
-
project_name = args.shift
|
19
|
-
description = args.join(' ').strip
|
20
|
-
project = projects[project_name] || project_name
|
15
|
+
def options; @options ||= {} end
|
16
|
+
def projects; @projects ||= options['projects'] || {} end
|
21
17
|
|
22
|
-
|
18
|
+
def ==(other)
|
19
|
+
[self.options, self.entries] == [other.options, other.entries]
|
23
20
|
end
|
24
21
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
file.write(lines.join)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def write_line(project, description)
|
36
|
-
line = "[#{time_string} - #{placeholder}] "
|
37
|
-
line << project if project
|
38
|
-
line << ":\t" << description unless description.empty?
|
39
|
-
File.open(log_filename, 'a') do |file|
|
40
|
-
file.puts(line)
|
22
|
+
def run(argv, options=nil)
|
23
|
+
self.options = options
|
24
|
+
case argv.first
|
25
|
+
when 'stop' ; stop
|
26
|
+
when 'cat' ; cat
|
27
|
+
else start(*argv)
|
41
28
|
end
|
42
29
|
end
|
43
30
|
|
44
|
-
def time_string(time = Time.now)
|
45
|
-
time.strftime('%H:%M')
|
46
|
-
end
|
47
|
-
|
48
31
|
def log_filename
|
49
32
|
str = options['filename'] || 'track'
|
50
33
|
str += '-'
|
@@ -53,7 +36,30 @@ class Track
|
|
53
36
|
return str
|
54
37
|
end
|
55
38
|
|
56
|
-
def
|
57
|
-
|
39
|
+
def last_entry
|
40
|
+
entries.last
|
58
41
|
end
|
42
|
+
|
43
|
+
def add_entry(project, description=nil)
|
44
|
+
entry = Entry.new(Time.now, nil, project, description)
|
45
|
+
entries << entry
|
46
|
+
entry
|
47
|
+
end
|
48
|
+
|
49
|
+
def start(project, *description)
|
50
|
+
stop
|
51
|
+
description = description.join(' ')
|
52
|
+
project = projects[project] || project
|
53
|
+
add_entry(project, description)
|
54
|
+
end
|
55
|
+
|
56
|
+
def stop
|
57
|
+
return if entries.empty? || last_entry.stopped?
|
58
|
+
last_entry.stop
|
59
|
+
end
|
60
|
+
|
61
|
+
def cat
|
62
|
+
$stdout.puts(entries.map{|e|e.to_s}.join("\n"))
|
63
|
+
end
|
64
|
+
|
59
65
|
end
|
data/lib/track/entry.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
class Entry
|
2
|
+
attr_reader :start_time, :stop_time, :project, :description
|
3
|
+
def initialize(start_time, stop_time, project, description)
|
4
|
+
@start_time = start_time
|
5
|
+
@stop_time = stop_time
|
6
|
+
@project = project
|
7
|
+
@description = description
|
8
|
+
end
|
9
|
+
|
10
|
+
def stopped?
|
11
|
+
not stop_time.nil?
|
12
|
+
end
|
13
|
+
|
14
|
+
def stop
|
15
|
+
@stop_time = Time.now
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
line = "[#{start_string} - #{stop_string}] "
|
20
|
+
line << project if project
|
21
|
+
line << ":\t" << description unless description.nil? || description.empty?
|
22
|
+
line
|
23
|
+
end
|
24
|
+
|
25
|
+
def start_string
|
26
|
+
time_string(start_time)
|
27
|
+
end
|
28
|
+
|
29
|
+
def stop_string
|
30
|
+
if stop_time
|
31
|
+
time_string(stop_time)
|
32
|
+
else
|
33
|
+
placeholder
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def time_string(time = Time.now)
|
40
|
+
time.strftime('%H:%M')
|
41
|
+
end
|
42
|
+
|
43
|
+
def placeholder
|
44
|
+
"--:--"
|
45
|
+
end
|
46
|
+
end
|
data/lib/track/store.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Store
|
2
|
+
class << self
|
3
|
+
|
4
|
+
def store(obj, options={})
|
5
|
+
File.open(db_filename,'w') do |file|
|
6
|
+
file.write Marshal.dump(obj)
|
7
|
+
end
|
8
|
+
return obj
|
9
|
+
end
|
10
|
+
|
11
|
+
def load
|
12
|
+
if File.size?(db_filename)
|
13
|
+
Marshal.load(File.read(db_filename))
|
14
|
+
else
|
15
|
+
Track.new
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def open
|
20
|
+
track = load
|
21
|
+
yield track
|
22
|
+
store track
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def db_filename
|
27
|
+
File.join(ENV['HOME'], '.trackdb')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
$TESTING=true
|
2
|
-
$:.push File.join(File.dirname(__FILE__), '..', '
|
3
|
-
|
2
|
+
$:.push File.join(File.dirname(__FILE__), '..', 'lib')
|
3
|
+
require 'track'
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Entry do
|
4
|
+
|
5
|
+
describe "#stopped?" do
|
6
|
+
it "should be false if the entry is not yet stopped" do
|
7
|
+
Entry.new(Time.now, nil, "Project", "description").stopped?.should be_false
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#stop" do
|
12
|
+
it "should stop the entry" do
|
13
|
+
started_entry = Entry.new(Time.now, nil, "Project", "description")
|
14
|
+
started_entry.stop
|
15
|
+
started_entry.should be_stopped
|
16
|
+
end
|
17
|
+
end
|
18
|
+
describe "#to_s" do
|
19
|
+
before do
|
20
|
+
@time = Time.now
|
21
|
+
@entry = Entry.new(@time, nil, "Project", "Description")
|
22
|
+
end
|
23
|
+
|
24
|
+
it "includes the start time" do
|
25
|
+
@entry.to_s.should include(@entry.start_string)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "includes a placeholder for the end time" do
|
29
|
+
@entry.to_s.should include('--:--')
|
30
|
+
end
|
31
|
+
|
32
|
+
it "wraps the times in []" do
|
33
|
+
@entry.to_s.should match(/\[.+\]/)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "includes the project name followed by a \":\"" do
|
37
|
+
@entry.to_s.should include("Project:")
|
38
|
+
end
|
39
|
+
|
40
|
+
it "includes the description" do
|
41
|
+
@entry.to_s.should include("Description")
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "with project \"Project\" and description \"Description\"" do
|
45
|
+
it "looks like \"[<time> - --:--] Project: Description\"" do
|
46
|
+
@entry.to_s.should == "[#{@entry.start_string} - --:--] Project:\tDescription"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Store do
|
4
|
+
TMP_FILENAME = File.expand_path(File.join(File.dirname(__FILE__), '..', 'fixtures', 'trackdb.tmp'))
|
5
|
+
|
6
|
+
before do
|
7
|
+
Store.stub!(:db_filename).and_return(TMP_FILENAME)
|
8
|
+
@track = Track.new
|
9
|
+
|
10
|
+
@time = Time.now
|
11
|
+
end
|
12
|
+
|
13
|
+
after do
|
14
|
+
File.unlink(TMP_FILENAME) if File.exists?(TMP_FILENAME)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "storing and loading an object" do
|
18
|
+
it "should store and load the object" do
|
19
|
+
Store.store(@track)
|
20
|
+
Store.load.should == @track
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "opening a track from storage and adding an entry" do
|
25
|
+
it "should store the modified track" do
|
26
|
+
pending
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/spec/track_spec.rb
CHANGED
@@ -1,28 +1,24 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/spec_helper'
|
2
2
|
|
3
3
|
describe Track do
|
4
|
-
TMP_FILENAME = File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'time_log.tmp'))
|
5
|
-
|
6
4
|
before do
|
7
5
|
@track = Track.new
|
8
|
-
@track.stub!(:log_filename).and_return(TMP_FILENAME)
|
9
6
|
|
10
7
|
@time = Time.now
|
11
8
|
@time.stub!(:now).and_return(@time)
|
12
9
|
@time_string = @time.strftime('%H:%M')
|
13
10
|
end
|
14
11
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
File.readlines(TMP_FILENAME).last.chomp
|
12
|
+
describe "#==" do
|
13
|
+
it "should be equal if the options are equal and the entries are equal" do
|
14
|
+
@track = Track.new
|
15
|
+
@track.options[:foo] = :foo
|
16
|
+
@track.entries << :foo
|
17
|
+
@track2 = Track.new
|
18
|
+
@track2.options[:foo] = :foo
|
19
|
+
@track2.entries << :foo
|
20
|
+
@track.should == @track2
|
21
|
+
end
|
26
22
|
end
|
27
23
|
|
28
24
|
describe "#run" do
|
@@ -46,87 +42,123 @@ describe Track do
|
|
46
42
|
end
|
47
43
|
end
|
48
44
|
|
49
|
-
describe "#
|
50
|
-
it "
|
51
|
-
@track.
|
52
|
-
@track.
|
45
|
+
describe "#last_entry" do
|
46
|
+
it "should return the last entry" do
|
47
|
+
entry = @track.add_entry('project')
|
48
|
+
@track.last_entry.should == entry
|
53
49
|
end
|
50
|
+
end
|
54
51
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
end.should change{line_count}
|
52
|
+
describe "#add_entry" do
|
53
|
+
it "should add a new entry" do
|
54
|
+
lambda{@track.add_entry('project', 'description')}.should change(@track.entries, :size)
|
59
55
|
end
|
60
56
|
|
61
|
-
|
57
|
+
it "should return the new entry" do
|
58
|
+
@track.add_entry('project', 'description').should == @track.last_entry
|
59
|
+
end
|
62
60
|
|
63
|
-
|
64
|
-
|
65
|
-
|
61
|
+
it "should set the entry's start time to now" do
|
62
|
+
time = Time.now
|
63
|
+
Time.stub!(:now).and_return(time)
|
64
|
+
@track.add_entry('project', 'description').start_time.should == time
|
66
65
|
end
|
67
66
|
|
68
|
-
it "
|
69
|
-
|
67
|
+
it "does not require a description" do
|
68
|
+
lambda{@track.add_entry('project')}.should_not raise_error
|
70
69
|
end
|
70
|
+
end
|
71
71
|
|
72
|
-
|
73
|
-
|
72
|
+
describe "#start" do
|
73
|
+
it "should stop any started entry" do
|
74
|
+
@track.should_receive(:stop)
|
75
|
+
@track.start('project')
|
74
76
|
end
|
75
77
|
|
76
|
-
it "
|
77
|
-
|
78
|
+
it "should create an new entry" do
|
79
|
+
lambda{@track.start('project')}.should change(@track.entries, :size)
|
78
80
|
end
|
79
81
|
|
80
|
-
it "
|
81
|
-
|
82
|
+
it "should map a project shortname" do
|
83
|
+
expected = "Test Project"
|
84
|
+
@track.projects['test'] = expected
|
85
|
+
@track.start('test')
|
86
|
+
@track.last_entry.project.should == expected
|
82
87
|
end
|
83
88
|
|
84
|
-
it "
|
85
|
-
|
89
|
+
it "should use the given project if it is not a shortname" do
|
90
|
+
expected = 'test'
|
91
|
+
@track.start(expected)
|
92
|
+
@track.last_entry.project.should == expected
|
86
93
|
end
|
87
94
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
95
|
+
it "should concatenate multiple description arguments" do
|
96
|
+
actual = %w(tacos are teh awesum)
|
97
|
+
expected = 'tacos are teh awesum'
|
98
|
+
@track.start('test', actual)
|
99
|
+
@track.last_entry.description.should == expected
|
92
100
|
end
|
93
101
|
end
|
94
102
|
|
95
103
|
describe "#stop" do
|
96
|
-
|
97
|
-
@track.
|
98
|
-
@track.send(:stop)
|
99
|
-
last_line.should_not include('--:--')
|
100
|
-
last_line.should include("[#@time_string - #@time_string]")
|
104
|
+
before do
|
105
|
+
@track = Track.new
|
101
106
|
end
|
102
107
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
108
|
+
describe "without any entries" do
|
109
|
+
it "should not change the entries" do
|
110
|
+
expected = @track.entries.dup
|
111
|
+
@track.stop
|
112
|
+
actual = @track.entries
|
113
|
+
|
114
|
+
actual.should == expected
|
115
|
+
end
|
107
116
|
end
|
108
|
-
end
|
109
|
-
end
|
110
117
|
|
111
|
-
describe
|
112
|
-
|
113
|
-
|
114
|
-
|
118
|
+
describe "with a stopped entry" do
|
119
|
+
before do
|
120
|
+
@stopped_entry = Entry.new(Time.now, Time.now, '', '')
|
121
|
+
@track.entries << @stopped_entry
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should not change the entries" do
|
125
|
+
expected = @track.entries.dup
|
126
|
+
@track.stop
|
127
|
+
actual = @track.entries
|
115
128
|
|
116
|
-
|
117
|
-
|
118
|
-
@track.options['filename'] = 'file'
|
119
|
-
@track.send(:log_filename).should match(/^file/)
|
129
|
+
actual.should == expected
|
130
|
+
end
|
120
131
|
end
|
121
132
|
|
122
|
-
|
123
|
-
|
124
|
-
|
133
|
+
describe "with a started entry" do
|
134
|
+
before do
|
135
|
+
@started_entry = Entry.new(Time.now, nil, '', '')
|
136
|
+
@track.entries << @started_entry
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should stop the entry" do
|
140
|
+
@track.stop
|
141
|
+
@track.last_entry.should be_stopped
|
142
|
+
end
|
125
143
|
end
|
144
|
+
end
|
126
145
|
|
127
|
-
|
128
|
-
|
146
|
+
describe "#cat" do
|
147
|
+
it "should output each entry as a string on a line to stdout" do
|
148
|
+
old, $stdout = $stdout, StringIO.new
|
149
|
+
@track.add_entry('project')
|
150
|
+
@track.cat
|
151
|
+
$stdout.rewind
|
152
|
+
$stdout.read.should == @track.last_entry.to_s + "\n"
|
153
|
+
$stdout = old
|
129
154
|
end
|
155
|
+
end
|
156
|
+
end
|
130
157
|
|
158
|
+
describe Track do
|
159
|
+
before do
|
160
|
+
@track = Track.new
|
131
161
|
end
|
162
|
+
|
163
|
+
describe "#log_filename"
|
132
164
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ReinH-track
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rein Henrichs
|
@@ -9,7 +9,7 @@ autorequire: track
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-11-
|
12
|
+
date: 2008-11-26 00:00:00 -08:00
|
13
13
|
default_executable: track
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -29,9 +29,15 @@ files:
|
|
29
29
|
- Rakefile
|
30
30
|
- TODO
|
31
31
|
- bin/track
|
32
|
+
- lib/track
|
33
|
+
- lib/track/entry.rb
|
34
|
+
- lib/track/store.rb
|
32
35
|
- lib/track.rb
|
33
36
|
- spec/fixtures
|
34
37
|
- spec/spec_helper.rb
|
38
|
+
- spec/track
|
39
|
+
- spec/track/entry_spec.rb
|
40
|
+
- spec/track/store_spec.rb
|
35
41
|
- spec/track_spec.rb
|
36
42
|
has_rdoc: true
|
37
43
|
homepage: http://github.com/ReinH/track
|