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 +1 -0
- data/README.md +112 -11
- data/Rakefile +0 -1
- data/VERSION.yml +2 -2
- data/lib/timetrap.rb +6 -8
- data/lib/timetrap/cli.rb +9 -15
- data/lib/timetrap/config.rb +12 -2
- data/lib/timetrap/formatters.rb +3 -0
- data/lib/timetrap/formatters/ical.rb +10 -1
- data/lib/timetrap/formatters/ids.rb +11 -0
- data/lib/timetrap/formatters/json.rb +24 -0
- data/lib/timetrap/helpers.rb +29 -2
- data/spec/timetrap_spec.rb +155 -79
- data/timetrap.gemspec +5 -5
- metadata +8 -21
data/CONTRIBUTORS
CHANGED
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
143
|
-
|
144
|
-
|
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
|
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
|
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
|
-
|
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
|
-
|
336
|
+
**formatter_search_paths**: an array of directories to search for user defined fomatter classes
|
236
337
|
|
237
|
-
|
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
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
|
-
|
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.
|
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.
|
42
|
-
|
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
|
251
|
+
puts load_formatter(args['-f'] || Config['default_formatter']).new(entries).output
|
258
252
|
end
|
259
253
|
end
|
260
254
|
|
data/lib/timetrap/config.rb
CHANGED
@@ -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))
|
@@ -1,4 +1,13 @@
|
|
1
|
-
|
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,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
|
data/lib/timetrap/helpers.rb
CHANGED
@@ -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
|
|
data/spec/timetrap_spec.rb
CHANGED
@@ -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 "
|
169
|
+
describe "format" do
|
170
170
|
before do
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
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
|
-
|
188
|
-
|
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
|
-
|
212
|
+
OUTPUT
|
200
213
|
|
201
|
-
|
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
|
-
|
225
|
+
OUTPUT
|
213
226
|
|
214
|
-
|
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
|
-
|
235
|
-
|
247
|
+
OUTPUT
|
248
|
+
end
|
236
249
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
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
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
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
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
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
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
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
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
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
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
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
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
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
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
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
|
-
|
291
|
-
|
292
|
-
|
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 '
|
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 '
|
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 '
|
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 '
|
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 '
|
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 '
|
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 '
|
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.
|
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-
|
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:
|
4
|
+
hash: 11
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 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-
|
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
|