timetrap 1.4.1 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1 +1,2 @@
1
1
  tags
2
+ pkg
data/README.md CHANGED
@@ -135,7 +135,7 @@ Commands
135
135
  start or end times. Defaults to the current time although an ``--id`` flag can
136
136
  be passed with the entry's id (see display.)
137
137
 
138
- usage: ``t edit [--id ID] [--start TIME] [--end TIME] [NOTES]``
138
+ usage: ``t edit [--id ID] [--start TIME] [--end TIME] [--append] [NOTES]``
139
139
 
140
140
  **format**
141
141
  Deprecated
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 1
3
- :minor: 4
4
- :patch: 1
3
+ :minor: 5
4
+ :patch: 0
data/lib/timetrap.rb CHANGED
@@ -57,7 +57,6 @@ module Timetrap
57
57
  Entry.create(:sheet => Timetrap.current_sheet, :note => note, :start => time).save
58
58
  rescue => e
59
59
  CLI.say e.message
60
- CLI.say e.backtrace.join("\n")
61
60
  end
62
61
 
63
62
  def switch sheet
data/lib/timetrap/cli.rb CHANGED
@@ -28,10 +28,13 @@ where COMMAND is one of:
28
28
  -f, --format <format> The output format. Currently supports ical, csv, and
29
29
  text (default).
30
30
  * edit - alter an entry's note, start, or end time. Defaults to the active entry
31
- usage: t edit [--id ID] [--start TIME] [--end TIME] [NOTES]
31
+ usage: t edit [--id ID] [--start TIME] [--end TIME] [--append] [NOTES]
32
32
  -i, --id <id:i> Alter entry with id <id> instead of the running entry
33
33
  -s, --start <time:qs> Change the start time to <time>
34
34
  -e, --end <time:qs> Change the end time to <time>
35
+ -z, --append Append to the current note instead of replacing it
36
+ the delimiter between appended notes is
37
+ configurable (see configure)
35
38
  * format - deprecated: alias for display
36
39
  * in - start the timer for the current timesheet
37
40
  usage: t in [--at TIME] [NOTES]
@@ -67,6 +70,9 @@ where COMMAND is one of:
67
70
 
68
71
  def invoke
69
72
  args['-h'] ? say(USAGE) : invoke_command_if_valid
73
+ rescue => e
74
+ say e.message
75
+ exit 1
70
76
  end
71
77
 
72
78
  def commands
@@ -116,7 +122,13 @@ where COMMAND is one of:
116
122
  say "can't find entry" && return unless entry
117
123
  entry.update :start => args['-s'] if args['-s'] =~ /.+/
118
124
  entry.update :end => args['-e'] if args['-e'] =~ /.+/
119
- entry.update :note => unused_args if unused_args =~ /.+/
125
+ if unused_args =~ /.+/
126
+ note = unused_args
127
+ if args['-z']
128
+ note = [entry.note, note].join(Config['append_notes_delimiter'])
129
+ end
130
+ entry.update :note => note
131
+ end
120
132
  end
121
133
 
122
134
  def backend
@@ -14,7 +14,9 @@ module Timetrap
14
14
  # Path to the sqlite db
15
15
  'database_file' => "#{ENV['HOME']}/.timetrap.db",
16
16
  # Unit of time for rounding (-r) in seconds
17
- 'round_in_seconds' => 900
17
+ 'round_in_seconds' => 900,
18
+ # delimiter used when appending notes with `t edit --append`
19
+ 'append_notes_delimiter' => ' '
18
20
  }
19
21
  end
20
22
 
@@ -28,10 +30,13 @@ module Timetrap
28
30
  end
29
31
 
30
32
  def configure!
31
- unless File.exist?(PATH)
32
- File.open(PATH, 'w') do |fh|
33
- fh.puts(defaults.to_yaml)
34
- end
33
+ configs = if File.exist?(PATH)
34
+ defaults.merge(YAML.load_file(PATH))
35
+ else
36
+ defaults
37
+ end
38
+ File.open(PATH, 'w') do |fh|
39
+ fh.puts(configs.to_yaml)
35
40
  end
36
41
  end
37
42
  end
@@ -14,11 +14,11 @@ module Timetrap
14
14
  end
15
15
 
16
16
  def start= time
17
- self[:start]= Chronic.parse(time.to_s) || time
17
+ self[:start]= process_time(time)
18
18
  end
19
19
 
20
20
  def end= time
21
- self[:end]= Chronic.parse(time.to_s) || time
21
+ self[:end]= process_time(time)
22
22
  end
23
23
 
24
24
  def start
@@ -56,6 +56,36 @@ module Timetrap
56
56
  map{|e|e.sheet}.uniq.sort
57
57
  end
58
58
 
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
+
59
89
  # do a quick pseudo migration. This should only get executed on the first run
60
90
  set_schema do
61
91
  primary_key :id
@@ -69,7 +69,7 @@ describe Timetrap do
69
69
  end
70
70
  end
71
71
 
72
- it "should descirve config file" do
72
+ it "should describe config file" do
73
73
  FakeFS do
74
74
  invoke "configure"
75
75
  $stdout.string.should == "Config file is at \"#{ENV['HOME']}/.timetrap.yml\"\n"
@@ -88,6 +88,15 @@ describe Timetrap do
88
88
  Timetrap.active_entry.note.should == 'new description'
89
89
  end
90
90
 
91
+ it "should allow appending to the description of the active period" do
92
+ Timetrap::Config.stub(:[]).with('append_notes_delimiter').and_return('//')
93
+ Timetrap.active_entry.note.should == 'running entry'
94
+ invoke 'edit --append new'
95
+ Timetrap.active_entry.note.should == 'running entry//new'
96
+ invoke 'edit -z more'
97
+ Timetrap.active_entry.note.should == 'running entry//new//more'
98
+ end
99
+
91
100
  it "should edit the start time of the active period" do
92
101
  invoke 'edit --start "yesterday 10am"'
93
102
  Timetrap.active_entry.start.should == Chronic.parse("yesterday 10am")
@@ -329,6 +338,22 @@ END:VCALENDAR
329
338
  invoke 'in work --at "10am 2008-10-03"'
330
339
  Timetrap::Entry.order_by(:id).last.start.should == Time.parse('2008-10-03 10:00')
331
340
  end
341
+
342
+ it "should fail with a warning for misformatted cli options it can't parse" do
343
+ now = Time.now
344
+ Time.stub!(:now).and_return now
345
+ invoke 'in work --at="18 minutes ago"'
346
+ Timetrap::Entry.order_by(:id).last.should be_nil
347
+ $stdout.string.should =~ /\w+/
348
+ end
349
+
350
+ it "should fail with a time argurment of total garbage" do
351
+ now = Time.now
352
+ Time.stub!(:now).and_return now
353
+ invoke 'in work --at "total garbage"'
354
+ Timetrap::Entry.order_by(:id).last.should be_nil
355
+ $stdout.string.should =~ /\w+/
356
+ end
332
357
  end
333
358
 
334
359
  describe "kill" do
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.4.1"
8
+ s.version = "1.5.0"
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-10-28}
12
+ s.date = %q{2010-10-30}
13
13
  s.default_executable = %q{t}
14
14
  s.description = %q{Command line time tracker}
15
15
  s.email = %q{sgrock@gmail.com}
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: 5
4
+ hash: 3
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
- - 4
9
- - 1
10
- version: 1.4.1
8
+ - 5
9
+ - 0
10
+ version: 1.5.0
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-10-28 00:00:00 -07:00
18
+ date: 2010-10-30 00:00:00 -07:00
19
19
  default_executable: t
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency