doing 1.0.53 → 1.0.58
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +27 -10
- data/bin/doing +700 -558
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +217 -60
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 27d29fb84f196b9219119d876aad52a8d4569b12324ece7617b74636d4245885
|
4
|
+
data.tar.gz: 40ed7d9d336b757d777dfa7014e754f115d732835a0a62c4e6a72ce9102c4a7f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: de8702acf70b5ea8382cf848b65b5164c2230e7f19b893032d4216fc225d596290146a8f461aae4d215a5021f1a0a4129b12750b119f324032f4e206819bfd3d
|
7
|
+
data.tar.gz: a52d1d2f07988441a14f5451206bfb9add306a9cf3b7bd0908b493c5076b5c720d8d26285bc5634f6e23db9ff81e670cd56c20e514bd5163ce82cab950a659dd
|
data/README.md
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
|
5
5
|
_If you're one of the rare people like me who find this useful, feel free to [buy me some coffee](http://brettterpstra.com/donate/)._
|
6
6
|
|
7
|
-
|
8
7
|
## Contents
|
9
8
|
|
10
9
|
- [What and why](#what-and-why)
|
@@ -16,7 +15,6 @@ _If you're one of the rare people like me who find this useful, feel free to [bu
|
|
16
15
|
- [Troubleshooting](#troubleshooting)
|
17
16
|
- [Changelog](#changelog)
|
18
17
|
|
19
|
-
<!-- end toc -->
|
20
18
|
<!--README-->
|
21
19
|
|
22
20
|
## What and why
|
@@ -31,11 +29,9 @@ _Side note:_ I actually use the library behind this utility as part of another s
|
|
31
29
|
|
32
30
|
## Installation
|
33
31
|
|
34
|
-
|
35
|
-
|
36
|
-
To install the _latest_ version, use `--pre`:
|
32
|
+
The current version of `doing` is <!--VER-->1.0.57<!--END VER-->.
|
37
33
|
|
38
|
-
$ [sudo] gem install
|
34
|
+
$ [sudo] gem install doing
|
39
35
|
|
40
36
|
Only use `sudo` if your environment requires it. If you're using the system Ruby on a Mac, for example, it will likely be necessary. If `gem install doing` fails, then run `sudo gem install doing` and provide your administrator password.
|
41
37
|
|
@@ -68,8 +64,10 @@ A basic configuration looks like this:
|
|
68
64
|
default_date_format: '%Y-%m-%d %H:%M'
|
69
65
|
marker_tag: flagged
|
70
66
|
marker_color: yellow
|
67
|
+
tags_color: boldcyan
|
71
68
|
default_tags: []
|
72
|
-
editor_app:
|
69
|
+
editor_app: TaskPaper
|
70
|
+
config_editor_app: Sublime Text
|
73
71
|
:include_notes: true
|
74
72
|
views:
|
75
73
|
color:
|
@@ -138,7 +136,9 @@ You can rename the section that holds your current tasks. By default, this is `C
|
|
138
136
|
|
139
137
|
### Default editors
|
140
138
|
|
141
|
-
The setting `editor_app` only applies to Mac OS X users. It's the default application that the command `doing open` will open your WWID file in. If this is blank, it will be opened by whatever the system default is, or you can use `-a app_name` or `-b bundle_id` to override.
|
139
|
+
The setting `editor_app` only applies to Mac OS X users. It's the default application that the command `doing open` will open your WWID file in. Personally, I use `editor_app: TaskPaper`. If this is blank, it will be opened by whatever the system default is for the doing file extension (default is `.md`), or you can use `-a app_name` or `-b bundle_id` to override.
|
140
|
+
|
141
|
+
You can define a separate app to open the config file in when running `doing config`. The key for this is `config_editor_app`.
|
142
142
|
|
143
143
|
In the case of the `doing now -e` command, your `$EDITOR` environment variable will be used to complete the entry text and notes. Set it in your `~/.bash_profile` or whatever is appropriate for your system:
|
144
144
|
|
@@ -495,6 +495,8 @@ This creates a search pattern looking for a string of word characters followed b
|
|
495
495
|
|
496
496
|
You can also add notes at the time of entry by using the `-n` or `--note` flag with `doing now`, `doing later`, or `doing done`. If you pass text to any of the creation commands which has multiple lines, everything after the first line break will become the note.
|
497
497
|
|
498
|
+
If a string passed to `now`, `later`, or `done` has a parenthetical at the end, the parenthetical will be removed from the title and its contents added as a note. So `doing now Working on @project1 (Adding some unit tests)` would create an entry titled "Working on @project1" with a note "Adding some unit tests." This is the equivalent of `doing now Working on @project1 -n "Adding some unit tests"`.
|
499
|
+
|
498
500
|
#### Displaying entries:
|
499
501
|
|
500
502
|
show - List all entries
|
@@ -520,6 +522,18 @@ If you have a use for it, you can use `-o csv` on the show or view commands to o
|
|
520
522
|
|
521
523
|
You can also show entries matching a search string with `doing grep` (synonym `doing search`). If you want to search with regular expressions or for an exact match, surround your search query with forward slashes, e.g. `doing search /project name/`. If you pass a search string without slashes, it's treated as a fuzzy search string, meaning matches can be found as long as the characters in the search string are in order and with no more than three other characters between each. By default searches are across all sections, but you can limit it to one with the `-s SECTION_NAME` flag. Searches can be displayed with the default template, or output as HTML, CSV, or JSON.
|
522
524
|
|
525
|
+
##### Modifying the last entry
|
526
|
+
|
527
|
+
If you want to make a change to the last entry added, use `doing last -e`. The `-e` flag opens the last entry (including note) in your editor, and when you close your editor, your doing file will be updated with any changes you made to the entry.
|
528
|
+
|
529
|
+
You can choose the last entry in a specific section by including the `-s` flag, so `doing last -s Later -e` would edit the most recent entry in the Later section.
|
530
|
+
|
531
|
+
You can also use text search or a tag filter to get an entry earlier than the most recent one. A tag search with `doing last --tag=project1 -e` will edit the last entry tagged `@project1`. Multiple tags can be combined with commas, and you can use `--bool` to specify whether the search is `AND` (matches all tags given), `OR` (matches any tag given), or `NOT` (matches none of the tags).
|
532
|
+
|
533
|
+
You can edit the last entry that matches a search string with `--search=QUERY`. `QUERY` can either be a raw string, or you can surround it with slashes to search by regex (`doing last --search="/project./" -e`). If the string is raw text, fuzzy matching will be used, so the characters must be in order but can be separated by up to three other characters.
|
534
|
+
|
535
|
+
Both `--tag` and `--search` can be constrained to a single section with `-s SECTION`.
|
536
|
+
|
523
537
|
#### Views
|
524
538
|
|
525
539
|
view - Display a user-created view
|
@@ -579,11 +593,14 @@ __Fish:__ See the file [`doing.fish`](https://github.com/ttscoff/doing/blob/mast
|
|
579
593
|
|
580
594
|
The LaunchBar action requires that `doing` be available in `/usr/local/bin/doing`. If it's not (because you're using RVM or similar), you'll need to symlink it there. Running the action with Return will show the latest 9 items from Currently, along with any time intervals recorded, and includes a submenu of Timers for each tag.
|
581
595
|
|
582
|
-
Pressing Spacebar and typing allows you to add a new entry to currently. You an also trigger a custom show command by typing "show [section/tag]" and hitting return.
|
596
|
+
Pressing Spacebar and typing allows you to add a new entry to currently. You an also trigger a custom show command by typing "show [section/tag]" and hitting return. Include any command line flags at the end of the string, and if you add text in parenthesis, it will be processed as a note on the entry.
|
583
597
|
|
584
598
|
Point of interest, the LaunchBar Action makes use of the `-o json` flag for outputting JSON to the action's script for parsing.
|
585
599
|
|
586
|
-
See <https://brettterpstra.com/projects/doing/> for the download
|
600
|
+
<!--GITHUB-->See <https://brettterpstra.com/projects/doing/> for the download.<!--END GITHUB-->
|
601
|
+
<!--JEKYLL
|
602
|
+
{% download 117 %}
|
603
|
+
-->
|
587
604
|
|
588
605
|
Evan Lovely has [created an Alfred workflow as well](http://www.evanlovely.com/blog/technology/alfred-for-terpstras-doing/).
|
589
606
|
|
data/bin/doing
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
$LOAD_PATH.unshift File.join(__dir__, '..', 'lib')
|
3
5
|
require 'gli'
|
4
6
|
require 'doing'
|
@@ -7,9 +9,9 @@ require 'pp'
|
|
7
9
|
|
8
10
|
def class_exists?(class_name)
|
9
11
|
klass = Module.const_get(class_name)
|
10
|
-
|
12
|
+
klass.is_a?(Class)
|
11
13
|
rescue NameError
|
12
|
-
|
14
|
+
false
|
13
15
|
end
|
14
16
|
|
15
17
|
if class_exists? 'Encoding'
|
@@ -17,19 +19,17 @@ if class_exists? 'Encoding'
|
|
17
19
|
Encoding.default_internal = Encoding::UTF_8 if Encoding.respond_to?('default_internal')
|
18
20
|
end
|
19
21
|
|
20
|
-
|
21
22
|
include GLI::App
|
22
23
|
version Doing::VERSION
|
23
24
|
|
24
25
|
wwid = WWID.new
|
25
|
-
if Dir.respond_to?('home')
|
26
|
-
|
27
|
-
else
|
28
|
-
|
29
|
-
end
|
26
|
+
wwid.user_home = if Dir.respond_to?('home')
|
27
|
+
Dir.home
|
28
|
+
else
|
29
|
+
File.expand_path('~')
|
30
|
+
end
|
30
31
|
wwid.configure
|
31
32
|
|
32
|
-
|
33
33
|
program_desc 'A CLI for a What Was I Doing system'
|
34
34
|
|
35
35
|
default_command :recent
|
@@ -42,330 +42,329 @@ desc 'Send results report to STDOUT instead of STDERR'
|
|
42
42
|
switch [:stdout], default_value: false, negatable: false
|
43
43
|
|
44
44
|
desc 'Exclude auto tags and default tags'
|
45
|
-
switch [
|
45
|
+
switch %i[x noauto], default_value: false
|
46
46
|
|
47
47
|
desc 'Use a specific configuration file'
|
48
|
-
flag [:config_file]
|
49
|
-
|
50
|
-
|
51
|
-
# desc 'Wrap notes at X chars (0 for no wrap)'
|
52
|
-
# flag [:w, :wrapwidth], must_match: /^\d+$/, type: Integer
|
48
|
+
flag [:config_file], default_value: wwid.config_file
|
53
49
|
|
54
50
|
desc 'Specify a different doing_file'
|
55
|
-
flag [
|
51
|
+
flag %i[f doing_file]
|
56
52
|
|
57
53
|
desc 'Add an entry'
|
58
|
-
arg_name '
|
54
|
+
arg_name 'ENTRY'
|
59
55
|
command [:now, :next] do |c|
|
60
56
|
c.desc 'Section'
|
61
|
-
c.arg_name '
|
62
|
-
c.flag [
|
57
|
+
c.arg_name 'NAME'
|
58
|
+
c.flag %i[s section], default_value: wwid.current_section
|
63
59
|
|
64
60
|
c.desc "Edit entry with #{ENV['EDITOR']}"
|
65
|
-
c.switch [
|
61
|
+
c.switch %i[e editor], negatable: false, default_value: false
|
66
62
|
|
67
63
|
c.desc 'Backdate start time [4pm|20m|2h|yesterday noon]'
|
68
|
-
c.
|
64
|
+
c.arg_name 'DATE_STRING'
|
65
|
+
c.flag %i[b back]
|
69
66
|
|
70
67
|
c.desc 'Timed entry, marks last entry in section as @done'
|
71
|
-
c.switch [
|
68
|
+
c.switch %i[f finish_last], negatable: false, default_value: false
|
72
69
|
|
73
70
|
c.desc 'Note'
|
74
|
-
c.arg_name '
|
75
|
-
c.flag [
|
71
|
+
c.arg_name 'TEXT'
|
72
|
+
c.flag %i[n note]
|
76
73
|
|
77
74
|
# c.desc "Edit entry with specified app"
|
78
75
|
# c.arg_name 'editor_app'
|
79
76
|
# # c.flag [:a, :app]
|
80
77
|
|
81
|
-
c.action do |
|
78
|
+
c.action do |_global_options, options, args|
|
82
79
|
if options[:back]
|
83
80
|
date = wwid.chronify(options[:back])
|
84
81
|
|
85
|
-
raise
|
82
|
+
raise 'Unable to parse date string' if date.nil?
|
86
83
|
else
|
87
84
|
date = Time.now
|
88
85
|
end
|
89
86
|
|
90
87
|
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
91
88
|
|
92
|
-
if options[:e] || (args.
|
93
|
-
raise
|
94
|
-
|
95
|
-
input
|
96
|
-
input
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
89
|
+
if options[:e] || (args.empty? && $stdin.stat.size.zero?)
|
90
|
+
raise 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
91
|
+
|
92
|
+
input = ''
|
93
|
+
input += args.join(' ') unless args.empty?
|
94
|
+
input = wwid.fork_editor(input).strip
|
95
|
+
|
96
|
+
raise 'No content' if input.empty?
|
97
|
+
|
98
|
+
title, note = wwid.format_input(input)
|
99
|
+
note.push(options[:n]) if options[:n]
|
100
|
+
wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:f] })
|
101
|
+
wwid.write(wwid.doing_file)
|
102
|
+
elsif args.length.positive?
|
103
|
+
title, note = wwid.format_input(args.join(' '))
|
104
|
+
note.push(options[:n]) if options[:n]
|
105
|
+
wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:f] })
|
106
|
+
wwid.write(wwid.doing_file)
|
107
|
+
elsif $stdin.stat.size.positive?
|
108
|
+
title, note = wwid.format_input($stdin.read)
|
109
|
+
note.push(options[:n]) if options[:n]
|
110
|
+
wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:f] })
|
111
|
+
wwid.write(wwid.doing_file)
|
105
112
|
else
|
106
|
-
|
107
|
-
title, note = wwid.format_input(args.join(" "))
|
108
|
-
note.push(options[:n]) if options[:n]
|
109
|
-
wwid.add_item(title.cap_first, section, {note: note, back: date, timed: options[:f]})
|
110
|
-
wwid.write(wwid.doing_file)
|
111
|
-
elsif STDIN.stat.size > 0
|
112
|
-
title, note = wwid.format_input(STDIN.read)
|
113
|
-
note.push(options[:n]) if options[:n]
|
114
|
-
wwid.add_item(title.cap_first, section, {note: note, back: date, timed: options[:f]})
|
115
|
-
wwid.write(wwid.doing_file)
|
116
|
-
else
|
117
|
-
raise "You must provide content when creating a new entry"
|
118
|
-
end
|
113
|
+
raise 'You must provide content when creating a new entry'
|
119
114
|
end
|
120
115
|
end
|
121
116
|
end
|
122
117
|
|
123
118
|
desc 'Add a note to the last entry'
|
124
|
-
long_desc %
|
119
|
+
long_desc %(
|
125
120
|
If -r is provided with no other arguments, the last note is removed. If new content is specified through arguments or STDIN, any previous note will be replaced with the new one.
|
126
121
|
|
127
122
|
Use -e to load the last entry in a text editor where you can append a note.
|
128
|
-
|
129
|
-
arg_name '
|
123
|
+
)
|
124
|
+
arg_name 'NOTE_TEXT'
|
130
125
|
command :note do |c|
|
131
126
|
c.desc 'Section'
|
132
|
-
c.arg_name '
|
133
|
-
c.flag [
|
127
|
+
c.arg_name 'NAME'
|
128
|
+
c.flag %i[s section], default_value: 'All'
|
134
129
|
|
135
130
|
c.desc "Edit entry with #{ENV['EDITOR']}"
|
136
|
-
c.switch [
|
131
|
+
c.switch %i[e editor], negatable: false, default_value: false
|
137
132
|
|
138
133
|
c.desc "Replace/Remove last entry's note (default append)"
|
139
|
-
c.switch [
|
134
|
+
c.switch %i[r remove], negatable: false, default_value: false
|
140
135
|
|
141
|
-
c.action do |
|
136
|
+
c.action do |_global_options, options, args|
|
142
137
|
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
143
138
|
|
144
|
-
if options[:e] || (args.
|
145
|
-
raise
|
139
|
+
if options[:e] || (args.empty? && $stdin.stat.size.zero? && !options[:r])
|
140
|
+
raise 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
146
141
|
|
147
|
-
input = args.
|
142
|
+
input = !args.empty? ? args.join(' ') : ''
|
148
143
|
|
149
|
-
prev_input = wwid.last_note(section) ||
|
150
|
-
|
151
|
-
prev_input = prev_input.join("\n")
|
152
|
-
end
|
144
|
+
prev_input = wwid.last_note(section) || ''
|
145
|
+
prev_input = prev_input.join("\n") if prev_input.instance_of?(Array)
|
153
146
|
input = prev_input + input
|
154
147
|
|
155
|
-
input = wwid.fork_editor(input)
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
148
|
+
input = wwid.fork_editor(input).strip
|
149
|
+
raise 'No content, cancelled' unless input
|
150
|
+
|
151
|
+
_title, note = wwid.format_input(input)
|
152
|
+
|
153
|
+
raise 'No note content' unless note
|
154
|
+
|
155
|
+
wwid.note_last(section, note, replace: true)
|
156
|
+
elsif !args.empty?
|
157
|
+
title, note = wwid.format_input(args.join(' '))
|
158
|
+
note.insert(0, title)
|
159
|
+
wwid.note_last(section, note, replace: options[:r])
|
160
|
+
elsif $stdin.stat.size.positive?
|
161
|
+
title, note = wwid.format_input($stdin.read)
|
162
|
+
note.insert(0, title)
|
163
|
+
wwid.note_last(section, note, replace: options[:r])
|
164
|
+
elsif options[:r]
|
165
|
+
wwid.note_last(section, [], replace: true)
|
166
166
|
else
|
167
|
-
|
168
|
-
title, note = wwid.format_input(args.join(" "))
|
169
|
-
note.insert(0, title)
|
170
|
-
wwid.note_last(section, note, replace: options[:r])
|
171
|
-
elsif STDIN.stat.size > 0
|
172
|
-
title, note = wwid.format_input(STDIN.read)
|
173
|
-
note.insert(0, title)
|
174
|
-
wwid.note_last(section, note, replace: options[:r])
|
175
|
-
else
|
176
|
-
if options[:r]
|
177
|
-
wwid.note_last(section, [], replace: true)
|
178
|
-
else
|
179
|
-
raise "You must provide content when adding a note"
|
180
|
-
end
|
181
|
-
end
|
167
|
+
raise 'You must provide content when adding a note'
|
182
168
|
end
|
183
169
|
wwid.write(wwid.doing_file)
|
184
170
|
end
|
185
171
|
end
|
186
172
|
|
187
173
|
desc 'Finish any running @meanwhile tasks and optionally create a new one'
|
188
|
-
arg_name '
|
174
|
+
arg_name 'ENTRY'
|
189
175
|
command :meanwhile do |c|
|
190
176
|
c.desc 'Section'
|
191
|
-
c.arg_name '
|
192
|
-
c.flag [
|
177
|
+
c.arg_name 'NAME'
|
178
|
+
c.flag %i[s section], default_value: wwid.current_section
|
193
179
|
|
194
180
|
c.desc "Edit entry with #{ENV['EDITOR']}"
|
195
|
-
c.switch [
|
181
|
+
c.switch %i[e editor], negatable: false, default_value: false
|
196
182
|
|
197
|
-
c.desc
|
198
|
-
c.switch [
|
183
|
+
c.desc 'Archive previous @meanwhile entry'
|
184
|
+
c.switch %i[a archive], default_value: false
|
199
185
|
|
200
186
|
c.desc 'Backdate start date for new entry to date string [4pm|20m|2h|yesterday noon]'
|
201
|
-
c.
|
187
|
+
c.arg_name 'DATE_STRING'
|
188
|
+
c.flag %i[b back]
|
202
189
|
|
203
190
|
c.desc 'Note'
|
204
|
-
c.arg_name '
|
205
|
-
c.flag [
|
191
|
+
c.arg_name 'TEXT'
|
192
|
+
c.flag %i[n note]
|
206
193
|
|
207
|
-
c.action do |
|
194
|
+
c.action do |_global_options, options, args|
|
208
195
|
if options[:back]
|
209
196
|
date = wwid.chronify(options[:back])
|
210
197
|
|
211
|
-
raise
|
198
|
+
raise 'Unable to parse date string' if date.nil?
|
212
199
|
else
|
213
200
|
date = Time.now
|
214
201
|
end
|
215
202
|
|
216
203
|
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
217
|
-
input =
|
204
|
+
input = ''
|
218
205
|
|
219
206
|
if options[:e]
|
220
|
-
raise
|
221
|
-
|
222
|
-
input
|
207
|
+
raise 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
208
|
+
|
209
|
+
input += args.join(' ') unless args.empty?
|
210
|
+
input = wwid.fork_editor(input).strip
|
211
|
+
elsif !args.empty?
|
212
|
+
input = args.join(' ')
|
213
|
+
elsif $stdin.stat.size.positive?
|
214
|
+
input = $stdin.read
|
215
|
+
end
|
216
|
+
|
217
|
+
if input && !input.empty?
|
218
|
+
input, note = wwid.format_input(input)
|
223
219
|
else
|
224
|
-
|
225
|
-
|
226
|
-
elsif STDIN.stat.size > 0
|
227
|
-
input = STDIN.read
|
228
|
-
end
|
220
|
+
input = nil
|
221
|
+
note = []
|
229
222
|
end
|
230
|
-
input = false unless input && input.length > 0
|
231
223
|
|
232
|
-
|
233
|
-
|
224
|
+
if options[:n]
|
225
|
+
note.push(options[:n])
|
226
|
+
elsif note.empty?
|
227
|
+
note = nil
|
228
|
+
end
|
229
|
+
|
230
|
+
wwid.stop_start('meanwhile', { new_item: input, back: date, section: section, archive: options[:a], note: note })
|
234
231
|
wwid.write(wwid.doing_file)
|
235
232
|
end
|
236
233
|
end
|
237
234
|
|
238
|
-
desc 'Output HTML templates for customization'
|
239
|
-
long_desc %
|
240
|
-
Templates are printed to STDOUT for piping to a file.
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
c.action do |
|
248
|
-
|
249
|
-
|
235
|
+
desc 'Output HTML and CSS templates for customization'
|
236
|
+
long_desc %(
|
237
|
+
Templates are printed to STDOUT for piping to a file.
|
238
|
+
Save them and use them in the configuration file under html_template.
|
239
|
+
|
240
|
+
Example `doing template HAML > ~/styles/my_doing.haml`
|
241
|
+
)
|
242
|
+
arg_name 'TYPE', must_match: /^(html|haml|css)/i
|
243
|
+
command :template do |c|
|
244
|
+
c.action do |_global_options, options, args|
|
245
|
+
raise 'No type specified, use `doing template [HAML|CSS]`' if args.empty?
|
246
|
+
|
247
|
+
case options[:t]
|
248
|
+
when /html|haml/i
|
249
|
+
$stdout.puts wwid.haml_template
|
250
|
+
when /css/i
|
251
|
+
$stdout.puts wwid.css_template
|
250
252
|
else
|
251
|
-
|
252
|
-
$stdout.puts wwid.haml_template
|
253
|
-
elsif options[:t] =~ /css/i
|
254
|
-
$stdout.puts wwid.css_template
|
255
|
-
else
|
256
|
-
raise "Invalid type specified, use --type=[HAML|CSS]"
|
257
|
-
end
|
253
|
+
raise 'Invalid type specified, must be HAML or CSS'
|
258
254
|
end
|
259
255
|
end
|
260
256
|
end
|
261
257
|
|
262
|
-
|
263
258
|
desc 'Add an item to the Later section'
|
264
|
-
arg_name '
|
259
|
+
arg_name 'ENTRY'
|
265
260
|
command :later do |c|
|
266
261
|
c.desc "Edit entry with #{ENV['EDITOR']}"
|
267
|
-
c.switch [
|
262
|
+
c.switch %i[e editor], negatable: false, default_value: false
|
268
263
|
|
269
|
-
c.desc
|
270
|
-
c.arg_name '
|
271
|
-
c.flag [
|
264
|
+
c.desc 'Edit entry with specified app'
|
265
|
+
c.arg_name 'APP'
|
266
|
+
c.flag %i[a app]
|
272
267
|
|
273
268
|
c.desc 'Backdate start time to date string [4pm|20m|2h|yesterday noon]'
|
274
|
-
c.
|
269
|
+
c.arg_name 'DATE_STRING'
|
270
|
+
c.flag %i[b back]
|
275
271
|
|
276
272
|
c.desc 'Note'
|
277
|
-
c.arg_name '
|
278
|
-
c.flag [
|
273
|
+
c.arg_name 'TEXT'
|
274
|
+
c.flag %i[n note]
|
279
275
|
|
280
|
-
c.action do |
|
276
|
+
c.action do |_global_options, options, args|
|
281
277
|
if options[:back]
|
282
278
|
date = wwid.chronify(options[:back])
|
283
|
-
raise
|
279
|
+
raise 'Unable to parse date string' if date.nil?
|
284
280
|
else
|
285
281
|
date = Time.now
|
286
282
|
end
|
287
283
|
|
288
|
-
if options[:e] || (args.
|
289
|
-
raise
|
290
|
-
|
291
|
-
input
|
292
|
-
input = wwid.fork_editor(input)
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
284
|
+
if options[:e] || (args.empty? && $stdin.stat.size.zero?)
|
285
|
+
raise 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
286
|
+
|
287
|
+
input = args.empty? ? '' : args.join(' ')
|
288
|
+
input = wwid.fork_editor(input).strip
|
289
|
+
raise 'No content' unless input && !input.empty?
|
290
|
+
|
291
|
+
title, note = wwid.format_input(input)
|
292
|
+
note.push(options[:n]) if options[:n]
|
293
|
+
wwid.add_item(title.cap_first, 'Later', { note: note, back: date })
|
294
|
+
wwid.write(wwid.doing_file)
|
295
|
+
elsif !args.empty?
|
296
|
+
title, note = wwid.format_input(args.join(' '))
|
297
|
+
note.push(options[:n]) if options[:n]
|
298
|
+
wwid.add_item(title.cap_first, 'Later', { note: note, back: date })
|
299
|
+
wwid.write(wwid.doing_file)
|
300
|
+
elsif $stdin.stat.size.positive?
|
301
|
+
title, note = wwid.format_input($stdin.read)
|
302
|
+
note.push(options[:n]) if options[:n]
|
303
|
+
wwid.add_item(title.cap_first, 'Later', { note: note, back: date })
|
304
|
+
wwid.write(wwid.doing_file)
|
301
305
|
else
|
302
|
-
|
303
|
-
title, note = wwid.format_input(args.join(" "))
|
304
|
-
note.push(options[:n]) if options[:n]
|
305
|
-
wwid.add_item(title.cap_first, "Later", {note: note, back: date})
|
306
|
-
wwid.write(wwid.doing_file)
|
307
|
-
elsif STDIN.stat.size > 0
|
308
|
-
title, note = wwid.format_input(STDIN.read)
|
309
|
-
note.push(options[:n]) if options[:n]
|
310
|
-
wwid.add_item(title.cap_first, "Later", {note: note, back: date})
|
311
|
-
wwid.write(wwid.doing_file)
|
312
|
-
else
|
313
|
-
raise "You must provide content when creating a new entry"
|
314
|
-
end
|
306
|
+
raise 'You must provide content when creating a new entry'
|
315
307
|
end
|
316
308
|
end
|
317
309
|
end
|
318
310
|
|
319
311
|
desc 'Add a completed item with @done(date). No argument finishes last entry.'
|
320
|
-
arg_name '
|
312
|
+
arg_name 'ENTRY'
|
321
313
|
command [:done, :did] do |c|
|
322
314
|
c.desc 'Remove @done tag'
|
323
|
-
c.switch [
|
315
|
+
c.switch %i[r remove], negatable: false, default_value: false
|
324
316
|
|
325
317
|
c.desc 'Include date'
|
326
318
|
c.switch [:date], negatable: true, default_value: true
|
327
319
|
|
328
320
|
c.desc 'Immediately archive the entry'
|
329
|
-
c.switch [
|
321
|
+
c.switch %i[a archive], negatable: false, default_value: false
|
330
322
|
|
331
|
-
c.desc
|
323
|
+
c.desc %(Set finish date to specific date/time (natural langauge parsed, e.g. --at=1:30pm).
|
324
|
+
If used, ignores --back. Used with --took, backdates start date)
|
325
|
+
c.arg_name 'DATE_STRING'
|
332
326
|
c.flag [:at]
|
333
327
|
|
334
328
|
c.desc 'Backdate start date by interval [4pm|20m|2h|yesterday noon]'
|
335
|
-
c.
|
329
|
+
c.arg_name 'DATE_STRING'
|
330
|
+
c.flag %i[b back]
|
336
331
|
|
337
|
-
c.desc
|
338
|
-
|
332
|
+
c.desc %(Set completion date to start date plus interval (XX[mhd] or HH:MM).
|
333
|
+
If used without the --back option, the start date will be moved back to allow
|
334
|
+
the completion date to be the current time.)
|
335
|
+
c.arg_name 'INTERVAL'
|
336
|
+
c.flag %i[t took]
|
339
337
|
|
340
338
|
c.desc 'Section'
|
341
|
-
c.
|
339
|
+
c.arg_name 'NAME'
|
340
|
+
c.flag %i[s section], default_value: wwid.current_section
|
342
341
|
|
343
342
|
c.desc "Edit entry with #{ENV['EDITOR']}"
|
344
|
-
c.switch [
|
343
|
+
c.switch %i[e editor], negatable: false, default_value: false
|
345
344
|
|
346
345
|
# c.desc "Edit entry with specified app"
|
347
346
|
# c.arg_name 'editor_app'
|
348
347
|
# # c.flag [:a, :app]
|
349
348
|
|
350
|
-
c.action do |
|
349
|
+
c.action do |_global_options, options, args|
|
351
350
|
took = 0
|
352
351
|
|
353
|
-
|
354
352
|
if options[:took]
|
355
353
|
took = wwid.chronify_qty(options[:took])
|
356
|
-
raise
|
354
|
+
raise 'Unable to parse date string for --took' if took.nil?
|
357
355
|
end
|
358
356
|
|
359
357
|
if options[:back]
|
360
358
|
date = wwid.chronify(options[:back])
|
361
|
-
raise
|
359
|
+
raise 'Unable to parse date string for --back' if date.nil?
|
362
360
|
else
|
363
361
|
date = options[:took] ? Time.now - took : Time.now
|
364
362
|
end
|
365
363
|
|
366
364
|
if options[:at]
|
367
365
|
finish_date = wwid.chronify(options[:at])
|
368
|
-
raise
|
366
|
+
raise 'Unable to parse date string for --at' if finish_date.nil?
|
367
|
+
|
369
368
|
date = options[:took] ? finish_date - took : finish_date
|
370
369
|
elsif options[:took]
|
371
370
|
finish_date = date + took
|
@@ -376,127 +375,147 @@ command [:done, :did] do |c|
|
|
376
375
|
end
|
377
376
|
|
378
377
|
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
379
|
-
donedate = options[:date] ? "(#{finish_date.strftime('%F %R')})" :
|
378
|
+
donedate = options[:date] ? "(#{finish_date.strftime('%F %R')})" : ''
|
380
379
|
|
381
380
|
if options[:e]
|
382
|
-
raise
|
383
|
-
|
384
|
-
input
|
385
|
-
input
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
elsif args.length == 0 && STDIN.stat.size == 0
|
381
|
+
raise 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
382
|
+
|
383
|
+
input = ''
|
384
|
+
input += args.join(' ') unless args.empty?
|
385
|
+
input = wwid.fork_editor(input).strip
|
386
|
+
raise 'No content' unless input && !input.empty?
|
387
|
+
|
388
|
+
title, note = wwid.format_input(input)
|
389
|
+
title += " @done#{donedate}"
|
390
|
+
section = 'Archive' if options[:a]
|
391
|
+
wwid.add_item(title.cap_first, section.cap_first, { note: note, back: date })
|
392
|
+
wwid.write(wwid.doing_file)
|
393
|
+
elsif args.empty? && $stdin.stat.size.zero?
|
396
394
|
if options[:r]
|
397
|
-
wwid.tag_last({tags: [
|
395
|
+
wwid.tag_last({ tags: ['done'], count: 1, section: section, remove: true })
|
398
396
|
else
|
399
|
-
wwid.tag_last({tags: [
|
397
|
+
wwid.tag_last({ tags: ['done'], count: 1, section: section, archive: options[:a], back: finish_date,
|
398
|
+
date: options[:date] })
|
400
399
|
end
|
400
|
+
elsif !args.empty?
|
401
|
+
title, note = wwid.format_input(args.join(' '))
|
402
|
+
title.chomp!
|
403
|
+
title += " @done#{donedate}"
|
404
|
+
section = 'Archive' if options[:a]
|
405
|
+
wwid.add_item(title.cap_first, section.cap_first, { note: note, back: date })
|
406
|
+
wwid.write(wwid.doing_file)
|
407
|
+
elsif $stdin.stat.size.positive?
|
408
|
+
title, note = wwid.format_input($stdin.read)
|
409
|
+
title += " @done#{donedate}"
|
410
|
+
section = options[:a] ? 'Archive' : section
|
411
|
+
wwid.add_item(title.cap_first, section.cap_first, { note: note, back: date })
|
412
|
+
wwid.write(wwid.doing_file)
|
401
413
|
else
|
402
|
-
|
403
|
-
title, note = wwid.format_input(args.join(" "))
|
404
|
-
title.chomp!
|
405
|
-
title += " @done#{donedate}"
|
406
|
-
section = "Archive" if options[:a]
|
407
|
-
wwid.add_item(title.cap_first, section.cap_first, {note: note, back: date})
|
408
|
-
wwid.write(wwid.doing_file)
|
409
|
-
elsif STDIN.stat.size > 0
|
410
|
-
title, note = wwid.format_input(STDIN.read)
|
411
|
-
title += " @done#{donedate}"
|
412
|
-
section = options[:a] ? "Archive" : section
|
413
|
-
wwid.add_item(title.cap_first, section.cap_first, {note: note, back: date})
|
414
|
-
wwid.write(wwid.doing_file)
|
415
|
-
else
|
416
|
-
raise "You must provide content when creating a new entry"
|
417
|
-
end
|
414
|
+
raise 'You must provide content when creating a new entry'
|
418
415
|
end
|
419
416
|
end
|
420
417
|
end
|
421
418
|
|
422
419
|
desc 'End last X entries with no time tracked'
|
423
420
|
long_desc 'Adds @done tag without datestamp so no elapsed time is recorded. Alias for `doing finish --no-date`.'
|
424
|
-
arg_name '
|
421
|
+
arg_name 'COUNT'
|
425
422
|
command :cancel do |c|
|
426
423
|
c.desc 'Archive entries'
|
427
|
-
c.switch [
|
424
|
+
c.switch %i[a archive], negatable: false, default_value: false
|
428
425
|
|
429
426
|
c.desc 'Section'
|
430
|
-
c.
|
427
|
+
c.arg_name 'NAME'
|
428
|
+
c.flag %i[s section], default_value: wwid.current_section
|
431
429
|
|
432
|
-
c.desc 'Cancel the last X entries containing TAG. Separate multiple tags with comma (--tag=
|
430
|
+
c.desc 'Cancel the last X entries containing TAG. Separate multiple tags with comma (--tag=tag1,tag2)'
|
431
|
+
c.arg_name 'TAG'
|
433
432
|
c.flag [:tag]
|
434
433
|
|
435
434
|
c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters'
|
435
|
+
c.arg_name 'BOOLEAN'
|
436
436
|
c.flag [:bool], must_match: /^(and|or|not)$/i, default_value: 'AND'
|
437
437
|
|
438
|
-
|
439
|
-
c.action do |global_options,options,args|
|
440
|
-
|
438
|
+
c.action do |_global_options, options, args|
|
441
439
|
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
442
440
|
|
443
441
|
if options[:tag].nil?
|
444
442
|
tags = []
|
445
443
|
else
|
446
|
-
tags = options[:tag].split(/ *, */).map {|t| t.strip.sub(/^@/, '') }
|
444
|
+
tags = options[:tag].split(/ *, */).map { |t| t.strip.sub(/^@/, '') }
|
447
445
|
options[:bool] = options[:bool] =~ /^(and|or|not)$/i ? options[:bool].upcase : 'AND'
|
448
446
|
end
|
449
447
|
|
450
|
-
if args.length > 1
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
448
|
+
raise 'Only one argument allowed' if args.length > 1
|
449
|
+
|
450
|
+
raise 'Invalid argument (specify number of recent items to mark @done)' unless args.empty? || args[0] =~ /\d+/
|
451
|
+
|
452
|
+
count = args[0] ? args[0].to_i : 1
|
453
|
+
opts = {
|
454
|
+
archive: options[:a],
|
455
|
+
count: count,
|
456
|
+
date: false,
|
457
|
+
section: section,
|
458
|
+
sequential: false,
|
459
|
+
tag: tags,
|
460
|
+
tag_bool: options[:bool],
|
461
|
+
tags: ['done']
|
462
|
+
}
|
463
|
+
wwid.tag_last(opts)
|
458
464
|
end
|
459
465
|
end
|
460
466
|
|
461
467
|
desc 'Mark last X entries as @done'
|
462
468
|
long_desc 'Marks the last X entries with a @done tag and current date. Does not alter already completed entries.'
|
463
|
-
arg_name '
|
469
|
+
arg_name 'COUNT'
|
464
470
|
command :finish do |c|
|
465
471
|
c.desc 'Include date'
|
466
472
|
c.switch [:date], negatable: true, default_value: true
|
467
473
|
|
468
474
|
c.desc 'Backdate completed date to date string [4pm|20m|2h|yesterday noon]'
|
469
|
-
c.
|
475
|
+
c.arg_name 'DATE_STRING'
|
476
|
+
c.flag %i[b back]
|
470
477
|
|
471
478
|
c.desc 'Set the completed date to the start date plus XX[hmd]'
|
472
|
-
c.
|
479
|
+
c.arg_name 'INTERVAL'
|
480
|
+
c.flag %i[t took]
|
473
481
|
|
474
|
-
c.desc 'Finish the last X entries containing TAG.
|
482
|
+
c.desc 'Finish the last X entries containing TAG.
|
483
|
+
Separate multiple tags with comma (--tag=tag1,tag2), combine with --bool'
|
484
|
+
c.arg_name 'TAG'
|
475
485
|
c.flag [:tag]
|
476
486
|
|
487
|
+
c.desc 'Finish the last X entries matching search filter, surround with slashes for regex (e.g. "/query.*/")'
|
488
|
+
c.arg_name 'QUERY'
|
489
|
+
c.flag [:search]
|
490
|
+
|
477
491
|
c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters'
|
492
|
+
c.arg_name 'BOOLEAN'
|
478
493
|
c.flag [:bool], must_match: /^(and|or|not)$/i, default_value: 'AND'
|
479
494
|
|
480
|
-
c.desc
|
495
|
+
c.desc %(Auto-generate finish dates from next entry's start time.
|
496
|
+
Automatically generate completion dates 1 minute before next start date.
|
497
|
+
--auto overrides the --date and --back parameters.)
|
481
498
|
c.switch [:auto], negatable: false, default_value: false
|
482
499
|
|
483
500
|
c.desc 'Archive entries'
|
484
|
-
c.switch [
|
501
|
+
c.switch %i[a archive], negatable: false, default_value: false
|
485
502
|
|
486
503
|
c.desc 'Section'
|
487
|
-
c.
|
488
|
-
|
489
|
-
c.action do |global_options,options,args|
|
504
|
+
c.arg_name 'NAME'
|
505
|
+
c.flag %i[s section], default_value: wwid.current_section
|
490
506
|
|
507
|
+
c.action do |_global_options, options, args|
|
491
508
|
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
492
509
|
|
493
510
|
unless options[:auto]
|
494
|
-
raise
|
511
|
+
raise '--back and --took cannot be used together' if options[:back] && options[:took]
|
512
|
+
|
513
|
+
raise '--search and --tag cannot be used together' if options[:search] && options[:tag]
|
495
514
|
|
496
515
|
if options[:back]
|
497
516
|
date = wwid.chronify(options[:back])
|
498
517
|
|
499
|
-
raise
|
518
|
+
raise 'Unable to parse date string' if date.nil?
|
500
519
|
elsif options[:took]
|
501
520
|
date = wwid.chronify_qty(options[:took])
|
502
521
|
else
|
@@ -507,201 +526,241 @@ command :finish do |c|
|
|
507
526
|
if options[:tag].nil?
|
508
527
|
tags = []
|
509
528
|
else
|
510
|
-
tags = options[:tag].split(/ *, */).map {|t| t.strip.sub(/^@/, '') }
|
529
|
+
tags = options[:tag].split(/ *, */).map { |t| t.strip.sub(/^@/, '') }
|
511
530
|
options[:bool] = options[:bool] =~ /^(and|or|not)$/i ? options[:bool].upcase : 'AND'
|
512
531
|
end
|
513
532
|
|
514
|
-
if args.length > 1
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
533
|
+
raise 'Only one argument allowed' if args.length > 1
|
534
|
+
|
535
|
+
raise 'Invalid argument (specify number of recent items to mark @done)' unless args.length == 0 || args[0] =~ /\d+/
|
536
|
+
|
537
|
+
count = args[0] ? args[0].to_i : 1
|
538
|
+
opts = {
|
539
|
+
archive: options[:a],
|
540
|
+
back: date,
|
541
|
+
count: count,
|
542
|
+
date: options[:date],
|
543
|
+
search: options[:search],
|
544
|
+
section: section,
|
545
|
+
sequential: options[:auto],
|
546
|
+
tag: tags,
|
547
|
+
tag_bool: options[:bool],
|
548
|
+
tags: ['done']
|
549
|
+
}
|
550
|
+
wwid.tag_last(opts)
|
522
551
|
end
|
523
552
|
end
|
524
553
|
|
525
554
|
desc 'Repeat last entry as new entry'
|
526
|
-
arg_name 'section'
|
527
555
|
command [:again, :resume] do |c|
|
528
556
|
c.desc 'Section'
|
529
|
-
c.
|
557
|
+
c.arg_name 'NAME'
|
558
|
+
c.flag %i[s section], default_value: 'All'
|
530
559
|
|
531
560
|
c.desc 'Add new entry to section (default: same section as repeated entry)'
|
561
|
+
c.arg_name 'SECTION_NAME'
|
532
562
|
c.flag [:in]
|
533
563
|
|
534
564
|
c.desc 'Repeat last entry matching tags. Combine multiple tags with a comma.'
|
565
|
+
c.arg_name 'TAG'
|
535
566
|
c.flag [:tag]
|
536
567
|
|
568
|
+
c.desc 'Repeat last entry matching search. Surround with
|
569
|
+
slashes for regex (e.g. "/query/").'
|
570
|
+
c.arg_name 'QUERY'
|
571
|
+
c.flag [:search]
|
572
|
+
|
537
573
|
c.desc 'Boolean used to combine multiple tags'
|
574
|
+
c.arg_name 'BOOLEAN'
|
538
575
|
c.flag [:bool], must_match: /^(and|or|not)$/i, default_value: 'ALL'
|
539
576
|
|
540
577
|
c.desc 'Note'
|
541
|
-
c.arg_name '
|
542
|
-
c.flag [
|
543
|
-
|
544
|
-
c.action do |
|
545
|
-
tags = options[:tag].nil? ? [] : options[:tag].split(/ *, */).map {|t| t.sub(/^@/, '').strip }
|
546
|
-
|
578
|
+
c.arg_name 'TEXT'
|
579
|
+
c.flag %i[n note]
|
580
|
+
|
581
|
+
c.action do |_global_options, options, _args|
|
582
|
+
tags = options[:tag].nil? ? [] : options[:tag].split(/ *, */).map { |t| t.sub(/^@/, '').strip }
|
583
|
+
opts = {
|
584
|
+
in: options[:in],
|
585
|
+
note: options[:n],
|
586
|
+
search: options[:search],
|
587
|
+
section: options[:s],
|
588
|
+
tag: tags,
|
589
|
+
tag_bool: options[:bool]
|
590
|
+
}
|
591
|
+
wwid.restart_last(opts)
|
547
592
|
end
|
548
593
|
end
|
549
594
|
|
550
|
-
desc '
|
551
|
-
arg_name '
|
595
|
+
desc 'Add tag(s) to last entry'
|
596
|
+
arg_name 'TAG', :multiple
|
552
597
|
command :tag do |c|
|
553
598
|
c.desc 'Section'
|
554
|
-
c.
|
599
|
+
c.arg_name 'SECTION_NAME'
|
600
|
+
c.flag %i[s section], default_value: 'All'
|
555
601
|
|
556
602
|
c.desc 'How many recent entries to tag (0 for all)'
|
557
|
-
c.
|
603
|
+
c.arg_name 'COUNT'
|
604
|
+
c.flag %i[c count], default_value: 1
|
558
605
|
|
559
606
|
c.desc 'Include current date/time with tag'
|
560
|
-
c.switch [
|
607
|
+
c.switch %i[d date], negatable: false, default_value: false
|
561
608
|
|
562
609
|
c.desc 'Remove given tag(s)'
|
563
|
-
c.switch [
|
610
|
+
c.switch %i[r remove], negatable: false, default_value: false
|
564
611
|
|
565
612
|
c.desc 'Autotag entries based on autotag configuration in ~/.doingrc'
|
566
|
-
c.switch [
|
613
|
+
c.switch %i[a autotag], negatable: false, default_value: false
|
567
614
|
|
568
|
-
c.action do |
|
569
|
-
if args.
|
570
|
-
raise "You must specify at least one tag"
|
571
|
-
else
|
572
|
-
|
573
|
-
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
615
|
+
c.action do |_global_options, options, args|
|
616
|
+
raise 'You must specify at least one tag' if args.empty? && !options[:a]
|
574
617
|
|
575
|
-
|
576
|
-
if args.join("") =~ /,/
|
577
|
-
tags = args.join("").split(/,/)
|
578
|
-
else
|
579
|
-
tags = args.join(" ").split(" ") # in case tags are quoted as one arg
|
580
|
-
end
|
618
|
+
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
581
619
|
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
620
|
+
if options[:a]
|
621
|
+
tags = []
|
622
|
+
else
|
623
|
+
tags = if args.join('') =~ /,/
|
624
|
+
args.join('').split(/,/)
|
625
|
+
else
|
626
|
+
args.join(' ').split(' ') # in case tags are quoted as one arg
|
627
|
+
end
|
586
628
|
|
587
|
-
|
629
|
+
tags.map! { |tag| tag.sub(/^@/, '').strip }
|
630
|
+
end
|
588
631
|
|
589
|
-
|
590
|
-
section_q = section == 'All' ? "" : " in section #{section}"
|
632
|
+
count = options[:c].to_i
|
591
633
|
|
592
|
-
|
593
|
-
|
594
|
-
elsif options[:r]
|
595
|
-
question = "Are you sure you want to remove #{tags.join(" and ")} from all records#{section_q}"
|
596
|
-
else
|
597
|
-
question = "Are you sure you want to add #{tags.join(" and ")} to all records#{section_q}"
|
598
|
-
end
|
634
|
+
if count.zero?
|
635
|
+
section_q = section == 'All' ? '' : " in section #{section}"
|
599
636
|
|
600
|
-
|
637
|
+
question = if options[:a]
|
638
|
+
"Are you sure you want to autotag all records#{section_q}"
|
639
|
+
elsif options[:r]
|
640
|
+
"Are you sure you want to remove #{tags.join(' and ')} from all records#{section_q}"
|
641
|
+
else
|
642
|
+
"Are you sure you want to add #{tags.join(' and ')} to all records#{section_q}"
|
643
|
+
end
|
601
644
|
|
602
|
-
|
603
|
-
raise "Cancelled"
|
604
|
-
end
|
605
|
-
end
|
645
|
+
res = wwid.yn(question, default_response: false)
|
606
646
|
|
607
|
-
|
647
|
+
raise 'Cancelled' unless res
|
608
648
|
end
|
649
|
+
opts = {
|
650
|
+
autotag: options[:a],
|
651
|
+
count: count,
|
652
|
+
date: options[:date],
|
653
|
+
remove: options[:r],
|
654
|
+
section: section,
|
655
|
+
tags: tags
|
656
|
+
}
|
657
|
+
wwid.tag_last(opts)
|
609
658
|
end
|
610
659
|
end
|
611
660
|
|
612
661
|
desc 'Mark last entry as highlighted'
|
613
662
|
command [:mark, :flag] do |c|
|
614
663
|
c.desc 'Section'
|
615
|
-
c.
|
664
|
+
c.arg_name 'NAME'
|
665
|
+
c.flag %i[s section], default_value: wwid.current_section
|
616
666
|
|
617
667
|
c.desc 'Remove mark'
|
618
|
-
c.switch [
|
668
|
+
c.switch %i[r remove], negatable: false, default_value: false
|
619
669
|
|
620
|
-
|
621
|
-
|
622
|
-
mark
|
623
|
-
wwid.tag_last({tags: [mark], section: options[:s], remove: options[:r]})
|
670
|
+
c.action do |_global_options, options, _args|
|
671
|
+
mark = wwid.config['marker_tag'] || 'flagged'
|
672
|
+
wwid.tag_last({ tags: [mark], section: options[:s], remove: options[:r] })
|
624
673
|
end
|
625
674
|
end
|
626
675
|
|
627
676
|
desc 'List all entries'
|
628
|
-
long_desc
|
629
|
-
|
677
|
+
long_desc %(
|
678
|
+
The argument can be a section name, @tag(s) or both.
|
679
|
+
"pick" or "choose" as an argument will offer a section menu.
|
680
|
+
)
|
681
|
+
arg_name '[SECTION|@TAGS]'
|
630
682
|
command :show do |c|
|
631
683
|
c.desc 'Tag filter, combine multiple tags with a comma. Added for compatibility with other commands.'
|
684
|
+
c.arg_name 'TAG'
|
632
685
|
c.flag [:tag]
|
633
686
|
|
634
687
|
c.desc 'Tag boolean (AND,OR,NONE)'
|
635
|
-
c.
|
688
|
+
c.arg_name 'BOOLEAN'
|
689
|
+
c.flag %i[b bool], must_match: /^(and|or|not)$/i, default_value: 'OR'
|
636
690
|
|
637
691
|
c.desc 'Max count to show'
|
638
|
-
c.
|
692
|
+
c.arg_name 'MAX'
|
693
|
+
c.flag %i[c count], default_value: 0
|
639
694
|
|
640
695
|
c.desc 'Age (oldest/newest)'
|
641
|
-
c.
|
696
|
+
c.arg_name 'AGE'
|
697
|
+
c.flag %i[a age], default_value: 'newest'
|
642
698
|
|
643
699
|
c.desc 'Sort order (asc/desc)'
|
644
|
-
c.
|
700
|
+
c.arg_name 'ORDER'
|
701
|
+
c.flag %i[s sort], must_match: /^(a|d)/i, default_value: 'ASC'
|
645
702
|
|
646
|
-
c.desc %
|
703
|
+
c.desc %(
|
647
704
|
Date range to show, or a single day to filter date on.
|
648
705
|
Date range argument should be quoted. Date specifications can be natural language.
|
649
706
|
To specify a range, use "to" or "through": `doing show --from "monday to friday"`
|
650
|
-
|
651
|
-
c.
|
707
|
+
)
|
708
|
+
c.arg_name 'DATE_OR_RANGE'
|
709
|
+
c.flag %i[f from]
|
652
710
|
|
653
711
|
c.desc 'Show time intervals on @done tasks'
|
654
|
-
c.switch [
|
712
|
+
c.switch %i[t times], default_value: true
|
655
713
|
|
656
714
|
c.desc 'Show intervals with totals at the end of output'
|
657
715
|
c.switch [:totals], default_value: false, negatable: true
|
658
716
|
|
659
717
|
c.desc 'Sort tags by (name|time)'
|
660
718
|
default = 'time'
|
661
|
-
if wwid.config.
|
662
|
-
|
663
|
-
|
664
|
-
c.flag [:tag_sort], default_value: default
|
719
|
+
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
720
|
+
c.arg_name 'KEY'
|
721
|
+
c.flag [:tag_sort], must_match: /^(name|time)/i, default_value: default
|
665
722
|
|
666
723
|
c.desc 'Only show items with recorded time intervals'
|
667
724
|
c.switch [:only_timed], default_value: false, negatable: false
|
668
725
|
|
669
|
-
c.desc 'Output to export format (csv|html|json)'
|
670
|
-
c.
|
671
|
-
c.
|
672
|
-
|
726
|
+
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
727
|
+
c.arg_name 'FORMAT'
|
728
|
+
c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
|
729
|
+
c.action do |_global_options, options, args|
|
673
730
|
tag_filter = false
|
674
731
|
tags = []
|
675
|
-
if args.length
|
676
|
-
|
677
|
-
|
732
|
+
if args.length.positive?
|
733
|
+
case args[0]
|
734
|
+
when /^all$/i
|
735
|
+
section = 'All'
|
678
736
|
args.shift
|
679
|
-
|
737
|
+
when /^(choose|pick)$/i
|
680
738
|
section = wwid.choose_section
|
681
739
|
args.shift
|
682
|
-
|
683
|
-
section =
|
740
|
+
when /^@/
|
741
|
+
section = 'All'
|
684
742
|
else
|
685
743
|
section = wwid.guess_section(args[0])
|
686
744
|
raise "No such section: #{args[0]}" unless section
|
745
|
+
|
687
746
|
args.shift
|
688
747
|
end
|
689
|
-
if args.length
|
690
|
-
args.each
|
748
|
+
if args.length.positive?
|
749
|
+
args.each do |arg|
|
691
750
|
if arg =~ /,/
|
692
|
-
arg.split(/,/).each
|
693
|
-
tags.push(tag.strip.sub(/^@/,''))
|
694
|
-
|
751
|
+
arg.split(/,/).each do |tag|
|
752
|
+
tags.push(tag.strip.sub(/^@/, ''))
|
753
|
+
end
|
695
754
|
else
|
696
|
-
tags.push(arg.strip.sub(/^@/,''))
|
755
|
+
tags.push(arg.strip.sub(/^@/, ''))
|
697
756
|
end
|
698
|
-
|
757
|
+
end
|
699
758
|
end
|
700
759
|
else
|
701
760
|
section = wwid.current_section
|
702
761
|
end
|
703
762
|
|
704
|
-
tags.concat(options[:tag].split(/ *, */).map {|t| t.sub(/^@/, '').strip }) if options[:tag]
|
763
|
+
tags.concat(options[:tag].split(/ *, */).map { |t| t.sub(/^@/, '').strip }) if options[:tag]
|
705
764
|
|
706
765
|
unless tags.empty?
|
707
766
|
tag_filter = {
|
@@ -720,96 +779,127 @@ command :show do |c|
|
|
720
779
|
start = wwid.chronify(date_string)
|
721
780
|
finish = false
|
722
781
|
end
|
723
|
-
exit_now!
|
782
|
+
exit_now! 'Unrecognized date string' unless start
|
724
783
|
dates = [start, finish]
|
725
784
|
end
|
726
785
|
|
727
786
|
options[:t] = true if options[:totals]
|
728
787
|
|
729
|
-
|
730
|
-
|
731
|
-
puts wwid.list_section({section: section, date_filter: dates, count: options[:c].to_i, tag_filter: tag_filter, age: options[:a], order: options[:s], output: options[:output], times: options[:t], totals: options[:totals], sort_tags: options[:sort_tags], highlight: true, only_timed: options[:only_timed]})
|
788
|
+
tags_color = wwid.config.key?('tags_color') ? wwid.config['tags_color'] : nil
|
732
789
|
|
790
|
+
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
791
|
+
opts = {
|
792
|
+
age: options[:a],
|
793
|
+
count: options[:c].to_i,
|
794
|
+
date_filter: dates,
|
795
|
+
highlight: true,
|
796
|
+
only_timed: options[:only_timed],
|
797
|
+
order: options[:s],
|
798
|
+
output: options[:output],
|
799
|
+
section: section,
|
800
|
+
sort_tags: options[:sort_tags],
|
801
|
+
tag_filter: tag_filter,
|
802
|
+
tags_color: tags_color,
|
803
|
+
times: options[:t],
|
804
|
+
totals: options[:totals]
|
805
|
+
}
|
806
|
+
puts wwid.list_section(opts)
|
733
807
|
end
|
734
808
|
end
|
735
809
|
|
736
810
|
desc 'Search for entries'
|
737
|
-
long_desc
|
738
|
-
Search all sections (or limit to a single section) for entries matching text or regular expression. Normal strings are fuzzy matched.
|
811
|
+
long_desc <<~'EODESC'
|
812
|
+
Search all sections (or limit to a single section) for entries matching text or regular expression. Normal strings are fuzzy matched.
|
739
813
|
|
740
|
-
To search with regular expressions, single quote the string and surround with slashes: `doing search '/\bm.*?x\b/'`
|
814
|
+
To search with regular expressions, single quote the string and surround with slashes: `doing search '/\bm.*?x\b/'`
|
741
815
|
EODESC
|
742
816
|
|
743
|
-
arg_name '
|
817
|
+
arg_name 'SEARCH_PATTERN'
|
744
818
|
command [:grep, :search] do |c|
|
745
819
|
c.desc 'Section'
|
746
|
-
c.
|
820
|
+
c.arg_name 'NAME'
|
821
|
+
c.flag %i[s section], default_value: 'All'
|
747
822
|
|
748
|
-
c.desc 'Output to export format (csv|html|json)'
|
749
|
-
c.
|
823
|
+
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
824
|
+
c.arg_name 'FORMAT'
|
825
|
+
c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
|
750
826
|
|
751
827
|
c.desc 'Show time intervals on @done tasks'
|
752
|
-
c.switch [
|
828
|
+
c.switch %i[t times], default_value: true
|
753
829
|
|
754
830
|
c.desc 'Show intervals with totals at the end of output'
|
755
831
|
c.switch [:totals], default_value: false, negatable: true
|
756
832
|
|
757
833
|
c.desc 'Sort tags by (name|time)'
|
758
834
|
default = 'time'
|
759
|
-
if wwid.config.
|
760
|
-
|
761
|
-
|
762
|
-
c.flag [:tag_sort], default_value: default
|
835
|
+
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
836
|
+
c.arg_name 'KEY'
|
837
|
+
c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
|
763
838
|
|
764
839
|
c.desc 'Only show items with recorded time intervals'
|
765
840
|
c.switch [:only_timed], default_value: false, negatable: false
|
766
841
|
|
767
|
-
c.action do |
|
842
|
+
c.action do |_global_options, options, args|
|
843
|
+
tags_color = wwid.config.key?('tags_color') ? wwid.config['tags_color'] : nil
|
768
844
|
|
769
845
|
section = wwid.guess_section(options[:s]) if options[:s]
|
770
846
|
|
771
847
|
options[:t] = true if options[:totals]
|
772
848
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
773
849
|
|
774
|
-
|
850
|
+
opts = {
|
851
|
+
highlight: true,
|
852
|
+
only_timed: options[:only_timed],
|
853
|
+
output: options[:output],
|
854
|
+
search: args.join(' '),
|
855
|
+
section: section,
|
856
|
+
sort_tags: options[:sort_tags],
|
857
|
+
tags_color: tags_color,
|
858
|
+
times: options[:t],
|
859
|
+
totals: options[:totals]
|
860
|
+
}
|
775
861
|
|
862
|
+
puts wwid.list_section(opts)
|
776
863
|
end
|
777
864
|
end
|
778
865
|
|
779
866
|
desc 'List recent entries'
|
780
867
|
default_value 10
|
781
|
-
arg_name '
|
868
|
+
arg_name 'COUNT'
|
782
869
|
command :recent do |c|
|
783
870
|
c.desc 'Section'
|
784
|
-
c.
|
871
|
+
c.arg_name 'NAME'
|
872
|
+
c.flag %i[s section], default_value: 'All'
|
785
873
|
|
786
874
|
c.desc 'Show time intervals on @done tasks'
|
787
|
-
c.switch [
|
875
|
+
c.switch %i[t times], default_value: true
|
788
876
|
|
789
877
|
c.desc 'Show intervals with totals at the end of output'
|
790
878
|
c.switch [:totals], default_value: false, negatable: true
|
791
879
|
|
792
880
|
c.desc 'Sort tags by (name|time)'
|
793
881
|
default = 'time'
|
794
|
-
if wwid.config.
|
795
|
-
|
796
|
-
|
797
|
-
c.flag [:tag_sort], default_value: default
|
798
|
-
|
799
|
-
c.action do |global_options,options,args|
|
882
|
+
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
883
|
+
c.arg_name 'KEY'
|
884
|
+
c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
|
800
885
|
|
886
|
+
c.action do |global_options, options, args|
|
801
887
|
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
802
888
|
|
803
889
|
unless global_options[:version]
|
804
|
-
|
805
|
-
count = args[0].to_i
|
806
|
-
else
|
807
|
-
count = 10
|
808
|
-
end
|
890
|
+
count = args.empty? ? 10 : args[0].to_i
|
809
891
|
options[:t] = true if options[:totals]
|
810
892
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
893
|
+
tags_color = wwid.config.key?('tags_color') ? wwid.config['tags_color'] : nil
|
811
894
|
|
812
|
-
|
895
|
+
opts = {
|
896
|
+
sort_tags: options[:sort_tags],
|
897
|
+
tags_color: tags_color,
|
898
|
+
times: options[:t],
|
899
|
+
totals: options[:totals]
|
900
|
+
}
|
901
|
+
|
902
|
+
puts wwid.recent(count, section.cap_first, opts)
|
813
903
|
|
814
904
|
end
|
815
905
|
end
|
@@ -818,63 +908,64 @@ end
|
|
818
908
|
desc 'List entries from today'
|
819
909
|
command :today do |c|
|
820
910
|
c.desc 'Specify a section'
|
821
|
-
c.arg_name '
|
822
|
-
c.flag [
|
911
|
+
c.arg_name 'NAME'
|
912
|
+
c.flag %i[s section], default_value: 'All'
|
823
913
|
|
824
914
|
c.desc 'Show time intervals on @done tasks'
|
825
|
-
c.switch [
|
915
|
+
c.switch %i[t times], default_value: true
|
826
916
|
|
827
917
|
c.desc 'Show time totals at the end of output'
|
828
918
|
c.switch [:totals], default_value: false, negatable: true
|
829
919
|
|
830
920
|
c.desc 'Sort tags by (name|time)'
|
831
921
|
default = 'time'
|
832
|
-
if wwid.config.
|
833
|
-
|
834
|
-
|
835
|
-
c.flag [:tag_sort], default_value: default
|
836
|
-
|
837
|
-
c.desc 'Output to export format (csv|html|json)'
|
838
|
-
c.flag [:o, :output]
|
922
|
+
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
923
|
+
c.arg_name 'KEY'
|
924
|
+
c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
|
839
925
|
|
840
|
-
c.
|
926
|
+
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
927
|
+
c.arg_name 'FORMAT'
|
928
|
+
c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
|
841
929
|
|
930
|
+
c.action do |_global_options, options, _args|
|
842
931
|
options[:t] = true if options[:totals]
|
843
932
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
844
933
|
|
845
|
-
puts wwid.today(options[:t], options[:output],
|
846
|
-
|
934
|
+
puts wwid.today(options[:t], options[:output],
|
935
|
+
{ totals: options[:totals], section: options[:s], sort_tags: options[:sort_tags] }).chomp
|
847
936
|
end
|
848
937
|
end
|
849
938
|
|
850
939
|
desc 'List entries for a date'
|
851
|
-
long_desc
|
852
|
-
|
940
|
+
long_desc %(Date argument can be natural language. "thursday" would be interpreted as "last thursday,"
|
941
|
+
and "2d" would be interpreted as "two days ago." If you use "to" or "through" between two dates,
|
942
|
+
it will create a range.)
|
943
|
+
arg_name 'DATE_STRING'
|
853
944
|
command :on do |c|
|
854
945
|
c.desc 'Section'
|
855
|
-
c.arg_name '
|
856
|
-
c.flag [
|
946
|
+
c.arg_name 'NAME'
|
947
|
+
c.flag %i[s section], default_value: 'All'
|
857
948
|
|
858
949
|
c.desc 'Show time intervals on @done tasks'
|
859
|
-
c.switch [
|
950
|
+
c.switch %i[t times], default_value: true
|
860
951
|
|
861
952
|
c.desc 'Show time totals at the end of output'
|
862
953
|
c.switch [:totals], default_value: false, negatable: true
|
863
954
|
|
864
955
|
c.desc 'Sort tags by (name|time)'
|
865
956
|
default = 'time'
|
866
|
-
if wwid.config.
|
867
|
-
|
868
|
-
|
869
|
-
c.flag [:tag_sort], default_value: default
|
957
|
+
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
958
|
+
c.arg_name 'KEY'
|
959
|
+
c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
|
870
960
|
|
871
|
-
c.desc 'Output to export format (csv|html|json)'
|
872
|
-
c.
|
961
|
+
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
962
|
+
c.arg_name 'FORMAT'
|
963
|
+
c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
|
873
964
|
|
874
|
-
c.action do |
|
875
|
-
exit_now!
|
965
|
+
c.action do |_global_options, options, args|
|
966
|
+
exit_now! 'Missing date argument' if args.empty?
|
876
967
|
|
877
|
-
date_string = args.join(
|
968
|
+
date_string = args.join(' ')
|
878
969
|
|
879
970
|
if date_string =~ / (to|through|thru) /
|
880
971
|
dates = date_string.split(/ (to|through|thru) /)
|
@@ -885,7 +976,7 @@ command :on do |c|
|
|
885
976
|
finish = false
|
886
977
|
end
|
887
978
|
|
888
|
-
exit_now!
|
979
|
+
exit_now! 'Unrecognized date string' unless start
|
889
980
|
|
890
981
|
message = "Date interpreted as #{start}"
|
891
982
|
message += " to #{finish}" if finish
|
@@ -894,56 +985,86 @@ command :on do |c|
|
|
894
985
|
options[:t] = true if options[:totals]
|
895
986
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
896
987
|
|
897
|
-
puts wwid.list_date([start, finish], options[:s], options[:t], options[:output],
|
898
|
-
|
988
|
+
puts wwid.list_date([start, finish], options[:s], options[:t], options[:output],
|
989
|
+
{ totals: options[:totals], sort_tags: options[:sort_tags] }).chomp
|
899
990
|
end
|
900
991
|
end
|
901
992
|
|
902
993
|
desc 'List entries from yesterday'
|
903
994
|
command :yesterday do |c|
|
904
995
|
c.desc 'Specify a section'
|
905
|
-
c.arg_name '
|
906
|
-
c.flag [
|
996
|
+
c.arg_name 'NAME'
|
997
|
+
c.flag %i[s section], default_value: 'All'
|
907
998
|
|
908
|
-
c.desc 'Output to export format (csv|html|json)'
|
909
|
-
c.
|
999
|
+
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
1000
|
+
c.arg_name 'FORMAT'
|
1001
|
+
c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
|
910
1002
|
|
911
1003
|
c.desc 'Show time intervals on @done tasks'
|
912
|
-
c.switch [
|
1004
|
+
c.switch %i[t times], default_value: true
|
913
1005
|
|
914
1006
|
c.desc 'Show time totals at the end of output'
|
915
1007
|
c.switch [:totals], default_value: false, negatable: true
|
916
1008
|
|
917
1009
|
c.desc 'Sort tags by (name|time)'
|
918
1010
|
default = 'time'
|
919
|
-
if wwid.config.
|
920
|
-
|
921
|
-
|
922
|
-
c.flag [:tag_sort], default_value: default
|
1011
|
+
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
1012
|
+
c.arg_name 'KEY'
|
1013
|
+
c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
|
923
1014
|
|
924
|
-
c.action do |
|
1015
|
+
c.action do |_global_options, options, _args|
|
925
1016
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
926
|
-
puts wwid.yesterday(options[:s], options[:t], options[:o],
|
927
|
-
|
1017
|
+
puts wwid.yesterday(options[:s], options[:t], options[:o],
|
1018
|
+
{ totals: options[:totals], sort_tags: options[:sort_tags] }).chomp
|
928
1019
|
end
|
929
1020
|
end
|
930
1021
|
|
931
|
-
desc 'Show the last entry'
|
1022
|
+
desc 'Show the last entry, optionally edit'
|
932
1023
|
command :last do |c|
|
933
1024
|
c.desc 'Specify a section'
|
934
|
-
c.
|
1025
|
+
c.arg_name 'NAME'
|
1026
|
+
c.flag %i[s section], default_value: 'All'
|
935
1027
|
|
936
|
-
c.
|
937
|
-
|
1028
|
+
c.desc "Edit entry with #{ENV['EDITOR']}"
|
1029
|
+
c.switch %i[e editor], negatable: false, default_value: false
|
1030
|
+
|
1031
|
+
c.desc 'Tag filter, combine multiple tags with a comma.'
|
1032
|
+
c.arg_name 'TAG'
|
1033
|
+
c.flag [:tag]
|
1034
|
+
|
1035
|
+
c.desc 'Tag boolean'
|
1036
|
+
c.arg_name 'BOOLEAN'
|
1037
|
+
c.flag [:bool], must_match: /(and|or|not)/i, default_value: 'AND'
|
1038
|
+
|
1039
|
+
c.desc 'Search filter, surround with slashes for regex (/query/)'
|
1040
|
+
c.arg_name 'QUERY'
|
1041
|
+
c.flag [:search]
|
1042
|
+
|
1043
|
+
c.action do |_global_options, options, _args|
|
1044
|
+
raise '--tag and --search cannot be used together' if options[:tag] && options[:search]
|
1045
|
+
|
1046
|
+
if options[:tag].nil?
|
1047
|
+
tags = []
|
1048
|
+
else
|
1049
|
+
tags = options[:tag].split(/ *, */).map { |t| t.strip.sub(/^@/, '') }
|
1050
|
+
options[:bool] = options[:bool] =~ /^(and|or|not)$/i ? options[:bool].upcase : 'AND'
|
1051
|
+
end
|
1052
|
+
|
1053
|
+
if options[:e]
|
1054
|
+
wwid.edit_last(section: options[:s], options: { search: options[:search], tag: tags, tag_bool: options[:bool] })
|
1055
|
+
else
|
1056
|
+
puts wwid.last(times: true, section: options[:s],
|
1057
|
+
options: { search: options[:search], tag: tags, tag_bool: options[:bool] }).strip
|
1058
|
+
end
|
938
1059
|
end
|
939
1060
|
end
|
940
1061
|
|
941
1062
|
desc 'List sections'
|
942
1063
|
command :sections do |c|
|
943
1064
|
c.desc 'List in single column'
|
944
|
-
c.switch [
|
1065
|
+
c.switch %i[c column], default_value: false
|
945
1066
|
|
946
|
-
c.action do |
|
1067
|
+
c.action do |_global_options, options, _args|
|
947
1068
|
joiner = options[:c] ? "\n" : "\t"
|
948
1069
|
print wwid.sections.join(joiner)
|
949
1070
|
end
|
@@ -951,57 +1072,58 @@ end
|
|
951
1072
|
|
952
1073
|
desc 'Select a section to display from a menu'
|
953
1074
|
command :choose do |c|
|
954
|
-
c.action do |
|
1075
|
+
c.action do |_global_options, _options, _args|
|
955
1076
|
section = wwid.choose_section
|
956
1077
|
puts wwid.list_section({ section: section.cap_first, count: 0 })
|
957
1078
|
end
|
958
1079
|
end
|
959
1080
|
|
960
1081
|
desc 'Add a new section to the "doing" file'
|
961
|
-
arg_name '
|
1082
|
+
arg_name 'SECTION_NAME'
|
962
1083
|
command :add_section do |c|
|
963
|
-
c.action do |
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
raise "Section #{args[0]} already exists"
|
969
|
-
end
|
1084
|
+
c.action do |_global_options, _options, args|
|
1085
|
+
raise "Section #{args[0]} already exists" if wwid.sections.include?(args[0])
|
1086
|
+
|
1087
|
+
wwid.add_section(args[0].cap_first)
|
1088
|
+
wwid.write(wwid.doing_file)
|
970
1089
|
end
|
971
1090
|
end
|
972
1091
|
|
973
1092
|
desc 'List available color variables for configuration templates and views'
|
974
1093
|
command :colors do |c|
|
975
|
-
c.action do |
|
1094
|
+
c.action do |_global_options, _options, _args|
|
976
1095
|
clrs = wwid.colors
|
977
1096
|
bgs = []
|
978
1097
|
fgs = []
|
979
|
-
clrs.each
|
1098
|
+
clrs.each do |k, v|
|
980
1099
|
if k =~ /bg/
|
981
1100
|
bgs.push("#{v} #{clrs['default']} <-- #{k}")
|
982
1101
|
else
|
983
1102
|
fgs.push("#{v}XXXX#{clrs['default']} <-- #{k}")
|
984
1103
|
end
|
985
|
-
|
1104
|
+
end
|
986
1105
|
puts fgs.join("\n")
|
987
1106
|
puts bgs.join("\n")
|
988
1107
|
end
|
989
1108
|
end
|
990
1109
|
|
991
1110
|
desc 'Display a user-created view'
|
992
|
-
arg_name '
|
1111
|
+
arg_name 'VIEW_NAME'
|
993
1112
|
command :view do |c|
|
994
1113
|
c.desc 'Section (override view settings)'
|
995
|
-
c.
|
1114
|
+
c.arg_name 'NAME'
|
1115
|
+
c.flag %i[s section]
|
996
1116
|
|
997
1117
|
c.desc 'Count to display (override view settings)'
|
998
|
-
c.
|
1118
|
+
c.arg_name 'COUNT'
|
1119
|
+
c.flag %i[c count], must_match: /^\d+$/, type: Integer
|
999
1120
|
|
1000
|
-
c.desc 'Output to export format (csv|html|json)'
|
1001
|
-
c.
|
1121
|
+
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
1122
|
+
c.arg_name 'FORMAT'
|
1123
|
+
c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
|
1002
1124
|
|
1003
1125
|
c.desc 'Show time intervals on @done tasks'
|
1004
|
-
c.switch [
|
1126
|
+
c.switch %i[t times], default_value: true
|
1005
1127
|
|
1006
1128
|
c.desc 'Show intervals with totals at the end of output'
|
1007
1129
|
c.switch [:totals], default_value: false, negatable: true
|
@@ -1011,67 +1133,84 @@ command :view do |c|
|
|
1011
1133
|
|
1012
1134
|
c.desc 'Sort tags by (name|time)'
|
1013
1135
|
default = 'time'
|
1014
|
-
if wwid.config.
|
1015
|
-
|
1016
|
-
|
1017
|
-
c.flag [:tag_sort], default_value: default
|
1136
|
+
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
1137
|
+
c.arg_name 'KEY'
|
1138
|
+
c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
|
1018
1139
|
|
1019
1140
|
c.desc 'Only show items with recorded time intervals'
|
1020
1141
|
c.switch [:only_timed], default_value: false, negatable: true
|
1021
1142
|
|
1022
|
-
c.action do |
|
1023
|
-
if args.empty?
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1143
|
+
c.action do |_global_options, options, args|
|
1144
|
+
title = if args.empty?
|
1145
|
+
wwid.choose_view
|
1146
|
+
else
|
1147
|
+
wwid.guess_view(args[0])
|
1148
|
+
end
|
1028
1149
|
|
1029
|
-
if options[:s]
|
1030
|
-
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
1031
|
-
end
|
1150
|
+
section = wwid.guess_section(options[:s]) || options[:s].cap_first if options[:s]
|
1032
1151
|
|
1033
1152
|
view = wwid.get_view(title)
|
1034
1153
|
if view
|
1035
|
-
if (view.
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
template = view.
|
1042
|
-
format = view.
|
1043
|
-
tags_color = view.
|
1154
|
+
only_timed = if (view.key?('only_timed') && view['only_timed']) || options[:only_timed]
|
1155
|
+
true
|
1156
|
+
else
|
1157
|
+
false
|
1158
|
+
end
|
1159
|
+
|
1160
|
+
template = view.key?('template') ? view['template'] : nil
|
1161
|
+
format = view.key?('date_format') ? view['date_format'] : nil
|
1162
|
+
tags_color = view.key?('tags_color') ? view['tags_color'] : nil
|
1044
1163
|
tag_filter = false
|
1045
|
-
if view.
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
tag_filter['bool'] = view.has_key?('tags_bool') && !view['tags_bool'].nil? ? view['tags_bool'].upcase : "OR"
|
1054
|
-
end
|
1164
|
+
if view.key?('tags') && !(view['tags'].nil? || view['tags'].empty?)
|
1165
|
+
tag_filter = { 'tags' => [], 'bool' => 'OR' }
|
1166
|
+
tag_filter['tags'] = if view['tags'].instance_of?(Array)
|
1167
|
+
view['tags'].map(&:strip)
|
1168
|
+
else
|
1169
|
+
view['tags'].gsub(/[, ]+/, ' ').split(' ').map(&:strip)
|
1170
|
+
end
|
1171
|
+
tag_filter['bool'] = view.key?('tags_bool') && !view['tags_bool'].nil? ? view['tags_bool'].upcase : 'OR'
|
1055
1172
|
end
|
1056
1173
|
|
1057
1174
|
# If the -o/--output flag was specified, override any default in the view template
|
1058
|
-
options[:o] ||= view.
|
1059
|
-
|
1060
|
-
count =
|
1061
|
-
|
1062
|
-
|
1175
|
+
options[:o] ||= view.key?('output_format') ? view['output_format'] : 'template'
|
1176
|
+
|
1177
|
+
count = if options[:c]
|
1178
|
+
options[:c]
|
1179
|
+
else
|
1180
|
+
view.key?('count') ? view['count'] : 10
|
1181
|
+
end
|
1182
|
+
section = if options[:s]
|
1183
|
+
section
|
1184
|
+
else
|
1185
|
+
view.key?('section') ? view['section'] : wwid.current_section
|
1186
|
+
end
|
1187
|
+
order = view.key?('order') ? view['order'] : 'asc'
|
1063
1188
|
|
1064
1189
|
options[:t] = true if options[:totals]
|
1065
|
-
options[:output]
|
1190
|
+
options[:output]&.downcase!
|
1066
1191
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
1067
1192
|
|
1068
|
-
|
1193
|
+
opts = {
|
1194
|
+
count: count,
|
1195
|
+
format: format,
|
1196
|
+
highlight: options[:color],
|
1197
|
+
only_timed: only_timed,
|
1198
|
+
order: order,
|
1199
|
+
output: options[:o],
|
1200
|
+
section: section,
|
1201
|
+
sort_tags: options[:sort_tags],
|
1202
|
+
tag_filter: tag_filter,
|
1203
|
+
tags_color: tags_color,
|
1204
|
+
template: template,
|
1205
|
+
times: options[:t],
|
1206
|
+
totals: options[:totals]
|
1207
|
+
}
|
1208
|
+
|
1209
|
+
puts wwid.list_section(opts)
|
1210
|
+
elsif title.instance_of?(FalseClass)
|
1211
|
+
exit_now! 'Cancelled'
|
1069
1212
|
else
|
1070
|
-
|
1071
|
-
exit_now! "Cancelled"
|
1072
|
-
else
|
1073
|
-
raise "View #{title} not found in config"
|
1074
|
-
end
|
1213
|
+
raise "View #{title} not found in config"
|
1075
1214
|
end
|
1076
1215
|
end
|
1077
1216
|
end
|
@@ -1079,130 +1218,135 @@ end
|
|
1079
1218
|
desc 'List available custom views'
|
1080
1219
|
command :views do |c|
|
1081
1220
|
c.desc 'List in single column'
|
1082
|
-
c.switch [
|
1221
|
+
c.switch %i[c column], default_value: false
|
1083
1222
|
|
1084
|
-
c.action do |
|
1223
|
+
c.action do |_global_options, options, _args|
|
1085
1224
|
joiner = options[:c] ? "\n" : "\t"
|
1086
1225
|
print wwid.views.join(joiner)
|
1087
1226
|
end
|
1088
1227
|
end
|
1089
1228
|
|
1090
1229
|
desc 'Move entries between sections'
|
1091
|
-
arg_name '
|
1230
|
+
arg_name 'SECTION_NAME'
|
1092
1231
|
default_value wwid.current_section
|
1093
1232
|
command :archive do |c|
|
1094
1233
|
c.desc 'Count to keep (ignored if archiving by tag)'
|
1095
|
-
c.
|
1234
|
+
c.arg_name 'COUNT'
|
1235
|
+
c.flag %i[k keep], default_value: 5, must_match: /^\d+$/, type: Integer
|
1096
1236
|
|
1097
1237
|
c.desc 'Move entries to'
|
1098
|
-
c.
|
1238
|
+
c.arg_name 'SECTION_NAME'
|
1239
|
+
c.flag %i[t to], default_value: 'Archive'
|
1099
1240
|
|
1100
1241
|
c.desc 'Tag filter, combine multiple tags with a comma. Added for compatibility with other commands.'
|
1242
|
+
c.arg_name 'TAG'
|
1101
1243
|
c.flag [:tag]
|
1102
1244
|
|
1103
|
-
c.desc 'Tag boolean'
|
1104
|
-
c.
|
1245
|
+
c.desc 'Tag boolean (AND|OR|NOT)'
|
1246
|
+
c.arg_name 'BOOLEAN'
|
1247
|
+
c.flag [:bool], must_match: /(and|or|not)/i, default_value: 'AND'
|
1105
1248
|
|
1106
|
-
c.action do |
|
1107
|
-
if args.
|
1108
|
-
if args[0] =~ /^@\S+/
|
1109
|
-
section = "all"
|
1110
|
-
tags = args.map {|t| t.sub(/^@/,'').strip }
|
1111
|
-
else
|
1112
|
-
section = args[0].cap_first
|
1113
|
-
tags = args.length > 1 ? args[1..-1].map {|t| t.sub(/^@/, '').strip } : nil
|
1114
|
-
end
|
1115
|
-
else
|
1249
|
+
c.action do |_global_options, options, args|
|
1250
|
+
if args.empty?
|
1116
1251
|
section = wwid.current_section
|
1117
1252
|
tags = []
|
1253
|
+
elsif args[0] =~ /^@\S+/
|
1254
|
+
section = 'all'
|
1255
|
+
tags = args.map { |t| t.sub(/^@/, '').strip }
|
1256
|
+
else
|
1257
|
+
section = args[0].cap_first
|
1258
|
+
tags = args.length > 1 ? args[1..].map { |t| t.sub(/^@/, '').strip } : nil
|
1118
1259
|
end
|
1119
1260
|
|
1120
|
-
tags.concat(options[:tag].split(/ *, */).map {|t| t.sub(/^@/, '').strip }) if options[:tag]
|
1261
|
+
tags.concat(options[:tag].split(/ *, */).map { |t| t.sub(/^@/, '').strip }) if options[:tag]
|
1121
1262
|
|
1122
|
-
wwid.archive(section, options[:
|
1263
|
+
wwid.archive(section, options[:keep], options[:to], tags, options[:bool])
|
1123
1264
|
end
|
1124
1265
|
end
|
1125
1266
|
|
1126
1267
|
desc 'Open the "doing" file in an editor'
|
1268
|
+
long_desc "`doing open` defaults to using the editor_app setting in #{wwid.config_file} (#{wwid.config.key?('editor_app') ? wwid.config['editor_app'] : 'not set'})"
|
1127
1269
|
command :open do |c|
|
1128
|
-
if `uname` =~ /Darwin/
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
c.desc 'open with app bundle id'
|
1134
|
-
c.arg_name 'bundle_id'
|
1135
|
-
c.flag [:b]
|
1136
|
-
end
|
1137
|
-
c.desc 'open with $EDITOR'
|
1138
|
-
c.switch [:e], negatable: false
|
1270
|
+
if `uname` =~ /Darwin/
|
1271
|
+
c.desc 'Open with app name'
|
1272
|
+
c.arg_name 'APP_NAME'
|
1273
|
+
c.flag [:a]
|
1139
1274
|
|
1140
|
-
|
1275
|
+
c.desc 'Open with app bundle id'
|
1276
|
+
c.arg_name 'BUNDLE_ID'
|
1277
|
+
c.flag [:b]
|
1278
|
+
end
|
1279
|
+
c.desc "Open with $EDITOR (#{ENV['EDITOR']})"
|
1280
|
+
c.switch %i[e editor], negatable: false, default_value: false
|
1281
|
+
|
1282
|
+
c.action do |_global_options, options, _args|
|
1141
1283
|
params = options.dup
|
1142
|
-
params.delete_if
|
1143
|
-
k.
|
1144
|
-
|
1284
|
+
params.delete_if do |k, v|
|
1285
|
+
k.instance_of?(String) || v.nil? || v == false
|
1286
|
+
end
|
1145
1287
|
if `uname` =~ /Darwin/
|
1146
|
-
if params.length < 2
|
1147
1288
|
if options[:a]
|
1148
|
-
system %
|
1289
|
+
system %(open -a "#{options[:a]}" "#{File.expand_path(wwid.doing_file)}")
|
1149
1290
|
elsif options[:b]
|
1150
|
-
system %
|
1291
|
+
system %(open -b "#{options[:b]}" "#{File.expand_path(wwid.doing_file)}")
|
1151
1292
|
elsif options[:e]
|
1152
|
-
raise
|
1153
|
-
|
1293
|
+
raise 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
1294
|
+
|
1295
|
+
system %($EDITOR "#{File.expand_path(wwid.doing_file)}")
|
1296
|
+
elsif wwid.config.key?('editor_app') && !wwid.config['editor_app'].nil?
|
1297
|
+
system %(open -a "#{wwid.config['editor_app']}" "#{File.expand_path(wwid.doing_file)}")
|
1154
1298
|
else
|
1155
|
-
|
1156
|
-
system %Q{open -a "#{wwid.config['editor_app']}" "#{File.expand_path(wwid.doing_file)}"}
|
1157
|
-
else
|
1158
|
-
system %Q{open "#{File.expand_path(wwid.doing_file)}"}
|
1159
|
-
end
|
1299
|
+
system %(open "#{File.expand_path(wwid.doing_file)}")
|
1160
1300
|
end
|
1301
|
+
|
1161
1302
|
else
|
1162
|
-
raise
|
1163
|
-
|
1164
|
-
|
1165
|
-
raise "No EDITOR variable defined in environment" if ENV['EDITOR'].nil?
|
1166
|
-
system %Q{$EDITOR "#{File.expand_path(wwid.doing_file)}"}
|
1303
|
+
raise 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
1304
|
+
|
1305
|
+
system %($EDITOR "#{File.expand_path(wwid.doing_file)}")
|
1167
1306
|
end
|
1168
1307
|
end
|
1169
1308
|
end
|
1170
1309
|
|
1171
|
-
|
1172
1310
|
desc 'Edit the configuration file'
|
1173
1311
|
command :config do |c|
|
1174
1312
|
c.desc 'Editor to use'
|
1175
|
-
c.
|
1313
|
+
c.arg_name 'EDITOR'
|
1314
|
+
c.flag %i[e editor], default_value: nil
|
1176
1315
|
|
1177
|
-
if `uname` =~ /
|
1316
|
+
if `uname` =~ /Darwin/
|
1178
1317
|
c.desc 'Application to use'
|
1318
|
+
c.arg_name 'APP_NAME'
|
1179
1319
|
c.flag [:a]
|
1180
1320
|
|
1181
|
-
c.desc "Use the editor_app defined in ~/.doingrc (#{wwid.config['editor_app']})"
|
1182
|
-
c.switch [:x]
|
1183
|
-
|
1184
1321
|
c.desc 'Application bundle id to use'
|
1322
|
+
c.arg_name 'BUNDLE_ID'
|
1185
1323
|
c.flag [:b]
|
1324
|
+
|
1325
|
+
c.desc "Use the config_editor_app defined in ~/.doingrc (#{wwid.config.key?('config_editor_app') ? wwid.config['config_editor_app'] : 'config_editor_app not set'})"
|
1326
|
+
c.switch [:x]
|
1186
1327
|
end
|
1187
|
-
|
1188
|
-
|
1328
|
+
|
1329
|
+
c.action do |_global_options, options, _args|
|
1330
|
+
if `uname` =~ /Darwin/
|
1189
1331
|
if options[:x]
|
1190
|
-
|
1332
|
+
`open -a "#{wwid.config['config_editor_app']}" "#{wwid.config_file}"`
|
1191
1333
|
elsif options[:a] || options[:b]
|
1192
1334
|
if options[:a]
|
1193
|
-
|
1335
|
+
`open -a "#{options[:a]}" "#{wwid.config_file}"`
|
1194
1336
|
elsif options[:b]
|
1195
|
-
|
1337
|
+
`open -b #{options[:b]} "#{wwid.config_file}"`
|
1196
1338
|
end
|
1197
1339
|
else
|
1198
|
-
raise
|
1340
|
+
raise 'No EDITOR variable defined in environment' if options[:e].nil? && ENV['EDITOR'].nil?
|
1341
|
+
|
1199
1342
|
editor = options[:e].nil? ? ENV['EDITOR'] : options[:e]
|
1200
|
-
system %
|
1343
|
+
system %(#{editor} "#{wwid.config_file}")
|
1201
1344
|
end
|
1202
1345
|
else
|
1203
|
-
raise
|
1346
|
+
raise 'No EDITOR variable defined in environment' if options[:e].nil? && ENV['EDITOR'].nil?
|
1347
|
+
|
1204
1348
|
editor = options[:e].nil? ? ENV['EDITOR'] : options[:e]
|
1205
|
-
system %
|
1349
|
+
system %(#{editor} "#{wwid.config_file}")
|
1206
1350
|
end
|
1207
1351
|
end
|
1208
1352
|
end
|
@@ -1210,19 +1354,19 @@ end
|
|
1210
1354
|
desc 'Undo the last change to the doing_file'
|
1211
1355
|
command :undo do |c|
|
1212
1356
|
c.desc 'Specify alternate doing file'
|
1213
|
-
c.
|
1357
|
+
c.arg_name 'PATH'
|
1358
|
+
c.flag %i[f file], default_value: wwid.doing_file
|
1214
1359
|
|
1215
|
-
c.action do |
|
1360
|
+
c.action do |_global_options, options, _args|
|
1216
1361
|
file = options[:f] || wwid.doing_file
|
1217
1362
|
wwid.restore_backup(file)
|
1218
1363
|
end
|
1219
1364
|
end
|
1220
1365
|
|
1221
|
-
|
1222
|
-
|
1223
|
-
if global[:config_file]
|
1366
|
+
pre do |global, _command, _options, _args|
|
1367
|
+
if global[:config_file] && global[:config_file] != wwid.config_file
|
1224
1368
|
wwid.config_file = global[:config_file]
|
1225
|
-
wwid.configure({ignore_local: true})
|
1369
|
+
wwid.configure({ ignore_local: true })
|
1226
1370
|
# wwid.results.push("Override config file #{wwid.config_file}")
|
1227
1371
|
end
|
1228
1372
|
|
@@ -1236,9 +1380,7 @@ pre do |global,command,options,args|
|
|
1236
1380
|
|
1237
1381
|
wwid.config[:include_notes] = false unless global[:notes]
|
1238
1382
|
|
1239
|
-
if global[:version]
|
1240
|
-
$stdout.puts "doing v" + Doing::VERSION
|
1241
|
-
end
|
1383
|
+
$stdout.puts "doing v#{Doing::VERSION}" if global[:version]
|
1242
1384
|
|
1243
1385
|
# Return true to proceed; false to abort and not call the
|
1244
1386
|
# chosen command
|
@@ -1247,7 +1389,7 @@ pre do |global,command,options,args|
|
|
1247
1389
|
true
|
1248
1390
|
end
|
1249
1391
|
|
1250
|
-
post do |global,
|
1392
|
+
post do |global, _command, _options, _args|
|
1251
1393
|
# Use skips_post before a command to skip this
|
1252
1394
|
# block on that command only
|
1253
1395
|
if global[:stdout]
|
@@ -1257,7 +1399,7 @@ post do |global,command,options,args|
|
|
1257
1399
|
end
|
1258
1400
|
end
|
1259
1401
|
|
1260
|
-
on_error do |
|
1402
|
+
on_error do |_exception|
|
1261
1403
|
# puts exception.message
|
1262
1404
|
true
|
1263
1405
|
end
|