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 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