timetrap 1.6.1 → 1.7.0
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/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
|