timetrap 1.7.0 → 1.7.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.rspec +2 -0
- data/CONTRIBUTORS +1 -0
- data/Gemfile +13 -0
- data/README.md +8 -1
- data/Rakefile +6 -4
- data/VERSION.yml +1 -1
- data/lib/timetrap.rb +2 -1
- data/lib/timetrap/cli.rb +20 -0
- data/lib/timetrap/helpers.rb +2 -2
- data/lib/timetrap/models.rb +2 -31
- data/lib/timetrap/timer.rb +34 -0
- data/spec/timetrap_spec.rb +76 -18
- data/timetrap.gemspec +4 -3
- metadata +6 -5
- data/spec/spec.opts +0 -2
data/.rspec
ADDED
data/CONTRIBUTORS
CHANGED
data/Gemfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
source 'http://rubygems.org'
|
2
|
+
|
3
|
+
gem 'sequel', ">= 3.9.0"
|
4
|
+
gem 'sqlite3-ruby', ">= 1.2.5"
|
5
|
+
gem 'chronic', "~> 0.3.0"
|
6
|
+
gem 'getopt-declare', ">= 1.28"
|
7
|
+
gem 'json', "~>1.4.6"
|
8
|
+
gem 'icalendar', "~>1.1.5"
|
9
|
+
|
10
|
+
group :test do
|
11
|
+
gem "rspec", "~>2"
|
12
|
+
gem 'fakefs'
|
13
|
+
end
|
data/README.md
CHANGED
@@ -65,7 +65,7 @@ You check out with the `out` command.
|
|
65
65
|
Checked out of sheet "coding"
|
66
66
|
|
67
67
|
You can edit entries that aren't running using `edit`'s `--id` or `-i` flag.
|
68
|
-
`t display --
|
68
|
+
`t display --ids` (or `t display -v`) will tell you the ids.
|
69
69
|
|
70
70
|
$ # note id column in output
|
71
71
|
$ t d -v
|
@@ -292,6 +292,13 @@ Commands
|
|
292
292
|
|
293
293
|
usage: ``t out [--at TIME] [TIMESHEET]``
|
294
294
|
|
295
|
+
**resume**
|
296
|
+
Start the timer for the current timesheet with the same notes as the last entry.
|
297
|
+
If there is no last entry the new one has blank notes ore uses the optional
|
298
|
+
NOTES parameter.
|
299
|
+
|
300
|
+
usage: ``t resume [--at TIME] [NOTES]``
|
301
|
+
|
295
302
|
**sheet**
|
296
303
|
Switch to a timesheet creating it if necessary. The default timesheet is
|
297
304
|
called "default". When no sheet is specified list all existing sheets.
|
data/Rakefile
CHANGED
@@ -1,12 +1,14 @@
|
|
1
|
-
require 'spec/rake/spectask'
|
2
1
|
require 'rake/rdoctask'
|
3
2
|
require 'rake/gempackagetask'
|
3
|
+
require 'rspec/core/rake_task'
|
4
4
|
|
5
|
+
desc 'Default: run specs.'
|
5
6
|
task :default => :spec
|
6
7
|
|
7
|
-
desc "Run
|
8
|
-
|
9
|
-
t.
|
8
|
+
desc "Run specs"
|
9
|
+
RSpec::Core::RakeTask.new do |t|
|
10
|
+
t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
|
11
|
+
# Put spec opts in a file named .rspec in root
|
10
12
|
end
|
11
13
|
|
12
14
|
Rake::RDocTask.new do |rd|
|
data/VERSION.yml
CHANGED
data/lib/timetrap.rb
CHANGED
data/lib/timetrap/cli.rb
CHANGED
@@ -73,6 +73,11 @@ COMMAND is one of:
|
|
73
73
|
usage: t out [--at TIME] [TIMESHEET]
|
74
74
|
-a, --at <time:qs> Use this time instead of now
|
75
75
|
|
76
|
+
* resume - Start the timer for the current time sheet with the same note as
|
77
|
+
the last entry on the sheet. If there is no entry it takes the passed note.
|
78
|
+
usage: t resume [--at TIME] [NOTES]
|
79
|
+
-a, --at <time:qs> Use this time instead of now
|
80
|
+
|
76
81
|
* sheet - Switch to a timesheet creating it if necessary. When no sheet is
|
77
82
|
specified list all sheets.
|
78
83
|
usage: t sheet [TIMESHEET]
|
@@ -210,6 +215,17 @@ COMMAND is one of:
|
|
210
215
|
warn "Checked into sheet #{Timer.current_sheet.inspect}."
|
211
216
|
end
|
212
217
|
|
218
|
+
def resume
|
219
|
+
last_entry = Timer.entries(Timer.current_sheet).last
|
220
|
+
warn "No entry yet on this sheet yet. Started a new entry." unless last_entry
|
221
|
+
note = (last_entry ? last_entry.note : nil)
|
222
|
+
warn "Resuming #{note.inspect} from entry ##{last_entry.id}" if note
|
223
|
+
|
224
|
+
self.unused_args = note || unused_args
|
225
|
+
|
226
|
+
self.in
|
227
|
+
end
|
228
|
+
|
213
229
|
def out
|
214
230
|
sheet = sheet_name_from_string(unused_args)
|
215
231
|
if Timer.stop sheet, args['-a']
|
@@ -315,6 +331,10 @@ COMMAND is one of:
|
|
315
331
|
args.unused.join(' ')
|
316
332
|
end
|
317
333
|
|
334
|
+
def unused_args=(str)
|
335
|
+
args.unused = str.split
|
336
|
+
end
|
337
|
+
|
318
338
|
def ask_user question
|
319
339
|
return true if args['-y']
|
320
340
|
$stderr.print question
|
data/lib/timetrap/helpers.rb
CHANGED
@@ -36,8 +36,8 @@ module Timetrap
|
|
36
36
|
else
|
37
37
|
Timetrap::Entry.filter('sheet = ?', Timer.current_sheet)
|
38
38
|
end
|
39
|
-
ee = ee.filter('start >= ?', Date.parse(
|
40
|
-
ee = ee.filter('start <= ?', Date.parse(
|
39
|
+
ee = ee.filter('start >= ?', Date.parse(Timer.process_time(args['-s']).to_s)) if args['-s']
|
40
|
+
ee = ee.filter('start <= ?', Date.parse(Timer.process_time(args['-e']).to_s) + 1) if args['-e']
|
41
41
|
ee
|
42
42
|
end
|
43
43
|
|
data/lib/timetrap/models.rb
CHANGED
@@ -14,11 +14,11 @@ module Timetrap
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def start= time
|
17
|
-
self[:start]= process_time(time)
|
17
|
+
self[:start]= Timer.process_time(time)
|
18
18
|
end
|
19
19
|
|
20
20
|
def end= time
|
21
|
-
self[:end]= process_time(time)
|
21
|
+
self[:end]= Timer.process_time(time)
|
22
22
|
end
|
23
23
|
|
24
24
|
def start
|
@@ -57,35 +57,6 @@ module Timetrap
|
|
57
57
|
end
|
58
58
|
|
59
59
|
private
|
60
|
-
def process_time(time)
|
61
|
-
case time
|
62
|
-
when Time
|
63
|
-
time
|
64
|
-
when String
|
65
|
-
if parsed = Chronic.parse(time)
|
66
|
-
parsed
|
67
|
-
elsif safe_for_time_parse?(time) and parsed = Time.parse(time)
|
68
|
-
parsed
|
69
|
-
else
|
70
|
-
raise ArgumentError, "Could not parse #{time.inspect}, entry not updated"
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
# Time.parse is optimistic and will parse things like '=18' into midnight
|
76
|
-
# on 18th of this month.
|
77
|
-
# It will also turn 'total garbage' into Time.now
|
78
|
-
# Here we do some sanity checks on the string to protect it from common
|
79
|
-
# cli formatting issues, and allow reasonable warning to be passed back to
|
80
|
-
# the user.
|
81
|
-
def safe_for_time_parse?(string)
|
82
|
-
# misformatted cli option
|
83
|
-
!string.include?('=') and
|
84
|
-
# a date time string needs a number in it
|
85
|
-
string =~ /\d/
|
86
|
-
end
|
87
|
-
|
88
|
-
|
89
60
|
# do a quick pseudo migration. This should only get executed on the first run
|
90
61
|
set_schema do
|
91
62
|
primary_key :id
|
data/lib/timetrap/timer.rb
CHANGED
@@ -8,6 +8,40 @@ module Timetrap
|
|
8
8
|
|
9
9
|
extend self
|
10
10
|
|
11
|
+
def process_time(time)
|
12
|
+
case time
|
13
|
+
when Time
|
14
|
+
time
|
15
|
+
when String
|
16
|
+
chronic = begin
|
17
|
+
Chronic.parse(time)
|
18
|
+
rescue => e
|
19
|
+
warn "#{e.class} in Chronic gem parsing time. Falling back to Time.parse"
|
20
|
+
end
|
21
|
+
|
22
|
+
if parsed = chronic
|
23
|
+
parsed
|
24
|
+
elsif safe_for_time_parse?(time) and parsed = Time.parse(time)
|
25
|
+
parsed
|
26
|
+
else
|
27
|
+
raise ArgumentError, "Could not parse #{time.inspect}, entry not updated"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Time.parse is optimistic and will parse things like '=18' into midnight
|
33
|
+
# on 18th of this month.
|
34
|
+
# It will also turn 'total garbage' into Time.now
|
35
|
+
# Here we do some sanity checks on the string to protect it from common
|
36
|
+
# cli formatting issues, and allow reasonable warning to be passed back to
|
37
|
+
# the user.
|
38
|
+
def safe_for_time_parse?(string)
|
39
|
+
# misformatted cli option
|
40
|
+
!string.include?('=') and
|
41
|
+
# a date time string needs a number in it
|
42
|
+
string =~ /\d/
|
43
|
+
end
|
44
|
+
|
11
45
|
def current_sheet= sheet
|
12
46
|
m = Meta.find_or_create(:key => 'current_sheet')
|
13
47
|
m.value = sheet
|
data/spec/timetrap_spec.rb
CHANGED
@@ -1,8 +1,16 @@
|
|
1
1
|
TEST_MODE = true
|
2
2
|
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'timetrap'))
|
3
|
-
require '
|
3
|
+
require 'rspec'
|
4
4
|
require 'fakefs/safe'
|
5
5
|
|
6
|
+
def local_time(str)
|
7
|
+
Timetrap::Timer.process_time(str)
|
8
|
+
end
|
9
|
+
|
10
|
+
def local_time_cli(str)
|
11
|
+
local_time(str).strftime('%Y-%m-%d %H:%M:%S')
|
12
|
+
end
|
13
|
+
|
6
14
|
module Timetrap::StubConfig
|
7
15
|
def with_stubbed_config options
|
8
16
|
options.each do |k, v|
|
@@ -197,7 +205,7 @@ The "format" command is deprecated in favor of "display". Sorry for the inconven
|
|
197
205
|
:note => 'entry 4', :start => '2008-10-05 18:00:00'
|
198
206
|
)
|
199
207
|
|
200
|
-
Time.stub!(:now).and_return
|
208
|
+
Time.stub!(:now).and_return local_time('2008-10-05 20:00:00')
|
201
209
|
@desired_output = <<-OUTPUT
|
202
210
|
Timesheet: SpecSheet
|
203
211
|
Day Start End Duration Notes
|
@@ -310,7 +318,6 @@ Grand Total 10:00:00
|
|
310
318
|
RUBY
|
311
319
|
end
|
312
320
|
invoke 'd -fbaz'
|
313
|
-
$stderr.string.should == ''
|
314
321
|
$stdout.string.should == "yeah I did it\n"
|
315
322
|
FileUtils.rm_r dir
|
316
323
|
end
|
@@ -381,28 +388,29 @@ start,end,note,sheet
|
|
381
388
|
|
382
389
|
describe 'json' do
|
383
390
|
before do
|
384
|
-
create_entry(:start => '2008-10-03 12:00:00', :end => '2008-10-03 14:00:00')
|
385
|
-
create_entry(:start => '2008-10-05 12:00:00', :end => '2008-10-05 14:00:00')
|
391
|
+
create_entry(:start => local_time_cli('2008-10-03 12:00:00'), :end => local_time_cli('2008-10-03 14:00:00'))
|
392
|
+
create_entry(:start => local_time_cli('2008-10-05 12:00:00'), :end => local_time_cli('2008-10-05 14:00:00'))
|
386
393
|
end
|
387
394
|
|
388
395
|
it "should export to json not including running items" do
|
389
396
|
invoke 'in'
|
390
397
|
invoke 'display -f json'
|
391
398
|
JSON.parse($stdout.string).should == JSON.parse(<<-EOF)
|
392
|
-
[{\"sheet\":\"default\",\"end\":\"
|
399
|
+
[{\"sheet\":\"default\",\"end\":\"#{local_time('2008-10-03 14:00:00')}\",\"start\":\"#{local_time('2008-10-03 12:00:00')}\",\"note\":\"note\",\"id\":1},{\"sheet\":\"default\",\"end\":\"#{local_time('2008-10-05 14:00:00')}\",\"start\":\"#{local_time('2008-10-05 12:00:00')}\",\"note\":\"note\",\"id\":2}]
|
393
400
|
EOF
|
394
401
|
end
|
395
402
|
end
|
396
403
|
|
397
404
|
describe 'ical' do
|
398
405
|
before do
|
399
|
-
create_entry(:start => '2008-10-03 12:00:00', :end => '2008-10-03 14:00:00')
|
400
|
-
create_entry(:start => '2008-10-05 12:00:00', :end => '2008-10-05 14:00:00')
|
406
|
+
create_entry(:start => local_time_cli('2008-10-03 12:00:00'), :end => local_time_cli('2008-10-03 14:00:00'))
|
407
|
+
create_entry(:start => local_time_cli('2008-10-05 12:00:00'), :end => local_time_cli('2008-10-05 14:00:00'))
|
401
408
|
end
|
402
409
|
|
403
410
|
it "should not export running items" do
|
404
411
|
invoke 'in'
|
405
412
|
invoke 'display --format ical'
|
413
|
+
|
406
414
|
$stdout.string.scan(/BEGIN:VEVENT/).should have(2).item
|
407
415
|
end
|
408
416
|
|
@@ -533,17 +541,17 @@ END:VCALENDAR
|
|
533
541
|
|
534
542
|
describe "with sheets defined" do
|
535
543
|
before do
|
536
|
-
Time.stub!(:now).and_return
|
537
|
-
create_entry( :sheet => 'A Longly Named Sheet 2', :start => '2008-10-03 12:00:00',
|
538
|
-
:end => '2008-10-03 14:00:00')
|
539
|
-
create_entry( :sheet => 'A Longly Named Sheet 2', :start => '2008-10-03 12:00:00',
|
540
|
-
:end => '2008-10-03 14:00:00')
|
541
|
-
create_entry( :sheet => 'A Longly Named Sheet 2', :start => '2008-10-05 12:00:00',
|
542
|
-
:end => '2008-10-05 14:00:00')
|
543
|
-
create_entry( :sheet => 'A Longly Named Sheet 2', :start => '2008-10-05 14:00:00',
|
544
|
+
Time.stub!(:now).and_return local_time("2008-10-05 18:00:00")
|
545
|
+
create_entry( :sheet => 'A Longly Named Sheet 2', :start => local_time_cli('2008-10-03 12:00:00'),
|
546
|
+
:end => local_time_cli('2008-10-03 14:00:00'))
|
547
|
+
create_entry( :sheet => 'A Longly Named Sheet 2', :start => local_time_cli('2008-10-03 12:00:00'),
|
548
|
+
:end => local_time_cli('2008-10-03 14:00:00'))
|
549
|
+
create_entry( :sheet => 'A Longly Named Sheet 2', :start => local_time_cli('2008-10-05 12:00:00'),
|
550
|
+
:end => local_time_cli('2008-10-05 14:00:00'))
|
551
|
+
create_entry( :sheet => 'A Longly Named Sheet 2', :start => local_time_cli('2008-10-05 14:00:00'),
|
544
552
|
:end => nil)
|
545
|
-
create_entry( :sheet => 'Sheet 1', :start => '2008-10-03 16:00:00',
|
546
|
-
:end => '2008-10-03 18:00:00')
|
553
|
+
create_entry( :sheet => 'Sheet 1', :start => local_time_cli('2008-10-03 16:00:00'),
|
554
|
+
:end => local_time_cli('2008-10-03 18:00:00'))
|
547
555
|
Timetrap::Timer.current_sheet = 'A Longly Named Sheet 2'
|
548
556
|
end
|
549
557
|
it "should list available timesheets" do
|
@@ -656,6 +664,56 @@ END:VCALENDAR
|
|
656
664
|
end
|
657
665
|
end
|
658
666
|
|
667
|
+
describe "resume" do
|
668
|
+
before :each do
|
669
|
+
@time = Time.now
|
670
|
+
Time.stub!(:now).and_return @time
|
671
|
+
|
672
|
+
invoke 'in Some strange task'
|
673
|
+
@last_active = Timetrap::Timer.active_entry
|
674
|
+
invoke 'out'
|
675
|
+
|
676
|
+
Timetrap::Timer.active_entry.should be_nil
|
677
|
+
@last_active.should_not be_nil
|
678
|
+
end
|
679
|
+
|
680
|
+
it "should allow to resume the last active sheet" do
|
681
|
+
invoke 'resume'
|
682
|
+
|
683
|
+
Timetrap::Timer.active_entry.note.should ==(@last_active.note)
|
684
|
+
Timetrap::Timer.active_entry.start.to_s.should == @time.to_s
|
685
|
+
end
|
686
|
+
|
687
|
+
it "should allow to resume the activity with a given time" do
|
688
|
+
invoke 'resume --at "10am 2008-10-03"'
|
689
|
+
|
690
|
+
Timetrap::Timer.active_entry.start.should eql(Time.parse('2008-10-03 10:00'))
|
691
|
+
end
|
692
|
+
|
693
|
+
describe "no existing entries" do
|
694
|
+
before(:each) do
|
695
|
+
Timetrap::Timer.entries(Timetrap::Timer.current_sheet).each do |e|
|
696
|
+
e.destroy
|
697
|
+
end
|
698
|
+
|
699
|
+
Timetrap::Timer.entries(Timetrap::Timer.current_sheet).should be_empty
|
700
|
+
Timetrap::Timer.active_entry.should be_nil
|
701
|
+
end
|
702
|
+
|
703
|
+
it "starts a new entry if no entry exists" do
|
704
|
+
invoke "resume"
|
705
|
+
Timetrap::Timer.active_entry.should_not be_nil
|
706
|
+
Timetrap::Timer.active_entry.note.should ==("")
|
707
|
+
end
|
708
|
+
|
709
|
+
it "allows to pass a note that is used for the new entry" do
|
710
|
+
invoke "resume New Note"
|
711
|
+
Timetrap::Timer.active_entry.should_not be_nil
|
712
|
+
Timetrap::Timer.active_entry.note.should ==("New Note")
|
713
|
+
end
|
714
|
+
end
|
715
|
+
end
|
716
|
+
|
659
717
|
describe "sheet" do
|
660
718
|
it "should switch to a new timesheet" do
|
661
719
|
invoke 'sheet sheet 1'
|
data/timetrap.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{timetrap}
|
8
|
-
s.version = "1.7.
|
8
|
+
s.version = "1.7.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Sam Goldstein"]
|
12
|
-
s.date = %q{2010-12-
|
12
|
+
s.date = %q{2010-12-18}
|
13
13
|
s.default_executable = %q{t}
|
14
14
|
s.description = %q{Command line time tracker}
|
15
15
|
s.email = %q{sgrock@gmail.com}
|
@@ -20,7 +20,9 @@ Gem::Specification.new do |s|
|
|
20
20
|
]
|
21
21
|
s.files = [
|
22
22
|
".gitignore",
|
23
|
+
".rspec",
|
23
24
|
"CONTRIBUTORS",
|
25
|
+
"Gemfile",
|
24
26
|
"LICENSE.txt",
|
25
27
|
"README.md",
|
26
28
|
"Rakefile",
|
@@ -39,7 +41,6 @@ Gem::Specification.new do |s|
|
|
39
41
|
"lib/timetrap/helpers.rb",
|
40
42
|
"lib/timetrap/models.rb",
|
41
43
|
"lib/timetrap/timer.rb",
|
42
|
-
"spec/spec.opts",
|
43
44
|
"spec/timetrap_spec.rb",
|
44
45
|
"timetrap.gemspec"
|
45
46
|
]
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: timetrap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 9
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 7
|
9
|
-
-
|
10
|
-
version: 1.7.
|
9
|
+
- 1
|
10
|
+
version: 1.7.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Sam Goldstein
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-12-
|
18
|
+
date: 2010-12-18 00:00:00 -08:00
|
19
19
|
default_executable: t
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -92,7 +92,9 @@ extra_rdoc_files:
|
|
92
92
|
- README.md
|
93
93
|
files:
|
94
94
|
- .gitignore
|
95
|
+
- .rspec
|
95
96
|
- CONTRIBUTORS
|
97
|
+
- Gemfile
|
96
98
|
- LICENSE.txt
|
97
99
|
- README.md
|
98
100
|
- Rakefile
|
@@ -111,7 +113,6 @@ files:
|
|
111
113
|
- lib/timetrap/helpers.rb
|
112
114
|
- lib/timetrap/models.rb
|
113
115
|
- lib/timetrap/timer.rb
|
114
|
-
- spec/spec.opts
|
115
116
|
- spec/timetrap_spec.rb
|
116
117
|
- timetrap.gemspec
|
117
118
|
has_rdoc: true
|
data/spec/spec.opts
DELETED