timetrap 1.6.1 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/CONTRIBUTORS CHANGED
@@ -1,3 +1,4 @@
1
1
  * Sam Goldstein
2
2
  * Ian Smith-Heisters
3
3
  * Ammar Ali
4
+ * Brian V. Hughes
data/README.md CHANGED
@@ -64,6 +64,31 @@ You check out with the `out` command.
64
64
  $ t out
65
65
  Checked out of sheet "coding"
66
66
 
67
+ You can edit entries that aren't running using `edit`'s `--id` or `-i` flag.
68
+ `t display --verbose` will tell you the ids.
69
+
70
+ $ # note id column in output
71
+ $ t d -v
72
+ Timesheet: coding
73
+ Id Day Start End Duration Notes
74
+ 43 Sun Nov 28, 2010 12:26:10 - 13:41:03 1:14:53 writing readme
75
+ 1:14:53
76
+ ---------------------------------------------------------
77
+ Total 1:14:53
78
+
79
+ $ # -i43 to edit entry 43
80
+ $ t e -i43 --end "2010-11-28 13:45"
81
+ editing entry #43
82
+
83
+ $ t d
84
+ Timesheet: coding
85
+ Day Start End Duration Notes
86
+ Sun Nov 28, 2010 12:26:10 - 13:45:00 1:18:50 writing readme
87
+ 1:18:50
88
+ ---------------------------------------------------------
89
+ Total 1:18:50
90
+
91
+
67
92
  ### Natural Language Times
68
93
 
69
94
  Commands such as `in`, `out`, `edit`, and `display` have flags that accept
@@ -91,7 +116,14 @@ list of parsable time formats, but all of these should work.
91
116
 
92
117
  ### Output Formats
93
118
 
94
- Timetrap supports several output formats. The default is a plain text format.
119
+ #### Built-in Formatters
120
+
121
+ Timetrap has built-in support for 5 output formats.
122
+
123
+ These are **text**, **csv**, **ical**, **json**, and **ids**
124
+
125
+ The default is a plain **text** format. (You can change the default format using
126
+ `t configure`).
95
127
 
96
128
  $ t display
97
129
  Timesheet: coding
@@ -107,7 +139,7 @@ Timetrap supports several output formats. The default is a plain text format.
107
139
  ---------------------------------------------------------
108
140
  Total 3:46:05
109
141
 
110
- You can also output csv for easy import into a spreadsheet.
142
+ The **CSV** formatters is easy to import into a spreadsheet.
111
143
 
112
144
  $ t display --format csv
113
145
  start,end,note,sheet
@@ -120,10 +152,73 @@ You can also output csv for easy import into a spreadsheet.
120
152
  "2010-08-29 21:15:58","2010-08-29 21:30:31","backups","coding"
121
153
  "2010-08-29 21:40:56","2010-08-29 22:32:26","backups","coding"
122
154
 
123
- Or to ical format for import into a calendar program (remember commands can be abbreviated).
155
+ **iCal** format lets you get your time into your favorite calendar program
156
+ (remember commands can be abbreviated).
124
157
 
125
158
  $ t d -f ical > MyTimeSheet.ics
126
159
 
160
+ The **ids** formatter is provided to facilitate scripting within timetrap. It only
161
+ outputs numeric id for the entries. This is handy if you want to move all entries
162
+ from one sheet to another sheet. You could do something like this:
163
+
164
+ $ for id in `t display sheet1 -f ids`; do t edit --id $id --move sheet2; done
165
+ editing entry #36
166
+ editing entry #37
167
+ editing entry #44
168
+ editing entry #46
169
+
170
+ A *json* formatter is also provided, because hackers love json.
171
+
172
+ $ t d -fjson
173
+
174
+ #### Custom Formatters
175
+
176
+ Timetrap tries to make it easy to define custom output formats.
177
+
178
+ You're encouraged to submit these back to timetrap for inclusion in a future
179
+ version.
180
+
181
+ To create a custom formatter you create a ruby class and implement two methods
182
+ on it.
183
+
184
+ As an example we'll create a formatter that only outputs the notes from
185
+ entries.
186
+
187
+ To ensure that timetrap can find your formatter put it in
188
+ `~/.timetrap/formatters/notes.rb`. The filename should be the same as the
189
+ string you will pass to `t d --format` to invoke it. If you want to put your
190
+ formatter in a different place you can run `t configure` and edit the
191
+ `formatter_search_paths` option.
192
+
193
+ All timetrap formatters live under the namespace `Timetrap::Formatters` so
194
+ define your class like this:
195
+
196
+ class Timetrap::Formatters::Notes
197
+ end
198
+
199
+ When `t display` is invoked, timetrap initializes a new instance of the
200
+ formatter passing it an Array of entries. It then calls `#output` which should
201
+ return a string to be printed to the screen.
202
+
203
+ This means we need to implement an `#initialize` method and an `#output`
204
+ method for the class. Something like this:
205
+
206
+ class Timetrap::Formatters::Notes
207
+ def initialize(entries)
208
+ @entries = entries
209
+ end
210
+
211
+ def output
212
+ @entries.map{|entry| entry[:note]}.join("\n")
213
+ end
214
+ end
215
+
216
+ Now when I invoke it:
217
+
218
+ $ t d -f notes
219
+ working on issue #123
220
+ working on issue #234
221
+
127
222
  Commands
128
223
  --------
129
224
  **archive**
@@ -139,9 +234,9 @@ Commands
139
234
 
140
235
  **configure**
141
236
  Creates a config file at ``~/.timetrap.yml`` or ``ENV['TIMETRAP_CONFIG_FILE']`` if
142
- one doesn't exist. Prints path to config file. Currently allows configuration
143
- of path to database file, and the number of seconds used when the `--round`
144
- flag is set (defaults to 15 minutes.)
237
+ one doesn't exist. If one does exist it will update it with new
238
+ configuration options preserving any user overrides. Prints path to config
239
+ file. This file may contain ERB.
145
240
 
146
241
  usage: ``t configure``
147
242
 
@@ -153,7 +248,7 @@ Commands
153
248
 
154
249
  Display is designed to support a variety of export formats that can be
155
250
  specified by passing the ``--format`` flag. This currently defaults to
156
- text. iCal and csv output are also supported.
251
+ text. iCal, csv, json, and numeric id output are also supported.
157
252
 
158
253
  Display also allows the use of a ``--round`` or ``-r`` flag which will round
159
254
  all times in the output. See global options below.
@@ -227,14 +322,20 @@ Global Options
227
322
  Configuration
228
323
  --------
229
324
 
230
- Configuration of TimeTrap's behavior can be done through a YAML config file.
325
+ Configuration of TimeTrap's behavior can be done through an ERB interpolated
326
+ YAML config file.
327
+
231
328
  See ``t configure`` for details. Currently supported options are:
232
329
 
233
- ``round_in_seconds``: The duration of time to use for rounding with the -r flag
330
+ **round_in_seconds**: The duration of time to use for rounding with the -r flag
331
+
332
+ **database_file**: The file path of the sqlite database
333
+
334
+ **append_notes_delimiter**: delimiter used when appending notes via `t edit --append`
234
335
 
235
- ``database_file``: The file path of the sqlite database
336
+ **formatter_search_paths**: an array of directories to search for user defined fomatter classes
236
337
 
237
- ``append_notes_delimiter``: delimiter used when appending notes via ``t edit --append``
338
+ **default_formatter**: The format to use when display is invoked without a `--format` option
238
339
 
239
340
  Special Thanks
240
341
  --------------
data/Rakefile CHANGED
@@ -36,7 +36,6 @@ begin
36
36
  s.add_dependency("sqlite3-ruby", ">= 1.2.5")
37
37
  s.add_dependency("chronic", "~> 0.3.0")
38
38
  s.add_dependency("getopt-declare", ">= 1.28")
39
- s.add_dependency("icalendar", ">= 1.1.2")
40
39
  end
41
40
  rescue LoadError
42
41
  puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 1
3
- :minor: 6
4
- :patch: 1
3
+ :minor: 7
4
+ :patch: 0
data/lib/timetrap.rb CHANGED
@@ -2,22 +2,20 @@ require 'rubygems'
2
2
  require 'chronic'
3
3
  require 'sequel'
4
4
  require 'yaml'
5
+ require 'erb'
5
6
  require 'sequel/extensions/inflector'
6
7
  require 'Getopt/Declare'
7
8
  require File.join(File.dirname(__FILE__), 'timetrap', 'config')
8
9
  require File.join(File.dirname(__FILE__), 'timetrap', 'helpers')
9
10
  require File.join(File.dirname(__FILE__), 'timetrap', 'cli')
10
11
  require File.join(File.dirname(__FILE__), 'timetrap', 'timer')
11
- DB_NAME = defined?(TEST_MODE) ? nil : Timetrap::Config['database_file']
12
- # connect to database. This will create one if it doesn't exist
13
- DB = Sequel.sqlite DB_NAME
14
- require File.join(File.dirname(__FILE__), 'timetrap', 'models')
15
- Dir["#{File.dirname(__FILE__)}/timetrap/formatters/*.rb"].each do |path|
16
- require path
17
- end
18
-
12
+ require File.join(File.dirname(__FILE__), 'timetrap', 'formatters')
19
13
  module Timetrap
14
+ DB_NAME = defined?(TEST_MODE) ? nil : Timetrap::Config['database_file']
15
+ # connect to database. This will create one if it doesn't exist
16
+ DB = Sequel.sqlite DB_NAME
20
17
  CLI.args = Getopt::Declare.new(<<-EOF)
21
18
  #{CLI::USAGE}
22
19
  EOF
23
20
  end
21
+ require File.join(File.dirname(__FILE__), 'timetrap', 'models')
data/lib/timetrap/cli.rb CHANGED
@@ -23,7 +23,8 @@ COMMAND is one of:
23
23
  * backend - Open an sqlite shell to the database.
24
24
  usage: t backend
25
25
 
26
- * configure - Write out a config file. print path to config file.
26
+ * configure - Write out a YAML config file. Print path to config file. The
27
+ file may contain ERB.
27
28
  usage: t configure
28
29
  Currently supported options are:
29
30
  round_in_seconds: The duration of time to use for rounding with
@@ -38,8 +39,11 @@ COMMAND is one of:
38
39
  -v, --ids Print database ids (for use with edit)
39
40
  -s, --start <date:qs> Include entries that start on this date or later
40
41
  -e, --end <date:qs> Include entries that start on this date or earlier
41
- -f, --format <format> The output format. Currently supports ical, csv, and
42
- text (default).
42
+ -f, --format <format> The output format. Valid built-in formats are
43
+ ical, csv, json, ids, and text (default).
44
+ Documentation on defining custom formats can be
45
+ found in the README included in this
46
+ distribution.
43
47
 
44
48
  * edit - Alter an entry's note, start, or end time. Defaults to the active entry.
45
49
  usage: t edit [--id ID] [--start TIME] [--end TIME] [--append] [NOTES]
@@ -106,7 +110,7 @@ COMMAND is one of:
106
110
 
107
111
  def invoke
108
112
  args['-h'] ? puts(USAGE) : invoke_command_if_valid
109
- rescue => e
113
+ rescue StandardError, LoadError => e
110
114
  raise e if args['--debug']
111
115
  warn e.message
112
116
  exit 1 unless defined? TEST_MODE
@@ -240,21 +244,11 @@ COMMAND is one of:
240
244
  end
241
245
 
242
246
  def display
243
- begin
244
- fmt_klass = if args['-f']
245
- Timetrap::Formatters.const_get("#{args['-f'].classify}")
246
- else
247
- Timetrap::Formatters::Text
248
- end
249
- rescue
250
- warn "Invalid format #{args['-f'].inspect} specified."
251
- return
252
- end
253
247
  entries = selected_entries.order(:start).all
254
248
  if entries == []
255
249
  warn "No entries were selected to display."
256
250
  else
257
- puts fmt_klass.new(entries).output
251
+ puts load_formatter(args['-f'] || Config['default_formatter']).new(entries).output
258
252
  end
259
253
  end
260
254
 
@@ -16,12 +16,18 @@ module Timetrap
16
16
  # Unit of time for rounding (-r) in seconds
17
17
  'round_in_seconds' => 900,
18
18
  # delimiter used when appending notes with `t edit --append`
19
- 'append_notes_delimiter' => ' '
19
+ 'append_notes_delimiter' => ' ',
20
+ # an array of directories to search for user defined fomatter classes
21
+ 'formatter_search_paths' => [
22
+ "#{ENV['HOME']}/.timetrap/formatters"
23
+ ],
24
+ # formatter to use when display is invoked without a --format option
25
+ 'default_formatter' => 'text'
20
26
  }
21
27
  end
22
28
 
23
29
  def [](key)
24
- overrides = File.exist?(PATH) ? YAML.load(File.read(PATH)) : {}
30
+ overrides = File.exist?(PATH) ? YAML.load(erb_render(File.read(PATH))) : {}
25
31
  defaults.merge(overrides)[key]
26
32
  rescue => e
27
33
  warn "invalid config file"
@@ -29,6 +35,10 @@ module Timetrap
29
35
  defaults[key]
30
36
  end
31
37
 
38
+ def erb_render(content)
39
+ ERB.new(content).result
40
+ end
41
+
32
42
  def configure!
33
43
  configs = if File.exist?(PATH)
34
44
  defaults.merge(YAML.load_file(PATH))
@@ -0,0 +1,3 @@
1
+ # Namespace for formatter classes
2
+ module Timetrap::Formatters
3
+ end
@@ -1,4 +1,13 @@
1
- require 'icalendar'
1
+ begin
2
+ require 'icalendar'
3
+ rescue LoadError
4
+ raise <<-ERR
5
+ The icalendar gem must be installed for ical output.
6
+ To install it:
7
+ $ [sudo] gem install icalendar -v"~>1.1.5"
8
+ ERR
9
+ end
10
+
2
11
  require 'date'
3
12
  module Timetrap
4
13
  module Formatters
@@ -0,0 +1,11 @@
1
+ module Timetrap
2
+ module Formatters
3
+ class Ids
4
+ attr_reader :output
5
+
6
+ def initialize entries
7
+ @output = entries.map(&:id).join(' ')
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,24 @@
1
+ begin
2
+ require 'json'
3
+ rescue LoadError
4
+ raise <<-ERR
5
+ The json gem must be installed for json output.
6
+ To install it:
7
+ $ [sudo] gem install json -v"~>1.4.6"
8
+ ERR
9
+ end
10
+
11
+ module Timetrap
12
+ module Formatters
13
+ class Json
14
+ attr_accessor :output
15
+
16
+ def initialize entries
17
+ @output = entries.map do |e|
18
+ next unless e.end
19
+ e.values
20
+ end.compact.to_json
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,6 +1,33 @@
1
1
  module Timetrap
2
2
  module Helpers
3
3
 
4
+ def load_formatter(formatter)
5
+ err_msg = "Can't load #{formatter.inspect} formatter."
6
+ begin
7
+ paths = (
8
+ Array(Config['formatter_search_paths']) +
9
+ [ File.join( File.dirname(__FILE__), 'formatters') ]
10
+ )
11
+ if paths.detect do |path|
12
+ begin
13
+ fp = File.join(path, formatter)
14
+ require File.join(path, formatter)
15
+ true
16
+ rescue LoadError
17
+ nil
18
+ end
19
+ end
20
+ else
21
+ raise LoadError, "Couldn't find #{formatter}.rb in #{paths.inspect}"
22
+ end
23
+ Timetrap::Formatters.const_get(formatter.camelize)
24
+ rescue LoadError, NameError => e
25
+ err = e.class.new("#{err_msg} (#{e.message})")
26
+ err.set_backtrace(e.backtrace)
27
+ raise err
28
+ end
29
+ end
30
+
4
31
  def selected_entries
5
32
  ee = if (sheet = sheet_name_from_string(unused_args)) == 'all'
6
33
  Timetrap::Entry.filter('sheet not like ? escape "!"', '!_%')
@@ -9,8 +36,8 @@ module Timetrap
9
36
  else
10
37
  Timetrap::Entry.filter('sheet = ?', Timer.current_sheet)
11
38
  end
12
- ee = ee.filter('start >= ?', Date.parse(args['-s'])) if args['-s']
13
- ee = ee.filter('start <= ?', Date.parse(args['-e']) + 1) if args['-e']
39
+ ee = ee.filter('start >= ?', Date.parse(Chronic.parse(args['-s']).to_s)) if args['-s']
40
+ ee = ee.filter('start <= ?', Date.parse(Chronic.parse(args['-e']).to_s) + 1) if args['-e']
14
41
  ee
15
42
  end
16
43
 
@@ -161,31 +161,44 @@ describe Timetrap do
161
161
 
162
162
  describe "backend" do
163
163
  it "should open an sqlite console to the db" do
164
- Timetrap::CLI.should_receive(:exec).with("sqlite3 #{DB_NAME}")
164
+ Timetrap::CLI.should_receive(:exec).with("sqlite3 #{Timetrap::DB_NAME}")
165
165
  invoke 'backend'
166
166
  end
167
167
  end
168
168
 
169
- describe "display" do
169
+ describe "format" do
170
170
  before do
171
- Timetrap::Entry.create( :sheet => 'another',
172
- :note => 'entry 4', :start => '2008-10-05 18:00:00'
173
- )
174
- Timetrap::Entry.create( :sheet => 'SpecSheet',
175
- :note => 'entry 2', :start => '2008-10-03 16:00:00', :end => '2008-10-03 18:00:00'
176
- )
177
- Timetrap::Entry.create( :sheet => 'SpecSheet',
178
- :note => 'entry 1', :start => '2008-10-03 12:00:00', :end => '2008-10-03 14:00:00'
179
- )
180
- Timetrap::Entry.create( :sheet => 'SpecSheet',
181
- :note => 'entry 3', :start => '2008-10-05 16:00:00', :end => '2008-10-05 18:00:00'
182
- )
183
- Timetrap::Entry.create( :sheet => 'SpecSheet',
184
- :note => 'entry 4', :start => '2008-10-05 18:00:00'
185
- )
171
+ create_entry
172
+ end
173
+ it "should be deprecated" do
174
+ invoke 'format'
175
+ $stderr.string.should == <<-WARN
176
+ The "format" command is deprecated in favor of "display". Sorry for the inconvenience.
177
+ WARN
178
+ end
179
+ end
186
180
 
187
- Time.stub!(:now).and_return Time.at(1223254800 + (60*60*2))
188
- @desired_output = <<-OUTPUT
181
+ describe "display" do
182
+ describe "text" do
183
+ before do
184
+ Timetrap::Entry.create( :sheet => 'another',
185
+ :note => 'entry 4', :start => '2008-10-05 18:00:00'
186
+ )
187
+ Timetrap::Entry.create( :sheet => 'SpecSheet',
188
+ :note => 'entry 2', :start => '2008-10-03 16:00:00', :end => '2008-10-03 18:00:00'
189
+ )
190
+ Timetrap::Entry.create( :sheet => 'SpecSheet',
191
+ :note => 'entry 1', :start => '2008-10-03 12:00:00', :end => '2008-10-03 14:00:00'
192
+ )
193
+ Timetrap::Entry.create( :sheet => 'SpecSheet',
194
+ :note => 'entry 3', :start => '2008-10-05 16:00:00', :end => '2008-10-05 18:00:00'
195
+ )
196
+ Timetrap::Entry.create( :sheet => 'SpecSheet',
197
+ :note => 'entry 4', :start => '2008-10-05 18:00:00'
198
+ )
199
+
200
+ Time.stub!(:now).and_return Time.at(1223254800 + (60*60*2))
201
+ @desired_output = <<-OUTPUT
189
202
  Timesheet: SpecSheet
190
203
  Day Start End Duration Notes
191
204
  Fri Oct 03, 2008 12:00:00 - 14:00:00 2:00:00 entry 1
@@ -196,9 +209,9 @@ Timesheet: SpecSheet
196
209
  4:00:00
197
210
  ---------------------------------------------------------
198
211
  Total 8:00:00
199
- OUTPUT
212
+ OUTPUT
200
213
 
201
- @desired_output_with_ids = <<-OUTPUT
214
+ @desired_output_with_ids = <<-OUTPUT
202
215
  Timesheet: SpecSheet
203
216
  Id Day Start End Duration Notes
204
217
  3 Fri Oct 03, 2008 12:00:00 - 14:00:00 2:00:00 entry 1
@@ -209,9 +222,9 @@ Id Day Start End Duration Notes
209
222
  4:00:00
210
223
  ---------------------------------------------------------
211
224
  Total 8:00:00
212
- OUTPUT
225
+ OUTPUT
213
226
 
214
- @desired_output_for_all = <<-OUTPUT
227
+ @desired_output_for_all = <<-OUTPUT
215
228
  Timesheet: SpecSheet
216
229
  Day Start End Duration Notes
217
230
  Fri Oct 03, 2008 12:00:00 - 14:00:00 2:00:00 entry 1
@@ -231,72 +244,116 @@ Timesheet: another
231
244
  Total 2:00:00
232
245
  -------------------------------------------------------------
233
246
  Grand Total 10:00:00
234
- OUTPUT
235
- end
247
+ OUTPUT
248
+ end
236
249
 
237
- it "should display the current timesheet" do
238
- Timetrap::Timer.current_sheet = 'SpecSheet'
239
- invoke 'display'
240
- $stdout.string.should == @desired_output
241
- end
250
+ it "should display the current timesheet" do
251
+ Timetrap::Timer.current_sheet = 'SpecSheet'
252
+ invoke 'display'
253
+ $stdout.string.should == @desired_output
254
+ end
242
255
 
243
- it "should display a non current timesheet" do
244
- Timetrap::Timer.current_sheet = 'another'
245
- invoke 'display SpecSheet'
246
- $stdout.string.should == @desired_output
247
- end
256
+ it "should display a non current timesheet" do
257
+ Timetrap::Timer.current_sheet = 'another'
258
+ invoke 'display SpecSheet'
259
+ $stdout.string.should == @desired_output
260
+ end
248
261
 
249
- it "should display a non current timesheet based on a partial name match" do
250
- Timetrap::Timer.current_sheet = 'another'
251
- invoke 'display S'
252
- $stdout.string.should == @desired_output
253
- end
262
+ it "should display a non current timesheet based on a partial name match" do
263
+ Timetrap::Timer.current_sheet = 'another'
264
+ invoke 'display S'
265
+ $stdout.string.should == @desired_output
266
+ end
254
267
 
255
- it "should prefer an exact match of a named sheet to a partial match" do
256
- Timetrap::Timer.current_sheet = 'Spec'
257
- Timetrap::Entry.create( :sheet => 'Spec',
258
- :note => 'entry 5', :start => '2008-10-05 18:00:00'
259
- )
260
- invoke 'display Spec'
261
- $stdout.string.should include("entry 5")
262
- end
268
+ it "should prefer an exact match of a named sheet to a partial match" do
269
+ Timetrap::Timer.current_sheet = 'Spec'
270
+ Timetrap::Entry.create( :sheet => 'Spec',
271
+ :note => 'entry 5', :start => '2008-10-05 18:00:00'
272
+ )
273
+ invoke 'display Spec'
274
+ $stdout.string.should include("entry 5")
275
+ end
263
276
 
264
- it "should display a timesheet with ids" do
265
- invoke 'display S --ids'
266
- $stdout.string.should == @desired_output_with_ids
267
- end
277
+ it "should display a timesheet with ids" do
278
+ invoke 'display S --ids'
279
+ $stdout.string.should == @desired_output_with_ids
280
+ end
268
281
 
269
- it "should display all timesheets" do
270
- Timetrap::Timer.current_sheet = 'another'
271
- invoke 'display all'
272
- $stdout.string.should == @desired_output_for_all
282
+ it "should display all timesheets" do
283
+ Timetrap::Timer.current_sheet = 'another'
284
+ invoke 'display all'
285
+ $stdout.string.should == @desired_output_for_all
286
+ end
287
+
288
+ it "should not display archived for all timesheets" do
289
+ $stdin.string = "yes\n"
290
+ invoke 'archive SpecSheet'
291
+ $stdout.string = ''
292
+ invoke 'display all'
293
+ $stdout.string.should_not =~ /_SpecSheet/
294
+ end
295
+
296
+ it "it should find a user provided formatter class and require it" do
297
+ create_entry
298
+ create_entry
299
+ dir = '/tmp/timetrap/foo/bar'
300
+ with_stubbed_config('formatter_search_paths' => dir)
301
+ FileUtils.mkdir_p(dir)
302
+ File.open(dir + '/baz.rb', 'w') do |f|
303
+ f.puts <<-RUBY
304
+ class Timetrap::Formatters::Baz
305
+ def initialize(entries); end
306
+ def output
307
+ "yeah I did it"
308
+ end
309
+ end
310
+ RUBY
311
+ end
312
+ invoke 'd -fbaz'
313
+ $stderr.string.should == ''
314
+ $stdout.string.should == "yeah I did it\n"
315
+ FileUtils.rm_r dir
316
+ end
273
317
  end
274
318
 
275
- it "should not display archived for all timesheets" do
276
- $stdin.string = "yes\n"
277
- invoke 'archive SpecSheet'
278
- $stdout.string = ''
279
- invoke 'display all'
280
- $stdout.string.should_not =~ /_SpecSheet/
319
+ describe "default" do
320
+ before do
321
+ create_entry(:start => '2008-10-03 12:00:00', :end => '2008-10-03 14:00:00')
322
+ create_entry(:start => '2008-10-05 12:00:00', :end => '2008-10-05 14:00:00')
323
+ end
324
+
325
+ it "should allow another formatter to be set as the default" do
326
+ with_stubbed_config 'default_formatter' => 'ids',
327
+ 'formatter_search_paths' => nil
328
+
329
+ invoke 'd'
330
+ $stdout.string.should == Timetrap::Entry.all.map(&:id).join(" ") + "\n"
331
+ end
281
332
  end
282
- end
283
333
 
284
- describe "format" do
285
- before do
286
- create_entry(:start => '2008-10-03 12:00:00', :end => '2008-10-03 14:00:00')
287
- create_entry(:start => '2008-10-05 12:00:00', :end => '2008-10-05 14:00:00')
334
+ describe 'ids' do
335
+ before do
336
+ create_entry(:start => '2008-10-03 12:00:00', :end => '2008-10-03 14:00:00')
337
+ create_entry(:start => '2008-10-05 12:00:00', :end => '2008-10-05 14:00:00')
338
+ end
339
+
340
+ it "should not export running items" do
341
+ invoke 'in'
342
+ invoke 'display --format ids'
343
+ $stdout.string.should == Timetrap::Entry.all.map(&:id).join(" ") + "\n"
344
+ end
345
+
288
346
  end
347
+
289
348
  describe 'csv' do
290
- it "should be deprecated" do
291
- invoke 'format'
292
- $stderr.string.should == <<-WARN
293
- The "format" command is deprecated in favor of "display". Sorry for the inconvenience.
294
- WARN
349
+ before do
350
+ create_entry(:start => '2008-10-03 12:00:00', :end => '2008-10-03 14:00:00')
351
+ create_entry(:start => '2008-10-05 12:00:00', :end => '2008-10-05 14:00:00')
295
352
  end
296
353
 
297
354
  it "should not export running items" do
298
355
  invoke 'in'
299
- invoke 'format --format csv'
356
+ invoke 'display --format csv'
300
357
  $stdout.string.should == <<-EOF
301
358
  start,end,note,sheet
302
359
  "2008-10-03 12:00:00","2008-10-03 14:00:00","note","default"
@@ -305,7 +362,7 @@ start,end,note,sheet
305
362
  end
306
363
 
307
364
  it "should filter events by the passed dates" do
308
- invoke 'format --format csv --start 2008-10-03 --end 2008-10-03'
365
+ invoke 'display --format csv --start 2008-10-03 --end 2008-10-03'
309
366
  $stdout.string.should == <<-EOF
310
367
  start,end,note,sheet
311
368
  "2008-10-03 12:00:00","2008-10-03 14:00:00","note","default"
@@ -313,7 +370,7 @@ start,end,note,sheet
313
370
  end
314
371
 
315
372
  it "should not filter events by date when none are passed" do
316
- invoke 'format --format csv'
373
+ invoke 'display --format csv'
317
374
  $stdout.string.should == <<-EOF
318
375
  start,end,note,sheet
319
376
  "2008-10-03 12:00:00","2008-10-03 14:00:00","note","default"
@@ -322,26 +379,45 @@ start,end,note,sheet
322
379
  end
323
380
  end
324
381
 
382
+ describe 'json' do
383
+ 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')
386
+ end
387
+
388
+ it "should export to json not including running items" do
389
+ invoke 'in'
390
+ invoke 'display -f json'
391
+ JSON.parse($stdout.string).should == JSON.parse(<<-EOF)
392
+ [{\"sheet\":\"default\",\"end\":\"Fri Oct 03 14:00:00 -0700 2008\",\"start\":\"Fri Oct 03 12:00:00 -0700 2008\",\"note\":\"note\",\"id\":1},{\"sheet\":\"default\",\"end\":\"Sun Oct 05 14:00:00 -0700 2008\",\"start\":\"Sun Oct 05 12:00:00 -0700 2008\",\"note\":\"note\",\"id\":2}]
393
+ EOF
394
+ end
395
+ end
396
+
325
397
  describe 'ical' do
398
+ 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')
401
+ end
326
402
 
327
403
  it "should not export running items" do
328
404
  invoke 'in'
329
- invoke 'format --format ical'
405
+ invoke 'display --format ical'
330
406
  $stdout.string.scan(/BEGIN:VEVENT/).should have(2).item
331
407
  end
332
408
 
333
409
  it "should filter events by the passed dates" do
334
- invoke 'format --format ical --start 2008-10-03 --end 2008-10-03'
410
+ invoke 'display --format ical --start 2008-10-03 --end 2008-10-03'
335
411
  $stdout.string.scan(/BEGIN:VEVENT/).should have(1).item
336
412
  end
337
413
 
338
414
  it "should not filter events by date when none are passed" do
339
- invoke 'format --format ical'
415
+ invoke 'display --format ical'
340
416
  $stdout.string.scan(/BEGIN:VEVENT/).should have(2).item
341
417
  end
342
418
 
343
419
  it "should export a sheet to an ical format" do
344
- invoke 'format --format ical --start 2008-10-03 --end 2008-10-03'
420
+ invoke 'display --format ical --start 2008-10-03 --end 2008-10-03'
345
421
  desired = <<-EOF
346
422
  BEGIN:VCALENDAR
347
423
  VERSION:2.0
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.6.1"
8
+ s.version = "1.7.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-11-29}
12
+ s.date = %q{2010-12-08}
13
13
  s.default_executable = %q{t}
14
14
  s.description = %q{Command line time tracker}
15
15
  s.email = %q{sgrock@gmail.com}
@@ -30,8 +30,11 @@ Gem::Specification.new do |s|
30
30
  "lib/timetrap.rb",
31
31
  "lib/timetrap/cli.rb",
32
32
  "lib/timetrap/config.rb",
33
+ "lib/timetrap/formatters.rb",
33
34
  "lib/timetrap/formatters/csv.rb",
34
35
  "lib/timetrap/formatters/ical.rb",
36
+ "lib/timetrap/formatters/ids.rb",
37
+ "lib/timetrap/formatters/json.rb",
35
38
  "lib/timetrap/formatters/text.rb",
36
39
  "lib/timetrap/helpers.rb",
37
40
  "lib/timetrap/models.rb",
@@ -58,20 +61,17 @@ Gem::Specification.new do |s|
58
61
  s.add_runtime_dependency(%q<sqlite3-ruby>, [">= 1.2.5"])
59
62
  s.add_runtime_dependency(%q<chronic>, ["~> 0.3.0"])
60
63
  s.add_runtime_dependency(%q<getopt-declare>, [">= 1.28"])
61
- s.add_runtime_dependency(%q<icalendar>, [">= 1.1.2"])
62
64
  else
63
65
  s.add_dependency(%q<sequel>, [">= 3.9.0"])
64
66
  s.add_dependency(%q<sqlite3-ruby>, [">= 1.2.5"])
65
67
  s.add_dependency(%q<chronic>, ["~> 0.3.0"])
66
68
  s.add_dependency(%q<getopt-declare>, [">= 1.28"])
67
- s.add_dependency(%q<icalendar>, [">= 1.1.2"])
68
69
  end
69
70
  else
70
71
  s.add_dependency(%q<sequel>, [">= 3.9.0"])
71
72
  s.add_dependency(%q<sqlite3-ruby>, [">= 1.2.5"])
72
73
  s.add_dependency(%q<chronic>, ["~> 0.3.0"])
73
74
  s.add_dependency(%q<getopt-declare>, [">= 1.28"])
74
- s.add_dependency(%q<icalendar>, [">= 1.1.2"])
75
75
  end
76
76
  end
77
77
 
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: 13
4
+ hash: 11
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
- - 6
9
- - 1
10
- version: 1.6.1
8
+ - 7
9
+ - 0
10
+ version: 1.7.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-11-29 00:00:00 -08:00
18
+ date: 2010-12-08 00:00:00 -08:00
19
19
  default_executable: t
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -81,22 +81,6 @@ dependencies:
81
81
  version: "1.28"
82
82
  type: :runtime
83
83
  version_requirements: *id004
84
- - !ruby/object:Gem::Dependency
85
- name: icalendar
86
- prerelease: false
87
- requirement: &id005 !ruby/object:Gem::Requirement
88
- none: false
89
- requirements:
90
- - - ">="
91
- - !ruby/object:Gem::Version
92
- hash: 23
93
- segments:
94
- - 1
95
- - 1
96
- - 2
97
- version: 1.1.2
98
- type: :runtime
99
- version_requirements: *id005
100
84
  description: Command line time tracker
101
85
  email: sgrock@gmail.com
102
86
  executables:
@@ -118,8 +102,11 @@ files:
118
102
  - lib/timetrap.rb
119
103
  - lib/timetrap/cli.rb
120
104
  - lib/timetrap/config.rb
105
+ - lib/timetrap/formatters.rb
121
106
  - lib/timetrap/formatters/csv.rb
122
107
  - lib/timetrap/formatters/ical.rb
108
+ - lib/timetrap/formatters/ids.rb
109
+ - lib/timetrap/formatters/json.rb
123
110
  - lib/timetrap/formatters/text.rb
124
111
  - lib/timetrap/helpers.rb
125
112
  - lib/timetrap/models.rb