ReinH-track 1.0.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ require 'date'
5
5
  require 'spec/rake/spectask'
6
6
 
7
7
  GEM = "track"
8
- GEM_VERSION = "1.0.2"
8
+ GEM_VERSION = "1.1.0"
9
9
  AUTHOR = "Rein Henrichs"
10
10
  EMAIL = "reinh@reinh.com"
11
11
  HOMEPAGE = "http://github.com/ReinH/track"
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
- home = ENV['HOME']
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 => stop a started task
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
- options ||= {}
26
- Track.new(options).run(ARGV)
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
- def initialize(options={})
6
- @options = options
7
- @projects = @options['projects'] || {}
8
- end
8
+ attr_reader :options, :projects, :entries
9
+ attr_writer :options
9
10
 
10
- def run(argv)
11
- argv.first == "stop" ? stop : start(*argv)
11
+ def initialize
12
+ @entries = []
12
13
  end
13
14
 
14
- private
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
- write_line(project, description)
18
+ def ==(other)
19
+ [self.options, self.entries] == [other.options, other.entries]
23
20
  end
24
21
 
25
- def stop
26
- return unless File.size?(log_filename)
27
- File.open(log_filename, 'r+') do |file|
28
- lines = file.readlines
29
- lines.last.sub!(placeholder, time_string)
30
- file.rewind
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 placeholder
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
@@ -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
@@ -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__), '..', 'bin')
3
- load 'track'
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
- after do
16
- File.unlink(TMP_FILENAME) if File.exists?(TMP_FILENAME)
17
- end
18
-
19
- def line_count
20
- `wc -l #{TMP_FILENAME} 2>/dev/null`.to_i
21
- end
22
- private :line_count
23
-
24
- def last_line
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 "#start" do
50
- it "stops any started task" do
51
- @track.should_receive(:stop)
52
- @track.send(:start)
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
- it "appends a line to the log file" do
56
- lambda do
57
- @track.send(:start, "Project", "Description")
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
- end
57
+ it "should return the new entry" do
58
+ @track.add_entry('project', 'description').should == @track.last_entry
59
+ end
62
60
 
63
- describe "#write_line" do
64
- before do
65
- @track.send(:write_line, "Project", "Description")
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 "includes the start time" do
69
- last_line.should include(@time_string)
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
- it "includes a placeholder for the end time" do
73
- last_line.should include('--:--')
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 "wraps the times in []" do
77
- last_line.should match(/\[.+\]/)
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 "includes the project name followed by a \":\"" do
81
- last_line.should include("Project:")
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 "includes the description" do
85
- last_line.should include("Project:")
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
- describe "with project \"Project\" and description \"Description\"" do
89
- it "looks like \"[<time> - --:--] Project: Description\"" do
90
- last_line.should == "[#{@time_string} - --:--] Project:\tDescription"
91
- end
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
- it "replaced the end time placeholder with the end time" do
97
- @track.send(:start)
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
- it "does not change the last line if there is nothing to stop" do
104
- @track.send(:start)
105
- @track.send(:stop)
106
- lambda {@track.send(:stop)}.should_not change{last_line}
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 Track do
112
- before do
113
- @track = Track.new
114
- end
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
- describe "#log_filename" do
117
- it "starts with the filename specified in the options" do
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
- it "includes the current date" do
123
- require 'date'
124
- @track.send(:log_filename).should include(Date.today.to_s)
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
- it "is a text file" do
128
- @track.send(:log_filename).should match(/\.txt$/)
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.2
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-17 00:00:00 -08:00
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