samg-timetrap 0.0.7 → 0.0.8

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/bin/dev_t ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ # Executable with absolute path to lib for hacking and development
3
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'timetrap')
4
+ Timetrap::CLI.invoke
@@ -0,0 +1,227 @@
1
+ module Timetrap
2
+ module CLI
3
+ extend Helpers
4
+ attr_accessor :args
5
+ extend self
6
+
7
+ USAGE = <<-EOF
8
+
9
+ Timetrap - Simple Time Tracking
10
+
11
+ Usage: #{File.basename $0} COMMAND [OPTIONS] [ARGS...]
12
+
13
+ where COMMAND is one of:
14
+ * alter - alter an entry's note, start, or end time. Defaults to the active entry
15
+ usage: t alter [--id ID] [--start TIME] [--end TIME] [NOTES]
16
+ -i, --id <id:i> Alter entry with id <id> instead of the running entry
17
+ -s, --start <time:qs> Change the start time to <time>
18
+ -e, --end <time:qs> Change the end time to <time>
19
+ * backend - open an sqlite shell to the database
20
+ usage: t backend
21
+ * display - display the current timesheet
22
+ usage: t display [--ids] [--start DATE] [--end DATE] [TIMESHEET]
23
+ -v, --ids Print database ids (for use with alter)
24
+ -s, --start <date:qs> Include entries that start on this date or later
25
+ -e, --end <date:qs> Include entries that start on this date or earlier
26
+ * format - export a sheet to csv format
27
+ usage: t format [--ids] [--start DATE] [--end DATE] FORMATTER
28
+ FORMATTER Currently only supports 'ical'
29
+ -s, --start <date:qs> Include entries that start on this date or later
30
+ -e, --end <date:qs> Include entries that start on this date or earlier
31
+ * in - start the timer for the current timesheet
32
+ usage: t in [--at TIME] [NOTES]
33
+ -a, --at <time:qs> Use this time instead of now
34
+ * kill - delete a timesheet
35
+ usage: t kill [--id ID] [TIMESHEET]
36
+ -i, --id <id:i> Alter entry with id <id> instead of the running entry
37
+ * list - show the available timesheets
38
+ usage: t list
39
+ * now - show the status of the current timesheet
40
+ usage: t now
41
+ * out - stop the timer for the current timesheet
42
+ usage: t out [--at TIME]
43
+ -a, --at <time:qs> Use this time instead of now
44
+ * running - show all running timesheets
45
+ NOT IMPLEMENTED
46
+ * switch - switch to a new timesheet
47
+ usage: t switch TIMESHEET
48
+
49
+ OTHER OPTIONS
50
+ -h, --help Display this help
51
+ EOF
52
+
53
+ def parse arguments
54
+ args.parse arguments
55
+ end
56
+
57
+ def invoke
58
+ args['-h'] ? say(USAGE) : invoke_command_if_valid
59
+ end
60
+
61
+ def commands
62
+ Timetrap::CLI::USAGE.scan(/\* \w+/).map{|s| s.gsub(/\* /, '')}
63
+ end
64
+
65
+ def say *something
66
+ puts *something
67
+ end
68
+
69
+ def invoke_command_if_valid
70
+ command = args.unused.shift
71
+ case (valid = commands.select{|name| name =~ %r|^#{command}|}).size
72
+ when 0 then say "Invalid command: #{command}"
73
+ when 1 then send valid[0]
74
+ else; say "Ambigous command: #{command}"; end
75
+ end
76
+
77
+ def alter
78
+ entry = args['-i'] ? Entry[args['-i']] : Timetrap.active_entry
79
+ say "can't find entry" && return unless entry
80
+ entry.update :start => args['-s'] if args['-s'] =~ /.+/
81
+ entry.update :end => args['-e'] if args['-e'] =~ /.+/
82
+ entry.update :note => unused_args if unused_args =~ /.+/
83
+ end
84
+
85
+ def backend
86
+ exec "sqlite3 #{DB_NAME}"
87
+ end
88
+
89
+ def in
90
+ Timetrap.start unused_args, args['-a']
91
+ end
92
+
93
+ def out
94
+ Timetrap.stop args['-a']
95
+ end
96
+
97
+ def kill
98
+ if e = Entry[args['-i']]
99
+ out = "are you sure you want to delete entry #{e.id}? "
100
+ out << "(#{e.note}) " if e.note.to_s =~ /.+/
101
+ print out
102
+ if $stdin.gets =~ /\Aye?s?\Z/i
103
+ e.destroy
104
+ say "it's dead"
105
+ else
106
+ say "will not kill"
107
+ end
108
+ elsif (sheets = Entry.map{|e| e.sheet }.uniq).include?(sheet = unused_args)
109
+ victims = Entry.filter(:sheet => sheet).count
110
+ print "are you sure you want to delete #{victims} entries on sheet #{sheet.inspect}? "
111
+ if $stdin.gets =~ /\Aye?s?\Z/i
112
+ Timetrap.kill_sheet sheet
113
+ say "killed #{victims} entries"
114
+ else
115
+ say "will not kill"
116
+ end
117
+ else
118
+ victim = args['-i'] ? args['-i'].to_s.inspect : sheet.inspect
119
+ say "can't find #{victim} to kill", 'sheets:', *sheets
120
+ end
121
+ end
122
+
123
+ def display
124
+ sheet = sheet_name_from_string(unused_args)
125
+ sheet = (sheet =~ /.+/ ? sheet : Timetrap.current_sheet)
126
+ say "Timesheet: #{sheet}"
127
+ id_heading = args['-v'] ? 'Id' : ' '
128
+ say "#{id_heading} Day Start End Duration Notes"
129
+ last_start = nil
130
+ from_current_day = []
131
+ ee = Timetrap::Entry.filter(:sheet => sheet).order(:start)
132
+ ee = ee.filter(:end >= Date.parse(args['-s'])) if args['-s']
133
+ ee = ee.filter(:start <= Date.parse(args['-e']) + 1) if args['-e']
134
+ ee.each_with_index do |e, i|
135
+
136
+
137
+ from_current_day << e
138
+ e_end = e.end || Time.now
139
+ say "%-4s%16s%11s -%9s%10s %s" % [
140
+ (args['-v'] ? e.id : ''),
141
+ format_date_if_new(e.start, last_start),
142
+ format_time(e.start),
143
+ format_time(e.end),
144
+ format_duration(e.start, e_end),
145
+ e.note
146
+ ]
147
+
148
+ nxt = ee.map[i+1]
149
+ if nxt == nil or !same_day?(e.start, nxt.start)
150
+ say "%52s" % format_total(from_current_day)
151
+ from_current_day = []
152
+ else
153
+ end
154
+ last_start = e.start
155
+ end
156
+ say <<-OUT
157
+ ---------------------------------------------------------
158
+ OUT
159
+ say " Total%43s" % format_total(ee)
160
+ end
161
+
162
+ # TODO: Consolidate display and format
163
+ def format
164
+ begin
165
+ fmt_klass = Timetrap::Formatters.const_get("#{unused_args.classify}")
166
+ rescue
167
+ say "Invalid format specified `#{unused_args}'"
168
+ return
169
+ end
170
+ ee = Timetrap::Entry.filter(:sheet => Timetrap.current_sheet)
171
+ ee = ee.filter(:end >= Date.parse(args['-s'])) if args['-s']
172
+ ee = ee.filter(:start <= Date.parse(args['-e']) + 1) if args['-e']
173
+ say Timetrap.format(fmt_klass,ee.all)
174
+ end
175
+
176
+ def switch
177
+ sheet = unused_args
178
+ if not sheet =~ /.+/ then say "No sheet specified"; return end
179
+ say "Switching to sheet " + Timetrap.switch(sheet)
180
+ end
181
+
182
+ def list
183
+ sheets = Entry.map{|e|e.sheet}.uniq.sort.map do |sheet|
184
+ sheet_atts = {:total => 0, :running => 0, :today => 0}
185
+ DB[:entries].filter(:sheet => sheet).inject(sheet_atts) do |m, e|
186
+ e_end = e[:end] || Time.now
187
+ m[:name] ||= sheet
188
+ m[:total] += (e_end.to_i - e[:start].to_i)
189
+ m[:running] += (e_end.to_i - e[:start].to_i) unless e[:end]
190
+ m[:today] += (e_end.to_i - e[:start].to_i) if same_day?(Time.now, e[:start])
191
+ m
192
+ end
193
+ end
194
+ width = sheets.sort_by{|h|h[:name].length }.last[:name].length + 4
195
+ say " %-#{width}s%-12s%-12s%s" % ["Timesheet", "Running", "Today", "Total Time"]
196
+ sheets.each do |sheet|
197
+ star = sheet[:name] == Timetrap.current_sheet ? '*' : ' '
198
+ say "#{star}%-#{width}s%-12s%-12s%s" % [
199
+ sheet[:running],
200
+ sheet[:today],
201
+ sheet[:total]
202
+ ].map(&method(:format_seconds)).unshift(sheet[:name])
203
+ end
204
+ end
205
+
206
+ def now
207
+ if Timetrap.running?
208
+ out = "#{Timetrap.current_sheet}: #{format_duration(Timetrap.active_entry.start, Time.now)}".gsub(/ /, ' ')
209
+ out << " (#{Timetrap.active_entry.note})" if Timetrap.active_entry.note =~ /.+/
210
+ say out
211
+ else
212
+ say "#{Timetrap.current_sheet}: not running"
213
+ end
214
+ end
215
+
216
+ def running
217
+ say "Sorry not implemented yet :-("
218
+ end
219
+
220
+ private
221
+
222
+ def unused_args
223
+ args.unused.join(' ')
224
+ end
225
+
226
+ end
227
+ end
@@ -0,0 +1,44 @@
1
+ module Timetrap
2
+ module Helpers
3
+ def format_time time
4
+ return '' unless time.respond_to?(:strftime)
5
+ time.strftime('%H:%M:%S')
6
+ end
7
+
8
+ def format_date time
9
+ return '' unless time.respond_to?(:strftime)
10
+ time.strftime('%a %b %d, %Y')
11
+ end
12
+
13
+ def format_date_if_new time, last_time
14
+ return '' unless time.respond_to?(:strftime)
15
+ same_day?(time, last_time) ? '' : format_date(time)
16
+ end
17
+
18
+ def same_day? time, other_time
19
+ format_date(time) == format_date(other_time)
20
+ end
21
+
22
+ def format_duration stime, etime
23
+ return '' unless stime and etime
24
+ secs = etime.to_i - stime.to_i
25
+ format_seconds secs
26
+ end
27
+
28
+ def format_seconds secs
29
+ "%2s:%02d:%02d" % [secs/3600, (secs%3600)/60, secs%60]
30
+ end
31
+
32
+ def format_total entries
33
+ secs = entries.inject(0){|m, e|e_end = e.end || Time.now; m += e_end.to_i - e.start.to_i if e_end && e.start;m}
34
+ "%2s:%02d:%02d" % [secs/3600, (secs%3600)/60, secs%60]
35
+ end
36
+
37
+ def sheet_name_from_string string
38
+ return "" unless string =~ /.+/
39
+ DB[:entries].filter(:sheet.like("#{string}%")).first[:sheet]
40
+ rescue
41
+ ""
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,34 @@
1
+ module Timetrap
2
+ class Entry < Sequel::Model
3
+ plugin :schema
4
+
5
+ def start= time
6
+ self[:start]= Chronic.parse(time) || time
7
+ end
8
+
9
+ def end= time
10
+ self[:end]= Chronic.parse(time) || time
11
+ end
12
+
13
+ # do a quick pseudo migration. This should only get executed on the first run
14
+ set_schema do
15
+ primary_key :id
16
+ column :note, :string
17
+ column :start, :timestamp
18
+ column :end, :timestamp
19
+ column :sheet, :string
20
+ end
21
+ create_table unless table_exists?
22
+ end
23
+
24
+ class Meta < Sequel::Model(:meta)
25
+ plugin :schema
26
+
27
+ set_schema do
28
+ primary_key :id
29
+ column :key, :string
30
+ column :value, :string
31
+ end
32
+ create_table unless table_exists?
33
+ end
34
+ end
data/lib/timetrap.rb CHANGED
@@ -1,262 +1,20 @@
1
1
  require 'rubygems'
2
2
  require 'chronic'
3
3
  require 'sequel'
4
+ require 'sequel/extensions/inflector'
4
5
  require 'Getopt/Declare'
5
- # connect to database. This will create one if it doesn't exist
6
+ require File.join(File.dirname(__FILE__), 'timetrap', 'helpers')
7
+ require File.join(File.dirname(__FILE__), 'timetrap', 'cli')
6
8
  DB_NAME = defined?(TEST_MODE) ? nil : "#{ENV['HOME']}/.timetrap.db"
9
+ # connect to database. This will create one if it doesn't exist
7
10
  DB = Sequel.sqlite DB_NAME
8
-
11
+ require File.join(File.dirname(__FILE__), 'timetrap', 'models')
12
+ Dir["#{File.dirname(__FILE__)}/timetrap/formatters/*.rb"].each do |path|
13
+ require path
14
+ end
9
15
  module Timetrap
10
16
  extend self
11
17
 
12
- module CLI
13
- attr_accessor :args
14
- extend self
15
-
16
- USAGE = <<-EOF
17
-
18
- Timetrap - Simple Time Tracking
19
-
20
- Usage: #{File.basename $0} COMMAND [OPTIONS] [ARGS...]
21
-
22
- where COMMAND is one of:
23
- * alter - alter an entry's note, start, or end time. Defaults to the active entry
24
- usage: t alter [--id ID] [--start TIME] [--end TIME] [NOTES]
25
- -i, --id <id:i> Alter entry with id <id> instead of the running entry
26
- -s, --start <time:qs> Change the start time to <time>
27
- -e, --end <time:qs> Change the end time to <time>
28
- * backend - open an sqlite shell to the database
29
- usage: t backend
30
- * display - display the current timesheet
31
- usage: t display [--ids] [TIMESHEET]
32
- -v, --ids Print database ids (for use with alter)
33
- * format - export a sheet to csv format
34
- NOT IMPLEMENTED
35
- * in - start the timer for the current timesheet
36
- usage: t in [--at TIME] [NOTES]
37
- -a, --at <time:qs> Use this time instead of now
38
- * kill - delete a timesheet
39
- usage: t kill [--id ID] [TIMESHEET]
40
- -i, --id <id:i> Alter entry with id <id> instead of the running entry
41
- * list - show the available timesheets
42
- usage: t list
43
- * now - show the status of the current timesheet
44
- usage: t now
45
- * out - stop the timer for the current timesheet
46
- usage: t out [--at TIME]
47
- -a, --at <time:qs> Use this time instead of now
48
- * running - show all running timesheets
49
- NOT IMPLEMENTED
50
- * switch - switch to a new timesheet
51
- usage: t switch TIMESHEET
52
-
53
- OTHER OPTIONS
54
- -h, --help Display this help
55
- EOF
56
-
57
- def parse arguments
58
- args.parse arguments
59
- end
60
-
61
- def invoke
62
- args['-h'] ? say(USAGE) : invoke_command_if_valid
63
- end
64
-
65
- def commands
66
- Timetrap::CLI::USAGE.scan(/\* \w+/).map{|s| s.gsub(/\* /, '')}
67
- end
68
-
69
- def invoke_command_if_valid
70
- command = args.unused.shift
71
- case (valid = commands.select{|name| name =~ %r|^#{command}|}).size
72
- when 0 then say "Invalid command: #{command}"
73
- when 1 then send valid[0]
74
- else; say "Ambigous command: #{command}"; end
75
- end
76
-
77
- def alter
78
- entry = args['-i'] ? Entry[args['-i']] : Timetrap.active_entry
79
- say "can't find entry" && return unless entry
80
- entry.update :start => args['-s'] if args['-s'] =~ /.+/
81
- entry.update :end => args['-e'] if args['-e'] =~ /.+/
82
- entry.update :note => unused_args if unused_args =~ /.+/
83
- end
84
-
85
- def backend
86
- exec "sqlite3 #{DB_NAME}"
87
- end
88
-
89
- def in
90
- Timetrap.start unused_args, args['-a']
91
- end
92
-
93
- def out
94
- Timetrap.stop args['-a']
95
- end
96
-
97
- def kill
98
- if e = Entry[args['-i']]
99
- out = "are you sure you want to delete entry #{e.id}? "
100
- out << "(#{e.note}) " if e.note.to_s =~ /.+/
101
- print out
102
- if $stdin.gets =~ /\Aye?s?\Z/i
103
- e.destroy
104
- say "it's dead"
105
- else
106
- say "will not kill"
107
- end
108
- elsif (sheets = Entry.map{|e| e.sheet }.uniq).include?(sheet = unused_args)
109
- victims = Entry.filter(:sheet => sheet).count
110
- print "are you sure you want to delete #{victims} entries on sheet #{sheet.inspect}? "
111
- if $stdin.gets =~ /\Aye?s?\Z/i
112
- Timetrap.kill_sheet sheet
113
- say "killed #{victims} entries"
114
- else
115
- say "will not kill"
116
- end
117
- else
118
- victim = args['-i'] ? args['-i'].to_s.inspect : sheet.inspect
119
- say "can't find #{victim} to kill", 'sheets:', *sheets
120
- end
121
- end
122
-
123
- def display
124
- sheet = sheet_name_from_string(unused_args)
125
- sheet = (sheet =~ /.+/ ? sheet : Timetrap.current_sheet)
126
- say "Timesheet: #{sheet}"
127
- id_heading = args['-v'] ? 'Id' : ' '
128
- say "#{id_heading} Day Start End Duration Notes"
129
- last_start = nil
130
- from_current_day = []
131
- (ee = Timetrap.entries(sheet)).each_with_index do |e, i|
132
-
133
-
134
- from_current_day << e
135
- e_end = e.end || Time.now
136
- say "%-4s%16s%11s -%9s%10s %s" % [
137
- (args['-v'] ? e.id : ''),
138
- format_date_if_new(e.start, last_start),
139
- format_time(e.start),
140
- format_time(e.end),
141
- format_duration(e.start, e_end),
142
- e.note
143
- ]
144
-
145
- nxt = Timetrap.entries(sheet).map[i+1]
146
- if nxt == nil or !same_day?(e.start, nxt.start)
147
- say "%52s" % format_total(from_current_day)
148
- from_current_day = []
149
- else
150
- end
151
- last_start = e.start
152
- end
153
- say <<-OUT
154
- ---------------------------------------------------------
155
- OUT
156
- say " Total%43s" % format_total(ee)
157
- end
158
-
159
- def format
160
- say "Sorry not implemented yet :-("
161
- end
162
-
163
- def switch
164
- sheet = args.unused.join(' ')
165
- if not sheet =~ /.+/ then say "No sheet specified"; return end
166
- say "Switching to sheet " + Timetrap.switch(sheet)
167
- end
168
-
169
- def list
170
- sheets = Entry.map{|e|e.sheet}.uniq.sort.map do |sheet|
171
- sheet_atts = {:total => 0, :running => 0, :today => 0}
172
- DB[:entries].filter(:sheet => sheet).inject(sheet_atts) do |m, e|
173
- e_end = e[:end] || Time.now
174
- m[:name] ||= sheet
175
- m[:total] += (e_end.to_i - e[:start].to_i)
176
- m[:running] += (e_end.to_i - e[:start].to_i) unless e[:end]
177
- m[:today] += (e_end.to_i - e[:start].to_i) if same_day?(Time.now, e[:start])
178
- m
179
- end
180
- end
181
- width = sheets.sort_by{|h|h[:name].length }.last[:name].length + 4
182
- say " %-#{width}s%-12s%-12s%s" % ["Timesheet", "Running", "Today", "Total Time"]
183
- sheets.each do |sheet|
184
- star = sheet[:name] == Timetrap.current_sheet ? '*' : ' '
185
- say "#{star}%-#{width}s%-12s%-12s%s" % [
186
- sheet[:running],
187
- sheet[:today],
188
- sheet[:total]
189
- ].map(&method(:format_seconds)).unshift(sheet[:name])
190
- end
191
- end
192
-
193
- def now
194
- if Timetrap.running?
195
- out = "#{Timetrap.current_sheet}: #{format_duration(Timetrap.active_entry.start, Time.now)}".gsub(/ /, ' ')
196
- out << " (#{Timetrap.active_entry.note})" if Timetrap.active_entry.note =~ /.+/
197
- say out
198
- else
199
- say "#{Timetrap.current_sheet}: not running"
200
- end
201
- end
202
-
203
- def running
204
- say "Sorry not implemented yet :-("
205
- end
206
-
207
- private
208
-
209
- def format_time time
210
- return '' unless time.respond_to?(:strftime)
211
- time.strftime('%H:%M:%S')
212
- end
213
-
214
- def format_date time
215
- return '' unless time.respond_to?(:strftime)
216
- time.strftime('%a %b %d, %Y')
217
- end
218
-
219
- def format_date_if_new time, last_time
220
- return '' unless time.respond_to?(:strftime)
221
- same_day?(time, last_time) ? '' : format_date(time)
222
- end
223
-
224
- def same_day? time, other_time
225
- format_date(time) == format_date(other_time)
226
- end
227
-
228
- def format_duration stime, etime
229
- return '' unless stime and etime
230
- secs = etime.to_i - stime.to_i
231
- format_seconds secs
232
- end
233
-
234
- def format_seconds secs
235
- "%2s:%02d:%02d" % [secs/3600, (secs%3600)/60, secs%60]
236
- end
237
-
238
- def format_total entries
239
- secs = entries.inject(0){|m, e|e_end = e.end || Time.now; m += e_end.to_i - e.start.to_i if e_end && e.start;m}
240
- "%2s:%02d:%02d" % [secs/3600, (secs%3600)/60, secs%60]
241
- end
242
-
243
- def sheet_name_from_string string
244
- return "" unless string =~ /.+/
245
- DB[:entries].filter(:sheet.like("#{string}%")).first[:sheet]
246
- rescue
247
- ""
248
- end
249
-
250
- def unused_args
251
- args.unused.join(' ')
252
- end
253
-
254
- public
255
- def say *something
256
- puts *something
257
- end
258
- end
259
-
260
18
  def current_sheet= sheet
261
19
  m = Meta.find_or_create(:key => 'current_sheet')
262
20
  m.value = sheet
@@ -306,45 +64,16 @@ where COMMAND is one of:
306
64
  Entry.filter(:sheet => sheet).destroy
307
65
  end
308
66
 
67
+ def format format_klass, entries
68
+ format_klass.new(entries).output
69
+ end
70
+
309
71
  class AlreadyRunning < StandardError
310
72
  def message
311
73
  "Timetrap is already running"
312
74
  end
313
75
  end
314
76
 
315
-
316
- class Entry < Sequel::Model
317
- plugin :schema
318
-
319
- def start= time
320
- self[:start]= Chronic.parse(time) || time
321
- end
322
-
323
- def end= time
324
- self[:end]= Chronic.parse(time) || time
325
- end
326
-
327
- # do a quick pseudo migration. This should only get executed on the first run
328
- set_schema do
329
- primary_key :id
330
- column :note, :string
331
- column :start, :timestamp
332
- column :end, :timestamp
333
- column :sheet, :string
334
- end
335
- create_table unless table_exists?
336
- end
337
-
338
- class Meta < Sequel::Model(:meta)
339
- plugin :schema
340
-
341
- set_schema do
342
- primary_key :id
343
- column :key, :string
344
- column :value, :string
345
- end
346
- create_table unless table_exists?
347
- end
348
77
  CLI.args = Getopt::Declare.new(<<-EOF)
349
78
  #{CLI::USAGE}
350
79
  EOF
@@ -5,7 +5,7 @@ require 'spec'
5
5
  describe Timetrap do
6
6
  def create_entry atts = {}
7
7
  Timetrap::Entry.create({
8
- :sheet => 's1',
8
+ :sheet => 'default',
9
9
  :start => Time.now,
10
10
  :end => Time.now,
11
11
  :note => 'note'}.merge(atts))
@@ -136,8 +136,42 @@ Id Day Start End Duration Notes
136
136
  end
137
137
 
138
138
  describe "format" do
139
- it "should export a sheet to a csv format" do
140
- pending
139
+ describe 'ical' do
140
+ before do
141
+ create_entry(:start => '2008-10-03 12:00:00', :end => '2008-10-03 14:00:00')
142
+ create_entry(:start => '2008-10-05 12:00:00', :end => '2008-10-05 14:00:00')
143
+ end
144
+
145
+ it "should filter events by the passed dates" do
146
+ invoke 'format ical --start 2008-10-03 --end 2008-10-03'
147
+ $stdout.string.scan(/BEGIN:VEVENT/).should have(1).item
148
+ end
149
+
150
+ it "should not filter events by date when none are passed" do
151
+ invoke 'format ical'
152
+ $stdout.string.scan(/BEGIN:VEVENT/).should have(2).item
153
+ end
154
+
155
+ it "should export a sheet to an ical format" do
156
+ invoke 'format ical --start 2008-10-03 --end 2008-10-03'
157
+ desired = <<-EOF
158
+ BEGIN:VCALENDAR
159
+ VERSION:2.0
160
+ CALSCALE:GREGORIAN
161
+ METHOD:PUBLISH
162
+ PRODID:iCalendar-Ruby
163
+ BEGIN:VEVENT
164
+ SEQUENCE:0
165
+ DTEND:20081003T140000
166
+ SUMMARY:note
167
+ DTSTART:20081003T120000
168
+ END:VEVENT
169
+ END:VCALENDAR
170
+ EOF
171
+ desired.each_line do |line|
172
+ $stdout.string.should =~ /#{line.chomp}/
173
+ end
174
+ end
141
175
  end
142
176
  end
143
177
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: samg-timetrap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Goldstein
@@ -71,12 +71,18 @@ extensions: []
71
71
  extra_rdoc_files: []
72
72
 
73
73
  files:
74
- - README.md
75
74
  - LICENSE.txt
76
75
  - Rakefile
77
- - lib/timetrap.rb
76
+ - README.md
77
+ - bin/dev_t
78
78
  - bin/t
79
+ - lib/timetrap
80
+ - lib/timetrap.rb
79
81
  - spec/timetrap_spec.rb
82
+ - lib/timetrap/cli.rb
83
+ - lib/timetrap/formatters
84
+ - lib/timetrap/helpers.rb
85
+ - lib/timetrap/models.rb
80
86
  has_rdoc: true
81
87
  homepage: http://github.com/samg/timetrap/tree/master
82
88
  post_install_message: