doing 1.0.45 → 1.0.46

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8449bbc0f6d6a8d059856d77abd2f4648544c3bda9e69c75a93a69fd8dffe851
4
- data.tar.gz: 90c223b97441535bd6cda1563fcbc2dd97197901b9972ceb51989b1f4ad9599c
3
+ metadata.gz: 35a90bfcfe901ff215cddf7ce3f0f19734371c2934c08af6bf0263137320d7cc
4
+ data.tar.gz: d791452d6e5235aaf2a04db3e485c57e56a99c84c65f61026f82fa0e6a56b04e
5
5
  SHA512:
6
- metadata.gz: a42c67cf02151718f867e98d2a6571c88a7360b2a83c5e529378730364f4b812a4811c8bc03e990b08193fc4503b6f0e3f16b572a21cac1f45d15cb562333755
7
- data.tar.gz: e0bac7d7dbd30808b9d438be3e4f887ddab05db294dff84467e55b3d3846e246e769b211dd37ff66f8e28c84c97262d23daca5af0445dd25ded03a7446707fef
6
+ metadata.gz: 2166a9ee774dc475e2f9d13c116ce7e1aaa550b0e454ce7ae47a6b950a855443a0c59d6a24ff87247aac28a88404314011a4d6420190fdf007b236a53dcac3e6
7
+ data.tar.gz: f3ebed8c0f2ae40770fca5df63a2a7a680915aa57d0baf0046a2ffb50f3908914bfbb8ba46a38ad943e6fe0fc1bd8d4e92b39761212bf6285dbf70b385544d77
data/bin/doing CHANGED
@@ -163,7 +163,7 @@ command :note do |c|
163
163
  if input
164
164
  title, note = wwid.format_input(input)
165
165
  if note
166
- wwid.note_last(section, note, true)
166
+ wwid.note_last(section, note, replace: true)
167
167
  else
168
168
  raise "No note content"
169
169
  end
@@ -174,14 +174,14 @@ command :note do |c|
174
174
  if args.length > 0
175
175
  title, note = wwid.format_input(args.join(" "))
176
176
  note.insert(0, title)
177
- wwid.note_last(section, note, options[:r])
177
+ wwid.note_last(section, note, replace: options[:r])
178
178
  elsif STDIN.stat.size > 0
179
179
  title, note = wwid.format_input(STDIN.read)
180
180
  note.insert(0, title)
181
- wwid.note_last(section, note, options[:r])
181
+ wwid.note_last(section, note, replace: options[:r])
182
182
  else
183
183
  if options[:r]
184
- wwid.note_last(section, [], true)
184
+ wwid.note_last(section, [], replace: true)
185
185
  else
186
186
  raise "You must provide content when adding a note"
187
187
  end
data/lib/doing/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Doing
2
- VERSION = '1.0.45'
2
+ VERSION = '1.0.46'
3
3
  end
data/lib/doing/wwid.rb CHANGED
@@ -7,7 +7,7 @@ require 'deep_merge'
7
7
  ##
8
8
  class String
9
9
  def cap_first
10
- self.sub(/^\w/) do |m|
10
+ sub(/^\w/) do |m|
11
11
  m.upcase
12
12
  end
13
13
  end
@@ -17,21 +17,21 @@ class String
17
17
  ##
18
18
  ## @param opt (Hash) Additional Options
19
19
  ##
20
- def link_urls(opt={})
20
+ def link_urls(opt = {})
21
21
  opt[:format] ||= :html
22
22
  if opt[:format] == :html
23
- self.gsub(/(?mi)((http|https):\/\/)?([\w\-_]+(\.[\w\-_]+)+)([\w\-\.,\@?^=%&:\/~\+#]*[\w\-\@^=%&\/~\+#])?/) {|match|
23
+ gsub(%r{(?mi)((http|https)://)?([\w\-_]+(\.[\w\-_]+)+)([\w\-.,@?^=%&:/~+#]*[\w\-@^=%&/~+#])?}) do |_match|
24
24
  m = Regexp.last_match
25
- proto = m[1].nil? ? "http://" : ""
26
- %Q{<a href="#{proto}#{m[0]}" title="Link to #{m[0]}">[#{m[3]}]</a>}
27
- }.gsub(/\<(\w+:.*?)\>/) {|match|
25
+ proto = m[1].nil? ? 'http://' : ''
26
+ %(<a href="#{proto}#{m[0]}" title="Link to #{m[0]}">[#{m[3]}]</a>)
27
+ end.gsub(/<(\w+:.*?)>/) do |match|
28
28
  m = Regexp.last_match
29
- unless m[1] =~ /<a href/
30
- %Q{<a href="#{m[1]}" title="Link to #{m[1]}">[link]</a>}
31
- else
29
+ if m[1] =~ /<a href/
32
30
  match
31
+ else
32
+ %(<a href="#{m[1]}" title="Link to #{m[1]}">[link]</a>)
33
33
  end
34
- }
34
+ end
35
35
  else
36
36
  self
37
37
  end
@@ -42,7 +42,8 @@ end
42
42
  ## @brief Main "What Was I Doing" methods
43
43
  ##
44
44
  class WWID
45
- attr_accessor :content, :sections, :current_section, :doing_file, :config, :user_home, :default_config_file, :config_file, :results, :auto_tag
45
+ attr_accessor :content, :sections, :current_section, :doing_file, :config, :user_home, :default_config_file,
46
+ :config_file, :results, :auto_tag
46
47
 
47
48
  ##
48
49
  ## @brief Initializes the object.
@@ -62,16 +63,13 @@ class WWID
62
63
  ## @return (String) A file path
63
64
  ##
64
65
  def find_local_config
65
-
66
66
  config = {}
67
67
  dir = Dir.pwd
68
68
 
69
69
  local_config_files = []
70
70
 
71
- while (dir != '/' && (dir =~ /[A-Z]:\//) == nil)
72
- if File.exists? File.join(dir, @default_config_file)
73
- local_config_files.push(File.join(dir, @default_config_file))
74
- end
71
+ while dir != '/' && (dir =~ %r{[A-Z]:/}).nil?
72
+ local_config_files.push(File.join(dir, @default_config_file)) if File.exist? File.join(dir, @default_config_file)
75
73
 
76
74
  dir = File.dirname(dir)
77
75
  end
@@ -82,32 +80,30 @@ class WWID
82
80
  ##
83
81
  ## @brief Reads a configuration.
84
82
  ##
85
- def read_config(opt={})
86
- unless @config_file
87
- if Dir.respond_to?('home')
88
- @config_file = File.join(Dir.home, @default_config_file)
89
- else
90
- @config_file = File.join(File.expand_path("~"), @default_config_file)
91
- end
92
- end
83
+ def read_config(opt = {})
84
+ @config_file ||= if Dir.respond_to?('home')
85
+ File.join(Dir.home, @default_config_file)
86
+ else
87
+ File.join(File.expand_path('~'), @default_config_file)
88
+ end
93
89
 
94
- if opt[:ignore_local]
95
- additional_configs = []
96
- else
97
- additional_configs = find_local_config
98
- end
90
+ additional_configs = if opt[:ignore_local]
91
+ []
92
+ else
93
+ find_local_config
94
+ end
99
95
 
100
96
  begin
101
97
  @local_config = {}
102
98
 
103
- @config = YAML.load_file(@config_file) || {} if File.exists?(@config_file)
104
- additional_configs.each { |cfg|
99
+ @config = YAML.load_file(@config_file) || {} if File.exist?(@config_file)
100
+ additional_configs.each do |cfg|
105
101
  new_config = YAML.load_file(cfg) || {} if cfg
106
102
  @local_config = @local_config.deep_merge(new_config)
107
- }
103
+ end
108
104
 
109
105
  # @config.deep_merge(@local_config)
110
- rescue
106
+ rescue StandardError
111
107
  @config = {}
112
108
  @local_config = {}
113
109
  # raise "error reading config"
@@ -119,24 +115,20 @@ class WWID
119
115
  ##
120
116
  ## @param opt (Hash) Additional Options
121
117
  ##
122
- def configure(opt={})
118
+ def configure(opt = {})
123
119
  @timers = {}
124
120
  opt[:ignore_local] ||= false
125
121
 
126
- unless @config_file
127
- @config_file = File.join(@user_home, @default_config_file)
128
- end
129
-
130
- read_config({:ignore_local => opt[:ignore_local]})
122
+ @config_file ||= File.join(@user_home, @default_config_file)
131
123
 
132
- user_config = @config.dup
124
+ read_config({ ignore_local: opt[:ignore_local] })
133
125
 
134
126
  @config = {} if @config.nil?
135
127
 
136
128
  @config['autotag'] ||= {}
137
129
  @config['autotag']['whitelist'] ||= []
138
130
  @config['autotag']['synonyms'] ||= {}
139
- @config['doing_file'] ||= "~/what_was_i_doing.md"
131
+ @config['doing_file'] ||= '~/what_was_i_doing.md'
140
132
  @config['current_section'] ||= 'Currently'
141
133
  @config['editor_app'] ||= nil
142
134
 
@@ -177,12 +169,12 @@ class WWID
177
169
  'tags_bool' => 'OR'
178
170
  },
179
171
  'color' => {
180
- 'date_format' => '%F %_I:%M%P',
181
- 'template' => '%boldblack%date %boldgreen| %boldwhite%title%default%note',
182
- 'wrap_width' => 0,
183
- 'section' => 'Currently',
184
- 'count' => 10,
185
- 'order' => "asc"
172
+ 'date_format' => '%F %_I:%M%P',
173
+ 'template' => '%boldblack%date %boldgreen| %boldwhite%title%default%note',
174
+ 'wrap_width' => 0,
175
+ 'section' => 'Currently',
176
+ 'count' => 10,
177
+ 'order' => 'asc'
186
178
  }
187
179
  }
188
180
  @config['marker_tag'] ||= 'flagged'
@@ -202,17 +194,13 @@ class WWID
202
194
  # end
203
195
  # end
204
196
 
205
- unless File.exists?(@config_file)
206
- File.open(@config_file, 'w') { |yf| YAML::dump(@config, yf) }
207
- end
197
+ File.open(@config_file, 'w') { |yf| YAML.dump(@config, yf) } unless File.exist?(@config_file)
208
198
 
209
199
  @config = @local_config.deep_merge(@config)
210
200
 
211
201
  @current_section = @config['current_section']
212
202
  @default_template = @config['templates']['default']['template']
213
- @default_date_format = @config['templates']['default']['date_format'];
214
-
215
-
203
+ @default_date_format = @config['templates']['default']['date_format']
216
204
  end
217
205
 
218
206
  ##
@@ -220,16 +208,16 @@ class WWID
220
208
  ##
221
209
  ## @param path (String) Override path to a doing file, optional
222
210
  ##
223
- def init_doing_file(path=nil)
211
+ def init_doing_file(path = nil)
224
212
  @doing_file = File.expand_path(@config['doing_file'])
225
213
 
226
214
  input = path
227
215
 
228
216
  if input.nil?
229
- create(@doing_file) unless File.exists?(@doing_file)
217
+ create(@doing_file) unless File.exist?(@doing_file)
230
218
  input = IO.read(@doing_file)
231
219
  input = input.force_encoding('utf-8') if input.respond_to? :force_encoding
232
- elsif File.exists?(File.expand_path(input)) && File.file?(File.expand_path(input)) && File.stat(File.expand_path(input)).size > 0
220
+ elsif File.exist?(File.expand_path(input)) && File.file?(File.expand_path(input)) && File.stat(File.expand_path(input)).size.positive?
233
221
  @doing_file = File.expand_path(input)
234
222
  input = IO.read(File.expand_path(input))
235
223
  input = input.force_encoding('utf-8') if input.respond_to? :force_encoding
@@ -243,40 +231,36 @@ class WWID
243
231
  @other_content_top = []
244
232
  @other_content_bottom = []
245
233
 
246
- section = "Uncategorized"
234
+ section = 'Uncategorized'
247
235
  lines = input.split(/[\n\r]/)
248
236
  current = 0
249
237
 
250
- lines.each {|line|
238
+ lines.each do |line|
251
239
  next if line =~ /^\s*$/
240
+
252
241
  if line =~ /^(\S[\S ]+):\s*(@\S+\s*)*$/
253
- section = $1
242
+ section = Regexp.last_match(1)
254
243
  @content[section] = {}
255
244
  @content[section]['original'] = line
256
245
  @content[section]['items'] = []
257
246
  current = 0
258
247
  elsif line =~ /^\s*- (\d{4}-\d\d-\d\d \d\d:\d\d) \| (.*)/
259
- date = Time.parse($1)
260
- title = $2
261
- @content[section]['items'].push({'title' => title, 'date' => date, 'section' => section})
248
+ date = Time.parse(Regexp.last_match(1))
249
+ title = Regexp.last_match(2)
250
+ @content[section]['items'].push({ 'title' => title, 'date' => date, 'section' => section })
262
251
  current += 1
263
- else
252
+ elsif current.zero?
264
253
  # if content[section]['items'].length - 1 == current
265
- if current == 0
266
- @other_content_top.push(line)
267
- else
268
- if line =~ /^\S/
269
- @other_content_bottom.push(line)
270
- else
271
- unless @content[section]['items'][current - 1].has_key? 'note'
272
- @content[section]['items'][current - 1]['note'] = []
273
- end
274
- @content[section]['items'][current - 1]['note'].push(line.gsub(/ *$/,''))
275
- end
276
- end
254
+ @other_content_top.push(line)
255
+ elsif line =~ /^\S/
256
+ @other_content_bottom.push(line)
257
+ else
258
+ @content[section]['items'][current - 1]['note'] = [] unless @content[section]['items'][current - 1].key? 'note'
259
+
260
+ @content[section]['items'][current - 1]['note'].push(line.gsub(/ *$/, ''))
277
261
  # end
278
262
  end
279
- }
263
+ end
280
264
  end
281
265
 
282
266
  ##
@@ -300,14 +284,12 @@ class WWID
300
284
  ##
301
285
  ## @brief Create a new doing file
302
286
  ##
303
- def create(filename=nil)
304
- if filename.nil?
305
- filename = @doing_file
306
- end
307
- unless File.exists?(filename) && File.stat(filename).size > 0
308
- File.open(filename,'w+') do |f|
309
- f.puts @current_section + ":"
310
- end
287
+ def create(filename = nil)
288
+ filename = @doing_file if filename.nil?
289
+ return if File.exist?(filename) && File.stat(filename).size.positive?
290
+
291
+ File.open(filename, 'w+') do |f|
292
+ f.puts "#{@current_section}:"
311
293
  end
312
294
  end
313
295
 
@@ -316,22 +298,26 @@ class WWID
316
298
  ##
317
299
  ## @param input (String) Text input for editor
318
300
  ##
319
- def fork_editor(input="")
320
- tmpfile = Tempfile.new(['doing','.md'])
301
+ def fork_editor(input = '')
302
+ tmpfile = Tempfile.new(['doing', '.md'])
321
303
 
322
- File.open(tmpfile.path,'w+') do |f|
304
+ File.open(tmpfile.path, 'w+') do |f|
323
305
  f.puts input
324
306
  f.puts "\n# The first line is the entry title, any lines after that are added as a note"
325
307
  end
326
308
 
327
309
  pid = Process.fork { system("$EDITOR #{tmpfile.path}") }
328
310
 
329
- trap("INT") {
330
- Process.kill(9, pid) rescue Errno::ESRCH
311
+ trap('INT') do
312
+ begin
313
+ Process.kill(9, pid)
314
+ rescue StandardError
315
+ Errno::ESRCH
316
+ end
331
317
  tmpfile.unlink
332
318
  tmpfile.close!
333
319
  exit 0
334
- }
320
+ end
335
321
 
336
322
  Process.wait(pid)
337
323
 
@@ -339,7 +325,7 @@ class WWID
339
325
  if $?.exitstatus == 0
340
326
  input = IO.read(tmpfile.path)
341
327
  else
342
- raise "Cancelled"
328
+ raise 'Cancelled'
343
329
  end
344
330
  ensure
345
331
  tmpfile.close
@@ -357,15 +343,13 @@ class WWID
357
343
  # @param input (String) The string to parse
358
344
  #
359
345
  def format_input(input)
360
- raise "No content in entry" if input.nil? || input.strip.length == 0
346
+ raise 'No content in entry' if input.nil? || input.strip.empty?
347
+
361
348
  input_lines = input.split(/[\n\r]+/)
362
349
  title = input_lines[0].strip
363
350
  note = input_lines.length > 1 ? input_lines[1..-1] : []
364
- note.map! { |line|
365
- line.strip
366
- }.delete_if { |line|
367
- line =~ /^\s*$/ || line =~ /^#/
368
- }
351
+ note.map!(&:strip)
352
+ note.delete_if { |line| line =~ /^\s*$/ || line =~ /^#/ }
369
353
 
370
354
  [title, note]
371
355
  end
@@ -383,21 +367,22 @@ class WWID
383
367
  #
384
368
  def chronify(input)
385
369
  now = Time.now
386
- raise "Invalid time expression #{input.inspect}" if input.to_s.strip == ""
387
- secs_ago = if input.match /^(\d+)$/
388
- # plain number, assume minutes
389
- $1.to_i * 60
390
- elsif (m = input.match /^((?<day>\d+)d)?((?<hour>\d+)h)?((?<min>\d+)m)?$/i)
391
- # day/hour/minute format e.g. 1d2h30m
392
- [[m['day'], 24*3600],
393
- [m['hour'], 3600],
394
- [m['min'], 60]].map {|qty, secs| qty ? (qty.to_i * secs) : 0 }.reduce(0, :+)
395
- end
370
+ raise "Invalid time expression #{input.inspect}" if input.to_s.strip == ''
371
+
372
+ secs_ago = if input.match(/^(\d+)$/)
373
+ # plain number, assume minutes
374
+ Regexp.last_match(1).to_i * 60
375
+ elsif (m = input.match(/^(?:(?<day>\d+)d)?(?:(?<hour>\d+)h)?(?:(?<min>\d+)m)?$/i))
376
+ # day/hour/minute format e.g. 1d2h30m
377
+ [[m['day'], 24 * 3600],
378
+ [m['hour'], 3600],
379
+ [m['min'], 60]].map { |qty, secs| qty ? (qty.to_i * secs) : 0 }.reduce(0, :+)
380
+ end
396
381
 
397
382
  if secs_ago
398
383
  now - secs_ago
399
384
  else
400
- Chronic.parse(input, {:context => :past, :ambiguous_time_range => 8})
385
+ Chronic.parse(input, { context: :past, ambiguous_time_range: 8 })
401
386
  end
402
387
  end
403
388
 
@@ -412,23 +397,24 @@ class WWID
412
397
  #
413
398
  def chronify_qty(qty)
414
399
  minutes = 0
415
- if qty.strip =~ /^(\d+):(\d\d)$/
416
- minutes += $1.to_i * 60
417
- minutes += $2.to_i
418
- elsif qty.strip =~ /^(\d+(?:\.\d+)?)([hmd])?$/
419
- amt = $1
420
- type = $2.nil? ? "m" : $2
400
+ case qty.strip
401
+ when /^(\d+):(\d\d)$/
402
+ minutes += Regexp.last_match(1).to_i * 60
403
+ minutes += Regexp.last_match(2).to_i
404
+ when /^(\d+(?:\.\d+)?)([hmd])?$/
405
+ amt = Regexp.last_match(1)
406
+ type = Regexp.last_match(2).nil? ? 'm' : Regexp.last_match(2)
421
407
 
422
408
  minutes = case type.downcase
423
- when 'm'
424
- amt.to_i
425
- when 'h'
426
- (amt.to_f * 60).round
427
- when 'd'
428
- (amt.to_f * 60 * 24).round
429
- else
430
- minutes
431
- end
409
+ when 'm'
410
+ amt.to_i
411
+ when 'h'
412
+ (amt.to_f * 60).round
413
+ when 'd'
414
+ (amt.to_f * 60 * 24).round
415
+ else
416
+ minutes
417
+ end
432
418
  end
433
419
  minutes * 60
434
420
  end
@@ -448,8 +434,8 @@ class WWID
448
434
  ## @param title (String) The new section title
449
435
  ##
450
436
  def add_section(title)
451
- @content[title.cap_first] = {'original' => "#{title}:", 'items' => []}
452
- @results.push(%Q{Added section "#{title.cap_first}"})
437
+ @content[title.cap_first] = { 'original' => "#{title}:", 'items' => [] }
438
+ @results.push(%(Added section "#{title.cap_first}"))
453
439
  end
454
440
 
455
441
  ##
@@ -458,32 +444,32 @@ class WWID
458
444
  ## @param frag (String) The user-provided string
459
445
  ## @param guessed (Boolean) already guessed and failed
460
446
  ##
461
- def guess_section(frag,guessed=false)
462
- return "All" if frag =~ /all/i
463
- sections.each {|section| return section.cap_first if frag.downcase == section.downcase }
447
+ def guess_section(frag, guessed: false)
448
+ return 'All' if frag =~ /all/i
449
+
450
+ sections.each { |section| return section.cap_first if frag.downcase == section.downcase }
464
451
  section = false
465
- re = frag.split('').join(".*?")
466
- sections.each {|sect|
467
- if sect =~ /#{re}/i
468
- $stderr.puts "Assuming you meant #{sect}"
469
- section = sect
470
- break
471
- end
472
- }
452
+ re = frag.split('').join('.*?')
453
+ sections.each do |sect|
454
+ next unless sect =~ /#{re}/i
455
+
456
+ warn "Assuming you meant #{sect}"
457
+ section = sect
458
+ break
459
+ end
473
460
  unless section || guessed
474
- alt = guess_view(frag,true)
475
- if alt
476
- raise "Did you mean `doing view #{alt}`?"
477
- else
478
- res = yn("Section #{frag} not found, create it",false)
461
+ alt = guess_view(frag, true)
462
+ raise "Did you mean `doing view #{alt}`?" if alt
479
463
 
480
- if res
481
- add_section(frag.cap_first)
482
- write(@doing_file)
483
- return frag.cap_first
484
- end
485
- raise "Unknown section: #{frag}"
464
+ res = yn("Section #{frag} not found, create it", default_response: false)
465
+
466
+ if res
467
+ add_section(frag.cap_first)
468
+ write(@doing_file)
469
+ return frag.cap_first
486
470
  end
471
+
472
+ raise "Unknown section: #{frag}"
487
473
  end
488
474
  section ? section.cap_first : guessed
489
475
  end
@@ -496,37 +482,31 @@ class WWID
496
482
  ##
497
483
  ## @return (Bool) yes or no
498
484
  ##
499
- def yn(question, default_response=false)
500
- if default_response
501
- default = 'y'
502
- else
503
- default = 'n'
504
- end
485
+ def yn(question, default_response: false)
486
+ default = default_response ? 'y' : 'n'
487
+
505
488
  # if this isn't an interactive shell, answer default
506
- unless $stdout.isatty
507
- if default.downcase == 'y'
508
- return true
509
- else
510
- return false
511
- end
512
- end
489
+ return default.downcase == 'y' unless $stdout.isatty
490
+
513
491
  # clear the buffer
514
- if ARGV.length
492
+ if ARGV&.length
515
493
  ARGV.length.times do
516
494
  ARGV.shift
517
495
  end
518
496
  end
519
497
  system 'stty cbreak'
520
- if default
521
- if default =~ /y/i
522
- options = "#{colors['white']}[#{colors['boldgreen']}Y#{colors['white']}/#{colors['boldwhite']}n#{colors['white']}]#{colors['default']}"
523
- else
524
- options = "#{colors['white']}[#{colors['boldwhite']}y#{colors['white']}/#{colors['boldgreen']}N#{colors['white']}]#{colors['default']}"
525
- end
526
- else
527
- options = "#{colors['white']}[#{colors['boldwhite']}y#{colors['white']}/#{colors['boldwhite']}n#{colors['white']}]#{colors['default']}"
528
- end
529
- $stdout.syswrite "#{colors['boldwhite']}#{question.sub(/\?$/,'')} #{options}#{colors['boldwhite']}?#{colors['default']} "
498
+
499
+ cw = colors['white']
500
+ cbw = colors['boldwhite']
501
+ cbg = colors['boldgreen']
502
+ cd = colors['default']
503
+
504
+ options = if default
505
+ default =~ /y/i ? "#{cw}[#{cbg}Y#{cw}/#{cbw}n#{cw}]#{cd}" : "#{cw}[#{cbw}y#{cw}/#{cbg}N#{cw}]#{cd}"
506
+ else
507
+ "#{cw}[#{cbw}y#{cw}/#{cbw}n#{cw}]#{cd}"
508
+ end
509
+ $stdout.syswrite "#{cbw}#{question.sub(/\?$/, '')} #{options}#{cbw}?#{cd} "
530
510
  res = $stdin.sysread 1
531
511
  puts
532
512
  system 'stty cooked'
@@ -534,9 +514,9 @@ class WWID
534
514
  res.chomp!
535
515
  res.downcase!
536
516
 
537
- res = default.downcase if res == ""
517
+ res = default.downcase if res == ''
538
518
 
539
- return res =~ /y/i
519
+ res =~ /y/i
540
520
  end
541
521
 
542
522
  ##
@@ -545,19 +525,19 @@ class WWID
545
525
  ## @param frag (String) The user-provided string
546
526
  ## @param guessed (Boolean) already guessed
547
527
  ##
548
- def guess_view(frag,guessed=false)
549
- views.each {|view| return view if frag.downcase == view.downcase}
528
+ def guess_view(frag, guessed = false)
529
+ views.each { |view| return view if frag.downcase == view.downcase }
550
530
  view = false
551
- re = frag.split('').join(".*?")
552
- views.each {|v|
553
- if v =~ /#{re}/i
554
- $stderr.puts "Assuming you meant #{v}"
555
- view = v
556
- break
557
- end
558
- }
531
+ re = frag.split('').join('.*?')
532
+ views.each do |v|
533
+ next unless v =~ /#{re}/i
534
+
535
+ warn "Assuming you meant #{v}"
536
+ view = v
537
+ break
538
+ end
559
539
  unless view || guessed
560
- alt = guess_section(frag,true)
540
+ alt = guess_section(frag, guessed: true)
561
541
  if alt
562
542
  raise "Did you mean `doing show #{alt}`?"
563
543
  else
@@ -574,7 +554,7 @@ class WWID
574
554
  ## @param section (String) The section to add to
575
555
  ## @param opt (Hash) Additional Options {:date, :note, :back, :timed}
576
556
  ##
577
- def add_item(title,section=nil,opt={})
557
+ def add_item(title, section = nil, opt = {})
578
558
  section ||= @current_section
579
559
  add_section(section) unless @content.has_key?(section)
580
560
  opt[:date] ||= Time.now
@@ -582,7 +562,7 @@ class WWID
582
562
  opt[:back] ||= Time.now
583
563
  opt[:timed] ||= false
584
564
 
585
- opt[:note] = [opt[:note]] if opt[:note].class == String
565
+ opt[:note] = [opt[:note]] if opt[:note].instance_of?(String)
586
566
 
587
567
  title = [title.strip.cap_first]
588
568
  title = title.join(' ')
@@ -590,39 +570,37 @@ class WWID
590
570
  if @auto_tag
591
571
  title = autotag(title)
592
572
  unless @config['default_tags'].empty?
593
- title += @config['default_tags'].map{|t|
594
- unless t.nil?
595
- dt = t.sub(/^ *@/,'').chomp
596
- if title =~ /@#{dt}/
597
- ""
598
- else
599
- ' @' + dt
600
- end
573
+ default_tags = @config['default_tags'].map do |t|
574
+ next if t.nil?
575
+
576
+ dt = t.sub(/^ *@/, '').chomp
577
+ if title =~ /@#{dt}/
578
+ ''
579
+ else
580
+ " @#{dt}"
601
581
  end
602
- }.delete_if {|t| t == "" }.join(" ")
582
+ end
583
+ default_tags.delete_if { |t| t == '' }
584
+ title += default_tags.join(' ')
603
585
  end
604
586
  end
605
- title.gsub!(/ +/,' ')
606
- entry = {'title' => title.strip, 'date' => opt[:back]}
607
- unless opt[:note].join('').strip == ''
608
- entry['note'] = opt[:note].map {|n| n.chomp}
609
- end
587
+ title.gsub!(/ +/, ' ')
588
+ entry = { 'title' => title.strip, 'date' => opt[:back] }
589
+ entry['note'] = opt[:note].map(&:chomp) unless opt[:note].join('').strip == ''
610
590
  items = @content[section]['items']
611
591
  if opt[:timed]
612
592
  items.reverse!
613
- items.each_with_index {|i,x|
614
- if i['title'] =~ / @done/
615
- next
616
- else
617
- items[x]['title'] = "#{i['title']} @done(#{opt[:back].strftime('%F %R')})"
618
- break
619
- end
620
- }
593
+ items.each_with_index do |i, x|
594
+ next if i['title'] =~ / @done/
595
+
596
+ items[x]['title'] = "#{i['title']} @done(#{opt[:back].strftime('%F %R')})"
597
+ break
598
+ end
621
599
  items.reverse!
622
600
  end
623
601
  items.push(entry)
624
602
  @content[section]['items'] = items
625
- @results.push(%Q{Added "#{entry['title']}" to #{section}})
603
+ @results.push(%(Added "#{entry['title']}" to #{section}))
626
604
  end
627
605
 
628
606
  ##
@@ -631,27 +609,23 @@ class WWID
631
609
  ## @param section (String) The section to retrieve from, default
632
610
  ## All
633
611
  ##
634
- def last_note(section="All")
612
+ def last_note(section = 'All')
635
613
  section = guess_section(section)
636
614
  if section =~ /^all$/i
637
- combined = {'items' => []}
638
- @content.each {|k,v|
615
+ combined = { 'items' => [] }
616
+ @content.each do |_k, v|
639
617
  combined['items'] += v['items']
640
- }
641
- section = combined['items'].dup.sort_by{|item| item['date'] }.reverse[0]['section']
642
- end
643
-
644
- if @content.has_key?(section)
645
- last_item = @content[section]['items'].dup.sort_by{|item| item['date'] }.reverse[0]
646
- $stderr.puts "Editing note for #{last_item['title']}"
647
- note = ''
648
- unless last_item['note'].nil?
649
- note = last_item['note'].map{|line| line.strip }.join("\n")
650
618
  end
651
- return "#{last_item['title']}\n# EDIT BELOW THIS LINE ------------\n#{note}"
652
- else
653
- raise "Section #{section} not found"
619
+ section = combined['items'].dup.sort_by { |item| item['date'] }.reverse[0]['section']
654
620
  end
621
+
622
+ raise "Section #{section} not found" unless @content.key?(section)
623
+
624
+ last_item = @content[section]['items'].dup.sort_by { |item| item['date'] }.reverse[0]
625
+ warn "Editing note for #{last_item['title']}"
626
+ note = ''
627
+ note = last_item['note'].map(&:strip).join("\n") unless last_item['note'].nil?
628
+ "#{last_item['title']}\n# EDIT BELOW THIS LINE ------------\n#{note}"
655
629
  end
656
630
 
657
631
  ##
@@ -665,12 +639,12 @@ class WWID
665
639
 
666
640
  last = last_entry(opt)
667
641
  if last.nil?
668
- @results.push(%Q{No previous entry found})
642
+ @results.push(%(No previous entry found))
669
643
  return
670
644
  end
671
645
  # Remove @done tag
672
646
  title = last['title'].sub(/\s*@done(\(.*?\))?/, '').chomp
673
- add_item(title, last['section'], {:note => opt[:note], :back => opt[:date], :timed => true})
647
+ add_item(title, last['section'], { note: opt[:note], back: opt[:date], timed: true })
674
648
  write(@doing_file)
675
649
  end
676
650
 
@@ -679,20 +653,20 @@ class WWID
679
653
  ##
680
654
  ## @param opt (Hash) Additional Options
681
655
  ##
682
- def last_entry(opt={})
656
+ def last_entry(opt = {})
683
657
  opt[:section] ||= @current_section
684
658
 
685
659
  sec_arr = []
686
660
 
687
661
  if opt[:section].nil?
688
662
  sec_arr = [@current_section]
689
- elsif opt[:section].class == String
663
+ elsif opt[:section].instance_of?(String)
690
664
  if opt[:section] =~ /^all$/i
691
- combined = {'items' => []}
692
- @content.each {|k,v|
665
+ combined = { 'items' => [] }
666
+ @content.each do |_k, v|
693
667
  combined['items'] += v['items']
694
- }
695
- items = combined['items'].dup.sort_by{|item| item['date'] }.reverse
668
+ end
669
+ items = combined['items'].dup.sort_by { |item| item['date'] }.reverse
696
670
  sec_arr.push(items[0]['section'])
697
671
 
698
672
  sec_arr = sections
@@ -701,13 +675,12 @@ class WWID
701
675
  end
702
676
  end
703
677
 
704
-
705
678
  all_items = []
706
679
  sec_arr.each do |section|
707
- all_items.concat(@content[section]['items'].dup) if @content.has_key?(section)
680
+ all_items.concat(@content[section]['items'].dup) if @content.key?(section)
708
681
  end
709
682
 
710
- all_items.sort_by { |item| item['date'] }.last
683
+ all_items.max_by { |item| item['date'] }
711
684
  end
712
685
 
713
686
  ##
@@ -715,33 +688,32 @@ class WWID
715
688
  ##
716
689
  ## @param opt (Hash) Additional Options
717
690
  ##
718
- def tag_last(opt={})
691
+ def tag_last(opt = {})
719
692
  opt[:section] ||= @current_section
720
693
  opt[:count] ||= 1
721
694
  opt[:archive] ||= false
722
- opt[:tags] ||= ["done"]
695
+ opt[:tags] ||= ['done']
723
696
  opt[:sequential] ||= false
724
697
  opt[:date] ||= false
725
698
  opt[:remove] ||= false
726
699
  opt[:autotag] ||= false
727
700
  opt[:back] ||= false
728
701
 
729
-
730
702
  sec_arr = []
731
703
 
732
704
  if opt[:section].nil?
733
705
  sec_arr = [@current_section]
734
- elsif opt[:section].class == String
706
+ elsif opt[:section].instance_of?(String)
735
707
  if opt[:section] =~ /^all$/i
736
708
  if opt[:count] == 1
737
- combined = {'items' => []}
738
- @content.each {|k,v|
709
+ combined = { 'items' => [] }
710
+ @content.each do |_k, v|
739
711
  combined['items'] += v['items']
740
- }
741
- items = combined['items'].dup.sort_by{|item| item['date'] }.reverse
712
+ end
713
+ items = combined['items'].dup.sort_by { |item| item['date'] }.reverse
742
714
  sec_arr.push(items[0]['section'])
743
715
  elsif opt[:count] > 1
744
- raise "A count greater than one requires a section to be specified"
716
+ raise 'A count greater than one requires a section to be specified'
745
717
  else
746
718
  sec_arr = sections
747
719
  end
@@ -750,21 +722,27 @@ class WWID
750
722
  end
751
723
  end
752
724
 
725
+ sec_arr.each do |section|
726
+ if @content.key?(section)
753
727
 
754
-
755
- sec_arr.each {|section|
756
- if @content.has_key?(section)
757
-
758
- items = @content[section]['items'].dup.sort_by{|item| item['date'] }.reverse
728
+ items = @content[section]['items'].dup.sort_by { |item| item['date'] }.reverse
759
729
 
760
730
  index = 0
761
731
  done_date = Time.now
762
732
  next_start = Time.now
763
- count = opt[:count] == 0 ? items.length : opt[:count]
764
- items.map! {|item|
733
+ count = (opt[:count]).zero? ? items.length : opt[:count]
734
+ items.map! do |item|
765
735
  break if index == count
766
736
 
767
- unless opt[:autotag]
737
+ if opt[:autotag]
738
+ new_title = autotag(item['title']) if @auto_tag
739
+ if new_title == item['title']
740
+ @results.push(%(Autotag: No changes))
741
+ else
742
+ @results.push("Tags updated: #{new_title}")
743
+ item['title'] = new_title
744
+ end
745
+ else
768
746
  if opt[:sequential]
769
747
  done_date = next_start - 1
770
748
  next_start = item['date']
@@ -775,62 +753,50 @@ class WWID
775
753
  end
776
754
 
777
755
  title = item['title']
778
- opt[:tags].each {|tag|
756
+ opt[:tags].each do |tag|
779
757
  tag.strip!
780
- if opt[:remove]
781
- if title =~ /@#{tag}\b/
782
- title.gsub!(/(^| )@#{tag}(\([^\)]*\))?/,'')
783
- @results.push(%Q{Removed @#{tag}: "#{title}" in #{section}})
784
- end
785
- else
786
- unless title =~ /@#{tag}/
787
- title.chomp!
788
- if opt[:date]
789
- title += " @#{tag}(#{done_date.strftime('%F %R')})"
790
- else
791
- title += " @#{tag}"
792
- end
793
- @results.push(%Q{Added @#{tag}: "#{title}" in #{section}})
794
- end
758
+ if opt[:remove] && title =~ /@#{tag}\b/
759
+ title.gsub!(/(^| )@#{tag}(\([^)]*\))?/, '')
760
+ @results.push(%(Removed @#{tag}: "#{title}" in #{section}))
761
+ elsif title !~ /@#{tag}/
762
+ title.chomp!
763
+ title += if opt[:date]
764
+ " @#{tag}(#{done_date.strftime('%F %R')})"
765
+ else
766
+ " @#{tag}"
767
+ end
768
+ @results.push(%(Added @#{tag}: "#{title}" in #{section}))
795
769
  end
796
- }
797
- item['title'] = title
798
- else
799
- new_title = autotag(item['title']) if @auto_tag
800
- unless new_title == item['title']
801
- @results.push("Tags updated: #{new_title}")
802
- item['title'] = new_title
803
- else
804
- @results.push(%Q{Autotag: No changes})
805
770
  end
771
+ item['title'] = title
806
772
  end
807
773
 
808
774
  index += 1
809
775
 
810
776
  item
811
- }
777
+ end
812
778
 
813
779
  @content[section]['items'] = items
814
780
 
815
- if opt[:archive] && section != "Archive" && opt[:count] > 0
781
+ if opt[:archive] && section != 'Archive' && (opt[:count]).positive?
816
782
  # concat [count] items from [section] and archive section
817
- archived = @content[section]['items'][0..opt[:count]-1].map {|i|
818
- i['title'].sub(/(?:@from\(.*?\))?(.*)$/,"\\1 @from(#{i['section']})")
819
- }.concat(@content['Archive']['items'])
783
+ archived = @content[section]['items'][0..opt[:count] - 1].map do |i|
784
+ i['title'].sub(/(?:@from\(.*?\))?(.*)$/, "\\1 @from(#{i['section']})")
785
+ end.concat(@content['Archive']['items'])
820
786
  # chop [count] items off of [section] items
821
787
  @content[opt[:section]]['items'] = @content[opt[:section]]['items'][opt[:count]..-1]
822
788
  # overwrite archive section with concatenated array
823
789
  @content['Archive']['items'] = archived
824
790
  # log it
825
- result = opt[:count] == 1 ? "1 entry" : "#{opt[:count]} entries"
791
+ result = opt[:count] == 1 ? '1 entry' : "#{opt[:count]} entries"
826
792
  @results.push("Archived #{result} from #{section}")
827
- elsif opt[:archive] && opt[:count] == 0
828
- @results.push("Archiving is skipped when operating on all entries") if opt[:count] == 0
793
+ elsif opt[:archive] && (opt[:count]).zero?
794
+ @results.push('Archiving is skipped when operating on all entries') if (opt[:count]).zero?
829
795
  end
830
796
  else
831
797
  raise "Section not found: #{section}"
832
798
  end
833
- }
799
+ end
834
800
 
835
801
  write(@doing_file)
836
802
  end
@@ -842,49 +808,47 @@ class WWID
842
808
  ## @param note (String) The note to add
843
809
  ## @param replace (Bool) Should replace existing note
844
810
  ##
845
- def note_last(section, note, replace=false)
811
+ def note_last(section, note, replace: false)
846
812
  section = guess_section(section)
847
813
 
848
814
  if section =~ /^all$/i
849
- combined = {'items' => []}
850
- @content.each {|k,v|
815
+ combined = { 'items' => [] }
816
+ @content.each do |_k, v|
851
817
  combined['items'] += v['items']
852
- }
853
- section = combined['items'].dup.sort_by{|item| item['date'] }.reverse[0]['section']
818
+ end
819
+ section = combined['items'].dup.sort_by { |item| item['date'] }.reverse[0]['section']
854
820
  end
855
821
 
856
- if @content.has_key?(section)
857
- # sort_section(opt[:section])
858
- items = @content[section]['items'].dup.sort_by{|item| item['date'] }.reverse
859
-
860
- current_note = items[0]['note']
861
- current_note = [] if current_note.nil?
862
- title = items[0]['title']
863
- if replace
864
- items[0]['note'] = note
865
- if note.empty? && !current_note.empty?
866
- @results.push(%Q{Removed note from "#{title}"})
867
- elsif current_note.length > 0 && note.length > 0
868
- @results.push(%Q{Replaced note from "#{title}"})
869
- elsif note.length > 0
870
- @results.push(%Q{Added note to "#{title}"})
871
- else
872
- @results.push(%Q{Entry "#{title}" has no note})
873
- end
874
- elsif current_note.class == Array
875
- items[0]['note'] = current_note.concat(note)
876
- @results.push(%Q{Added note to "#{title}"}) if note.length > 0
822
+ raise "Section #{section} not found" unless @content.key?(section)
823
+
824
+ # sort_section(opt[:section])
825
+ items = @content[section]['items'].dup.sort_by { |item| item['date'] }.reverse
826
+
827
+ current_note = items[0]['note']
828
+ current_note = [] if current_note.nil?
829
+ title = items[0]['title']
830
+ if replace
831
+ items[0]['note'] = note
832
+ if note.empty? && !current_note.empty?
833
+ @results.push(%(Removed note from "#{title}"))
834
+ elsif !current_note.empty? && !note.empty?
835
+ @results.push(%(Replaced note from "#{title}"))
836
+ elsif !note.empty?
837
+ @results.push(%(Added note to "#{title}"))
877
838
  else
878
- items[0]['note'] = note
879
- @results.push(%Q{Added note to "#{title}"}) if note.length > 0
839
+ @results.push(%(Entry "#{title}" has no note))
880
840
  end
881
-
882
- @content[section]['items'] = items
841
+ elsif current_note.instance_of?(Array)
842
+ items[0]['note'] = current_note.concat(note)
843
+ @results.push(%(Added note to "#{title}")) unless note.empty?
883
844
  else
884
- raise "Section #{section} not found"
845
+ items[0]['note'] = note
846
+ @results.push(%(Added note to "#{title}")) unless note.empty?
885
847
  end
886
- end
887
848
 
849
+ @content[section]['items'] = items
850
+
851
+ end
888
852
 
889
853
  #
890
854
  # @brief Accepts one tag and the raw text of a new item if the passed tag
@@ -896,7 +860,7 @@ class WWID
896
860
  # @param tag (String) Tag to replace
897
861
  # @param opt (Hash) Additional Options
898
862
  #
899
- def stop_start(tag,opt={})
863
+ def stop_start(tag, opt = {})
900
864
  opt[:section] ||= @current_section
901
865
  opt[:archive] ||= false
902
866
  opt[:back] ||= Time.now
@@ -905,36 +869,36 @@ class WWID
905
869
 
906
870
  opt[:section] = guess_section(opt[:section])
907
871
 
908
- tag.sub!(/^@/,'')
872
+ tag.sub!(/^@/, '')
909
873
 
910
874
  found_items = 0
911
- @content[opt[:section]]['items'].each_with_index {|item, i|
912
- if item['title'] =~ /@#{tag}/
913
- title = item['title'].gsub(/(^| )@(#{tag}|done)(\([^\)]*\))?/,'')
914
- title += " @done(#{opt[:back].strftime('%F %R')})"
915
-
916
- @content[opt[:section]]['items'][i]['title'] = title
917
- found_items += 1
918
-
919
- if opt[:archive] && opt[:section] != "Archive"
920
- @results.push(%Q{Completed and archived "#{@content[opt[:section]]['items'][i]['title']}"})
921
- archive_item = @content[opt[:section]]['items'][i]
922
- archive_item['title'] = i['title'].sub(/(?:@from\(.*?\))?(.*)$/,"\\1 @from(#{i['section']})")
923
- @content['Archive']['items'].push(archive_item)
924
- @content[opt[:section]]['items'].delete_at(i)
925
- else
926
- @results.push(%Q{Completed "#{@content[opt[:section]]['items'][i]['title']}"})
927
- end
875
+ @content[opt[:section]]['items'].each_with_index do |item, i|
876
+ next unless item['title'] =~ /@#{tag}/
877
+
878
+ title = item['title'].gsub(/(^| )@(#{tag}|done)(\([^)]*\))?/, '')
879
+ title += " @done(#{opt[:back].strftime('%F %R')})"
880
+
881
+ @content[opt[:section]]['items'][i]['title'] = title
882
+ found_items += 1
883
+
884
+ if opt[:archive] && opt[:section] != 'Archive'
885
+ @results.push(%(Completed and archived "#{@content[opt[:section]]['items'][i]['title']}"))
886
+ archive_item = @content[opt[:section]]['items'][i]
887
+ archive_item['title'] = i['title'].sub(/(?:@from\(.*?\))?(.*)$/, "\\1 @from(#{i['section']})")
888
+ @content['Archive']['items'].push(archive_item)
889
+ @content[opt[:section]]['items'].delete_at(i)
890
+ else
891
+ @results.push(%(Completed "#{@content[opt[:section]]['items'][i]['title']}"))
928
892
  end
929
- }
893
+ end
930
894
 
931
895
  @results.push("No active @#{tag} tasks found.") if found_items == 0
932
896
 
933
897
  if opt[:new_item]
934
898
  title, note = format_input(opt[:new_item])
935
- note.push(opt[:note].gsub(/ *$/,'')) if opt[:note]
899
+ note.push(opt[:note].gsub(/ *$/, '')) if opt[:note]
936
900
  title += " @#{tag}"
937
- add_item(title.cap_first, opt[:section], {:note => note.join(' ').rstrip, :back => opt[:back]})
901
+ add_item(title.cap_first, opt[:section], { note: note.join(' ').rstrip, back: opt[:back] })
938
902
  end
939
903
 
940
904
  write(@doing_file)
@@ -945,26 +909,23 @@ class WWID
945
909
  ##
946
910
  ## @param file (String) The filepath to write to
947
911
  ##
948
- def write(file=nil)
949
- unless @other_content_top
950
- output = ""
951
- else
952
- output = @other_content_top.join("\n") + "\n"
912
+ def write(file = nil)
913
+ output = @other_content_top ? "#{@other_content_top.join("\n")}\n" : ''
914
+
915
+ @content.each do |title, section|
916
+ output += "#{section['original']}\n"
917
+ output += list_section({ section: title, template: "\t- %date | %title%note", highlight: false })
953
918
  end
954
- @content.each {|title, section|
955
- output += section['original'] + "\n"
956
- output += list_section({:section => title, :template => "\t- %date | %title%note", :highlight => false})
957
- }
958
919
  output += @other_content_bottom.join("\n") unless @other_content_bottom.nil?
959
920
  if file.nil?
960
921
  $stdout.puts output
961
922
  else
962
923
  file = File.expand_path(file)
963
- if File.exists?(file)
924
+ if File.exist?(file)
964
925
  # Create a backup copy for the undo command
965
- FileUtils.cp(file,file+"~")
926
+ FileUtils.cp(file, "#{file}~")
966
927
 
967
- File.open(file,'w+') do |f|
928
+ File.open(file, 'w+') do |f|
968
929
  f.puts output
969
930
  end
970
931
  end
@@ -985,27 +946,27 @@ class WWID
985
946
  ## @param file (String) The filepath to restore
986
947
  ##
987
948
  def restore_backup(file)
988
- if File.exists?(file+"~")
989
- puts file+"~"
990
- FileUtils.cp(file+"~",file)
949
+ if File.exist?(file + '~')
950
+ puts file + '~'
951
+ FileUtils.cp(file + '~', file)
991
952
  @results.push("Restored #{file}")
992
953
  end
993
954
  end
994
955
 
995
-
996
956
  ##
997
957
  ## @brief Generate a menu of sections and allow user selection
998
958
  ##
999
959
  ## @return (String) The selected section name
1000
960
  ##
1001
961
  def choose_section
1002
- sections.each_with_index {|section, i|
1003
- puts "% 3d: %s" % [i+1, section]
1004
- }
962
+ sections.each_with_index do |section, i|
963
+ puts format('% 3d: %s', i + 1, section)
964
+ end
1005
965
  print "#{colors['green']}> #{colors['default']}"
1006
966
  num = STDIN.gets
1007
967
  return false if num =~ /^[a-z ]*$/i
1008
- return sections[num.to_i - 1]
968
+
969
+ sections[num.to_i - 1]
1009
970
  end
1010
971
 
1011
972
  ##
@@ -1023,13 +984,14 @@ class WWID
1023
984
  ## @return (String) The selected view name
1024
985
  ##
1025
986
  def choose_view
1026
- views.each_with_index {|view, i|
1027
- puts "% 3d: %s" % [i+1, view]
1028
- }
1029
- print "> "
987
+ views.each_with_index do |view, i|
988
+ puts format('% 3d: %s', i + 1, view)
989
+ end
990
+ print '> '
1030
991
  num = STDIN.gets
1031
992
  return false if num =~ /^[a-z ]*$/i
1032
- return views[num.to_i - 1]
993
+
994
+ views[num.to_i - 1]
1033
995
  end
1034
996
 
1035
997
  ##
@@ -1038,9 +1000,8 @@ class WWID
1038
1000
  ## @param title (String) The title of the view to retrieve
1039
1001
  ##
1040
1002
  def get_view(title)
1041
- if @config['views'].has_key?(title)
1042
- return @config['views'][title]
1043
- end
1003
+ return @config['views'][title] if @config['views'].has_key?(title)
1004
+
1044
1005
  false
1045
1006
  end
1046
1007
 
@@ -1050,14 +1011,14 @@ class WWID
1050
1011
  ##
1051
1012
  ## @param opt (Hash) Additional Options
1052
1013
  ##
1053
- def list_section(opt={})
1014
+ def list_section(opt = {})
1054
1015
  opt[:count] ||= 0
1055
1016
  count = opt[:count] - 1
1056
1017
  opt[:section] ||= nil
1057
1018
  opt[:format] ||= @default_date_format
1058
1019
  opt[:template] ||= @default_template
1059
- opt[:age] ||= "newest"
1060
- opt[:order] ||= "desc"
1020
+ opt[:age] ||= 'newest'
1021
+ opt[:order] ||= 'desc'
1061
1022
  opt[:today] ||= false
1062
1023
  opt[:tag_filter] ||= false
1063
1024
  opt[:tags_color] ||= false
@@ -1069,17 +1030,23 @@ class WWID
1069
1030
  opt[:date_filter] ||= []
1070
1031
 
1071
1032
  # opt[:highlight] ||= true
1072
- section = ""
1033
+ section = ''
1073
1034
  if opt[:section].nil?
1074
1035
  section = choose_section
1075
1036
  opt[:section] = @content[section]
1076
- elsif opt[:section].class == String
1037
+ elsif opt[:section].instance_of?(String)
1077
1038
  if opt[:section] =~ /^all$/i
1078
- combined = {'items' => []}
1079
- @content.each {|k,v|
1039
+ combined = { 'items' => [] }
1040
+ @content.each do |_k, v|
1080
1041
  combined['items'] += v['items']
1081
- }
1082
- section = opt[:tag_filter] && opt[:tag_filter]['bool'] != 'NONE' ? opt[:tag_filter]['tags'].map {|tag| "@#{tag}"}.join(" + ") : "doing"
1042
+ end
1043
+ section = if opt[:tag_filter] && opt[:tag_filter]['bool'] != 'NONE'
1044
+ opt[:tag_filter]['tags'].map do |tag|
1045
+ "@#{tag}"
1046
+ end.join(' + ')
1047
+ else
1048
+ 'doing'
1049
+ end
1083
1050
  opt[:section] = combined
1084
1051
  else
1085
1052
  section = guess_section(opt[:section])
@@ -1088,209 +1055,201 @@ class WWID
1088
1055
  end
1089
1056
 
1090
1057
  if opt[:section].class != Hash
1091
- $stderr.puts "Invalid section object"
1058
+ warn 'Invalid section object'
1092
1059
  return
1093
1060
  end
1094
1061
 
1095
- items = opt[:section]['items'].sort_by{|item| item['date'] }
1062
+ items = opt[:section]['items'].sort_by { |item| item['date'] }
1096
1063
 
1097
1064
  if opt[:date_filter].length == 2
1098
1065
  start_date = opt[:date_filter][0]
1099
1066
  end_date = opt[:date_filter][1]
1100
- items.keep_if {|item|
1067
+ items.keep_if do |item|
1101
1068
  if end_date
1102
1069
  item['date'] >= start_date && item['date'] <= end_date
1103
1070
  else
1104
1071
  item['date'].strftime('%F') == start_date.strftime('%F')
1105
1072
  end
1106
- }
1073
+ end
1107
1074
  end
1108
1075
 
1109
1076
  if opt[:tag_filter] && !opt[:tag_filter]['tags'].empty?
1110
- items.delete_if {|item|
1077
+ items.delete_if do |item|
1111
1078
  if opt[:tag_filter]['bool'] =~ /(AND|ALL)/
1112
1079
  score = 0
1113
- opt[:tag_filter]['tags'].each {|tag|
1080
+ opt[:tag_filter]['tags'].each do |tag|
1114
1081
  score += 1 if item['title'] =~ /@#{tag}/
1115
- }
1082
+ end
1116
1083
  score < opt[:tag_filter]['tags'].length
1117
1084
  elsif opt[:tag_filter]['bool'] =~ /NONE/
1118
1085
  del = false
1119
- opt[:tag_filter]['tags'].each {|tag|
1086
+ opt[:tag_filter]['tags'].each do |tag|
1120
1087
  del = true if item['title'] =~ /@#{tag}/
1121
- }
1088
+ end
1122
1089
  del
1123
1090
  elsif opt[:tag_filter]['bool'] =~ /(OR|ANY)/
1124
1091
  del = true
1125
- opt[:tag_filter]['tags'].each {|tag|
1092
+ opt[:tag_filter]['tags'].each do |tag|
1126
1093
  del = false if item['title'] =~ /@#{tag}/
1127
- }
1094
+ end
1128
1095
  del
1129
1096
  end
1130
- }
1097
+ end
1131
1098
  end
1132
1099
 
1133
1100
  if opt[:search]
1134
- items.keep_if {|item|
1135
- text = item['note'] ? item['title'] + item['note'].join(" ") : item['title']
1136
- if opt[:search].strip =~ /^\/.*?\/$/
1137
- pattern = opt[:search].sub(/\/(.*?)\//,'\1')
1138
- else
1139
- pattern = opt[:search].split('').join('.{0,3}')
1140
- end
1101
+ items.keep_if do |item|
1102
+ text = item['note'] ? item['title'] + item['note'].join(' ') : item['title']
1103
+ pattern = if opt[:search].strip =~ %r{^/.*?/$}
1104
+ opt[:search].sub(%r{/(.*?)/}, '\1')
1105
+ else
1106
+ opt[:search].split('').join('.{0,3}')
1107
+ end
1141
1108
  text =~ /#{pattern}/i
1142
- }
1109
+ end
1143
1110
  end
1144
1111
 
1145
1112
  if opt[:only_timed]
1146
- items.delete_if {|item|
1113
+ items.delete_if do |item|
1147
1114
  get_interval(item) == false
1148
- }
1115
+ end
1149
1116
  end
1150
1117
 
1151
1118
  if opt[:today]
1152
- items.delete_if {|item|
1119
+ items.delete_if do |item|
1153
1120
  item['date'] < Date.today.to_time
1154
- }.reverse!
1121
+ end.reverse!
1155
1122
  section = Time.now.strftime('%A, %B %d')
1156
1123
  elsif opt[:yesterday]
1157
- items.delete_if {|item| item['date'] <= Date.today.prev_day.to_time or
1158
- item['date'] >= Date.today.to_time
1159
- }.reverse!
1124
+ items.delete_if do |item|
1125
+ item['date'] <= Date.today.prev_day.to_time or
1126
+ item['date'] >= Date.today.to_time
1127
+ end.reverse!
1128
+ elsif opt[:age] =~ /oldest/i
1129
+ items = items[0..count]
1160
1130
  else
1161
- if opt[:age] =~ /oldest/i
1162
- items = items[0..count]
1163
- else
1164
- items = items.reverse[0..count]
1165
- end
1131
+ items = items.reverse[0..count]
1166
1132
  end
1167
1133
 
1168
- if opt[:order] =~ /^a/i
1169
- items.reverse!
1170
- end
1134
+ items.reverse! if opt[:order] =~ /^a/i
1171
1135
 
1172
- out = ""
1136
+ out = ''
1173
1137
 
1174
- if opt[:output]
1175
- raise "Unknown output format" unless opt[:output] =~ /(template|html|csv|json|timeline)/
1176
- end
1177
- if opt[:output] == "csv"
1178
- output = [CSV.generate_line(['date','title','note','timer','section'])]
1179
- items.each {|i|
1180
- note = ""
1138
+ raise 'Unknown output format' if opt[:output] && !(opt[:output] =~ /(template|html|csv|json|timeline)/)
1139
+
1140
+ if opt[:output] == 'csv'
1141
+ output = [CSV.generate_line(%w[date title note timer section])]
1142
+ items.each do |i|
1143
+ note = ''
1181
1144
  if i['note']
1182
- arr = i['note'].map{|line| line.strip}.delete_if{|e| e =~ /^\s*$/}
1145
+ arr = i['note'].map { |line| line.strip }.delete_if { |e| e =~ /^\s*$/ }
1183
1146
  note = arr.join("\n") unless arr.nil?
1184
1147
  end
1185
- if i['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
1186
- interval = get_interval(i, false)
1187
- end
1148
+ interval = get_interval(i, false) if i['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
1188
1149
  interval ||= 0
1189
- output.push(CSV.generate_line([i['date'],i['title'],note,interval,i['section']]))
1190
- }
1191
- out = output.join("")
1192
- elsif opt[:output] == "json" || opt[:output] == "timeline"
1150
+ output.push(CSV.generate_line([i['date'], i['title'], note, interval, i['section']]))
1151
+ end
1152
+ out = output.join('')
1153
+ elsif opt[:output] == 'json' || opt[:output] == 'timeline'
1193
1154
  items_out = []
1194
1155
  max = items[-1]['date'].strftime('%F')
1195
1156
  min = items[0]['date'].strftime('%F')
1196
- items.each_with_index {|i,index|
1157
+ items.each_with_index do |i, index|
1197
1158
  if String.method_defined? :force_encoding
1198
1159
  title = i['title'].force_encoding('utf-8')
1199
- note = i['note'].map {|line| line.force_encoding('utf-8').strip } if i['note']
1160
+ note = i['note'].map { |line| line.force_encoding('utf-8').strip } if i['note']
1200
1161
  else
1201
1162
  title = i['title']
1202
1163
  note = i['note'].map { |line| line.strip } if i['note']
1203
1164
  end
1204
1165
  if i['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
1205
- end_date = Time.parse($1)
1206
- interval = get_interval(i,false)
1166
+ end_date = Time.parse(Regexp.last_match(1))
1167
+ interval = get_interval(i, false)
1207
1168
  end
1208
- end_date ||= ""
1169
+ end_date ||= ''
1209
1170
  interval ||= 0
1210
- note ||= ""
1171
+ note ||= ''
1211
1172
 
1212
1173
  tags = []
1213
- skip_tags = ['meanwhile', 'done', 'cancelled', 'flagged']
1214
- i['title'].scan(/@([^\(\s]+)(?:\((.*?)\))?/).each {|tag|
1174
+ skip_tags = %w[meanwhile done cancelled flagged]
1175
+ i['title'].scan(/@([^(\s]+)(?:\((.*?)\))?/).each do |tag|
1215
1176
  tags.push(tag[0]) unless skip_tags.include?(tag[0])
1216
- }
1217
- if opt[:output] == "json"
1177
+ end
1178
+ if opt[:output] == 'json'
1218
1179
 
1219
1180
  items_out << {
1220
- :date => i['date'],
1221
- :end_date => end_date,
1222
- :title => title.strip, #+ " #{note}"
1223
- :note => note.class == Array ? note.map(&:strip).join("\n") : note,
1224
- :time => "%02d:%02d:%02d" % fmt_time(interval),
1225
- :tags => tags
1181
+ date: i['date'],
1182
+ end_date: end_date,
1183
+ title: title.strip, #+ " #{note}"
1184
+ note: note.instance_of?(Array) ? note.map(&:strip).join("\n") : note,
1185
+ time: '%02d:%02d:%02d' % fmt_time(interval),
1186
+ tags: tags
1226
1187
  }
1227
1188
 
1228
- elsif opt[:output] == "timeline"
1189
+ elsif opt[:output] == 'timeline'
1229
1190
  new_item = {
1230
1191
  'id' => index + 1,
1231
1192
  'content' => title.strip, #+ " #{note}"
1232
- 'title' => title.strip + " (#{"%02d:%02d:%02d" % fmt_time(interval)})",
1193
+ 'title' => title.strip + " (#{'%02d:%02d:%02d' % fmt_time(interval)})",
1233
1194
  'start' => i['date'].strftime('%F'),
1234
1195
  'type' => 'point'
1235
1196
  }
1236
1197
 
1237
1198
  if interval && interval > 0
1238
1199
  new_item['end'] = end_date.strftime('%F')
1239
- if interval > 3600 * 3
1240
- new_item['type'] = 'range'
1241
- end
1200
+ new_item['type'] = 'range' if interval > 3600 * 3
1242
1201
  end
1243
1202
  items_out.push(new_item)
1244
1203
  end
1245
- }
1246
- if opt[:output] == "json"
1204
+ end
1205
+ if opt[:output] == 'json'
1247
1206
  out = {
1248
1207
  'section' => section,
1249
1208
  'items' => items_out,
1250
- 'timers' => tag_times("json", opt[:sort_tags])
1209
+ 'timers' => tag_times('json', opt[:sort_tags])
1251
1210
  }.to_json
1252
- elsif opt[:output] == "timeline"
1253
- template =<<EOTEMPLATE
1254
- <!doctype html>
1255
- <html>
1256
- <head>
1257
- <link href="http://visjs.org/dist/vis.css" rel="stylesheet" type="text/css" />
1258
- <script src="http://visjs.org/dist/vis.js"></script>
1259
- </head>
1260
- <body>
1261
- <div id="mytimeline"></div>
1262
-
1263
- <script type="text/javascript">
1264
- // DOM element where the Timeline will be attached
1265
- var container = document.getElementById('mytimeline');
1266
-
1267
- // Create a DataSet with data (enables two way data binding)
1268
- var data = new vis.DataSet(#{items_out.to_json});
1269
-
1270
- // Configuration for the Timeline
1271
- var options = {
1272
- width: '100%',
1273
- height: '800px',
1274
- margin: {
1275
- item: 20
1276
- },
1277
- stack: true,
1278
- min: '#{min}',
1279
- max: '#{max}'
1280
- };
1281
-
1282
- // Create a Timeline
1283
- var timeline = new vis.Timeline(container, data, options);
1284
- </script>
1285
- </body>
1286
- </html>
1287
- EOTEMPLATE
1211
+ elsif opt[:output] == 'timeline'
1212
+ template = <<~EOTEMPLATE
1213
+ <!doctype html>
1214
+ <html>
1215
+ <head>
1216
+ <link href="http://visjs.org/dist/vis.css" rel="stylesheet" type="text/css" />
1217
+ <script src="http://visjs.org/dist/vis.js"></script>
1218
+ </head>
1219
+ <body>
1220
+ <div id="mytimeline"></div>
1221
+ #{' '}
1222
+ <script type="text/javascript">
1223
+ // DOM element where the Timeline will be attached
1224
+ var container = document.getElementById('mytimeline');
1225
+ #{' '}
1226
+ // Create a DataSet with data (enables two way data binding)
1227
+ var data = new vis.DataSet(#{items_out.to_json});
1228
+ #{' '}
1229
+ // Configuration for the Timeline
1230
+ var options = {
1231
+ width: '100%',
1232
+ height: '800px',
1233
+ margin: {
1234
+ item: 20
1235
+ },
1236
+ stack: true,
1237
+ min: '#{min}',
1238
+ max: '#{max}'
1239
+ };
1240
+ #{' '}
1241
+ // Create a Timeline
1242
+ var timeline = new vis.Timeline(container, data, options);
1243
+ </script>
1244
+ </body>
1245
+ </html>
1246
+ EOTEMPLATE
1288
1247
  return template
1289
1248
  end
1290
- elsif opt[:output] == "html"
1249
+ elsif opt[:output] == 'html'
1291
1250
  page_title = section
1292
1251
  items_out = []
1293
- items.each {|i|
1252
+ items.each do |i|
1294
1253
  # if i.has_key?('note')
1295
1254
  # note = '<span class="note">' + i['note'].map{|n| n.strip }.join('<br>') + '</span>'
1296
1255
  # else
@@ -1298,83 +1257,81 @@ EOTEMPLATE
1298
1257
  # end
1299
1258
  if String.method_defined? :force_encoding
1300
1259
  title = i['title'].force_encoding('utf-8').link_urls
1301
- note = i['note'].map {|line| line.force_encoding('utf-8').strip.link_urls } if i['note']
1260
+ note = i['note'].map { |line| line.force_encoding('utf-8').strip.link_urls } if i['note']
1302
1261
  else
1303
1262
  title = i['title'].link_urls
1304
1263
  note = i['note'].map { |line| line.strip.link_urls } if i['note']
1305
1264
  end
1306
1265
 
1307
- if i['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
1308
- interval = get_interval(i)
1309
- end
1266
+ interval = get_interval(i) if i['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
1310
1267
  interval ||= false
1311
1268
 
1312
1269
  items_out << {
1313
- :date => i['date'].strftime('%a %-I:%M%p'),
1314
- :title => title.gsub(/(@[^ \(]+(\(.*?\))?)/im,'<span class="tag">\1</span>').strip, #+ " #{note}"
1315
- :note => note,
1316
- :time => interval,
1317
- :section => i['section']
1270
+ date: i['date'].strftime('%a %-I:%M%p'),
1271
+ title: title.gsub(/(@[^ (]+(\(.*?\))?)/im, '<span class="tag">\1</span>').strip, #+ " #{note}"
1272
+ note: note,
1273
+ time: interval,
1274
+ section: i['section']
1318
1275
  }
1319
- }
1320
-
1321
- if @config['html_template']['haml'] && File.exists?(File.expand_path(@config['html_template']['haml']))
1322
- template = IO.read(File.expand_path(@config['html_template']['haml']))
1323
- else
1324
- template = haml_template
1325
1276
  end
1326
1277
 
1327
- if @config['html_template']['css'] && File.exists?(File.expand_path(@config['html_template']['css']))
1328
- style = IO.read(File.expand_path(@config['html_template']['css']))
1329
- else
1330
- style = css_template
1331
- end
1278
+ template = if @config['html_template']['haml'] && File.exist?(File.expand_path(@config['html_template']['haml']))
1279
+ IO.read(File.expand_path(@config['html_template']['haml']))
1280
+ else
1281
+ haml_template
1282
+ end
1283
+
1284
+ style = if @config['html_template']['css'] && File.exist?(File.expand_path(@config['html_template']['css']))
1285
+ IO.read(File.expand_path(@config['html_template']['css']))
1286
+ else
1287
+ css_template
1288
+ end
1332
1289
 
1333
- totals = opt[:totals] ? tag_times("html", opt[:sort_tags]) : ""
1290
+ totals = opt[:totals] ? tag_times('html', opt[:sort_tags]) : ''
1334
1291
  engine = Haml::Engine.new(template)
1335
- puts engine.render(Object.new, { :@items => items_out, :@page_title => page_title, :@style => style, :@totals => totals })
1292
+ puts engine.render(Object.new,
1293
+ { :@items => items_out, :@page_title => page_title, :@style => style, :@totals => totals })
1336
1294
  else
1337
- items.each {|item|
1338
-
1295
+ items.each do |item|
1339
1296
  if opt[:highlight] && item['title'] =~ /@#{@config['marker_tag']}\b/i
1340
1297
  flag = colors[@config['marker_color']]
1341
1298
  reset = colors['default']
1342
1299
  else
1343
- flag = ""
1344
- reset = ""
1300
+ flag = ''
1301
+ reset = ''
1345
1302
  end
1346
1303
 
1347
1304
  if (item.has_key?('note') && !item['note'].empty?) && @config[:include_notes]
1348
- note_lines = item['note'].delete_if{|line| line =~ /^\s*$/ }.map{|line| "\t\t" + line.sub(/^\t*/,'').sub(/^-/, '—') + " " }
1305
+ note_lines = item['note'].delete_if do |line|
1306
+ line =~ /^\s*$/
1307
+ end.map { |line| "\t\t" + line.sub(/^\t*/, '').sub(/^-/, '—') + ' ' }
1349
1308
  if opt[:wrap_width] && opt[:wrap_width] > 0
1350
1309
  width = opt[:wrap_width]
1351
- note_lines.map! {|line|
1310
+ note_lines.map! do |line|
1352
1311
  line.strip.gsub(/(.{1,#{width}})(\s+|\Z)/, "\t\\1\n")
1353
- }
1312
+ end
1354
1313
  end
1355
1314
  note = "\n#{note_lines.join("\n").chomp}"
1356
1315
  else
1357
- note = ""
1316
+ note = ''
1358
1317
  end
1359
1318
  output = opt[:template].dup
1360
1319
 
1361
1320
  output.gsub!(/%[a-z]+/) do |m|
1362
- if colors.has_key?(m.sub(/^%/,''))
1363
- colors[m.sub(/^%/,'')]
1321
+ if colors.has_key?(m.sub(/^%/, ''))
1322
+ colors[m.sub(/^%/, '')]
1364
1323
  else
1365
1324
  m
1366
1325
  end
1367
1326
  end
1368
1327
 
1369
- output.sub!(/%date/,item['date'].strftime(opt[:format]))
1328
+ output.sub!(/%date/, item['date'].strftime(opt[:format]))
1370
1329
 
1371
- if item['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
1372
- interval = get_interval(item)
1373
- end
1374
- interval ||= ""
1375
- output.sub!(/%interval/,interval)
1330
+ interval = get_interval(item) if item['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
1331
+ interval ||= ''
1332
+ output.sub!(/%interval/, interval)
1376
1333
 
1377
- output.sub!(/%shortdate/) {
1334
+ output.sub!(/%shortdate/) do
1378
1335
  if item['date'] > Date.today.to_time
1379
1336
  item['date'].strftime('%_I:%M%P')
1380
1337
  elsif item['date'] > (Date.today - 7).to_time
@@ -1384,45 +1341,45 @@ EOTEMPLATE
1384
1341
  else
1385
1342
  item['date'].strftime('%b %d %Y, %-I:%M%P')
1386
1343
  end
1387
- }
1344
+ end
1388
1345
 
1389
- output.sub!(/%title/) {|m|
1346
+ output.sub!(/%title/) do |_m|
1390
1347
  if opt[:wrap_width] && opt[:wrap_width] > 0
1391
- flag+item['title'].gsub(/(.{1,#{opt[:wrap_width]}})(\s+|\Z)/, "\\1\n\t ").chomp+reset
1348
+ flag + item['title'].gsub(/(.{1,#{opt[:wrap_width]}})(\s+|\Z)/, "\\1\n\t ").chomp + reset
1392
1349
  else
1393
- flag+item['title'].chomp+reset
1350
+ flag + item['title'].chomp + reset
1394
1351
  end
1395
- }
1352
+ end
1396
1353
 
1397
- output.sub!(/%section/,item['section']) if item['section']
1354
+ output.sub!(/%section/, item['section']) if item['section']
1398
1355
 
1399
1356
  if opt[:tags_color]
1400
1357
  escapes = output.scan(/(\e\[[\d;]+m)[^\e]+@/)
1401
- if escapes.length > 0
1402
- last_color = escapes[-1][0]
1403
- else
1404
- last_color = colors['default']
1405
- end
1406
- output.gsub!(/\s(@[^ \(]+)/," #{colors[opt[:tags_color]]}\\1#{last_color}")
1358
+ last_color = if escapes.length > 0
1359
+ escapes[-1][0]
1360
+ else
1361
+ colors['default']
1362
+ end
1363
+ output.gsub!(/\s(@[^ (]+)/, " #{colors[opt[:tags_color]]}\\1#{last_color}")
1407
1364
  end
1408
- output.sub!(/%note/,note)
1409
- output.sub!(/%odnote/,note.gsub(/^\t*/,""))
1410
- output.sub!(/%chompnote/,note.gsub(/\n+/,' ').gsub(/(^\s*|\s*$)/,'').gsub(/\s+/,' '))
1411
- output.gsub!(/%hr(_under)?/) do |m|
1412
- o = ""
1365
+ output.sub!(/%note/, note)
1366
+ output.sub!(/%odnote/, note.gsub(/^\t*/, ''))
1367
+ output.sub!(/%chompnote/, note.gsub(/\n+/, ' ').gsub(/(^\s*|\s*$)/, '').gsub(/\s+/, ' '))
1368
+ output.gsub!(/%hr(_under)?/) do |_m|
1369
+ o = ''
1413
1370
  `tput cols`.to_i.times do
1414
- o += $1.nil? ? "-" : "_"
1371
+ o += Regexp.last_match(1).nil? ? '-' : '_'
1415
1372
  end
1416
1373
  o
1417
1374
  end
1418
- output.gsub!(/%n/,"\n")
1419
- output.gsub!(/%t/,"\t")
1375
+ output.gsub!(/%n/, "\n")
1376
+ output.gsub!(/%t/, "\t")
1420
1377
 
1421
1378
  out += output + "\n"
1422
- }
1423
- out += tag_times("text", opt[:sort_tags]) if opt[:totals]
1379
+ end
1380
+ out += tag_times('text', opt[:sort_tags]) if opt[:totals]
1424
1381
  end
1425
- return out
1382
+ out
1426
1383
  end
1427
1384
 
1428
1385
  ##
@@ -1435,15 +1392,12 @@ EOTEMPLATE
1435
1392
  ## @param tags (Array) Tags to archive
1436
1393
  ## @param bool (String) Tag boolean combinator
1437
1394
  ##
1438
- def archive(section="Currently",count=5,destination=nil,tags=nil,bool=nil,export=nil)
1439
-
1395
+ def archive(section = 'Currently', count = 5, destination = nil, tags = nil, bool = nil, _export = nil)
1440
1396
  section = choose_section if section.nil? || section =~ /choose/i
1441
1397
  archive_all = section =~ /all/i # && !(tags.nil? || tags.empty?)
1442
1398
  section = guess_section(section) unless archive_all
1443
1399
 
1444
- if destination =~ /archive/i && !sections.include?("Archive")
1445
- add_section("Archive")
1446
- end
1400
+ add_section('Archive') if destination =~ /archive/i && !sections.include?('Archive')
1447
1401
 
1448
1402
  destination = guess_section(destination)
1449
1403
 
@@ -1451,16 +1405,16 @@ EOTEMPLATE
1451
1405
  if archive_all
1452
1406
  to_archive = sections.dup
1453
1407
  to_archive.delete(destination)
1454
- to_archive.each {|source,v|
1455
- do_archive(source, destination, { :count => count, :tags => tags, :bool => bool, :label => true })
1456
- }
1408
+ to_archive.each do |source, _v|
1409
+ do_archive(source, destination, { count: count, tags: tags, bool: bool, label: true })
1410
+ end
1457
1411
  else
1458
- do_archive(section, destination, { :count => count, :tags => tags, :bool => bool, :label => true })
1412
+ do_archive(section, destination, { count: count, tags: tags, bool: bool, label: true })
1459
1413
  end
1460
1414
 
1461
1415
  write(doing_file)
1462
1416
  else
1463
- raise "Either source or destination does not exist"
1417
+ raise 'Either source or destination does not exist'
1464
1418
  end
1465
1419
  end
1466
1420
 
@@ -1471,63 +1425,66 @@ EOTEMPLATE
1471
1425
  ## @param destination (String) The destination section
1472
1426
  ## @param opt (Hash) Additional Options
1473
1427
  ##
1474
- def do_archive(section, destination, opt={})
1428
+ def do_archive(section, destination, opt = {})
1475
1429
  count = opt[:count] || 5
1476
1430
  tags = opt[:tags] || []
1477
- bool = opt[:bool] || "AND"
1431
+ bool = opt[:bool] || 'AND'
1478
1432
  label = opt[:label] || false
1479
1433
 
1480
1434
  items = @content[section]['items']
1481
1435
  moved_items = []
1482
1436
 
1483
1437
  if tags && !tags.empty?
1484
- items.delete_if {|item|
1438
+ items.delete_if do |item|
1485
1439
  if bool =~ /(AND|ALL)/
1486
1440
  score = 0
1487
- tags.each {|tag|
1441
+ tags.each do |tag|
1488
1442
  score += 1 if item['title'] =~ /@#{tag}/i
1489
- }
1443
+ end
1490
1444
  res = score < tags.length
1491
1445
  moved_items.push(item) if res
1492
1446
  res
1493
1447
  elsif bool =~ /NONE/
1494
1448
  del = false
1495
- tags.each {|tag|
1449
+ tags.each do |tag|
1496
1450
  del = true if item['title'] =~ /@#{tag}/i
1497
- }
1451
+ end
1498
1452
  moved_items.push(item) if del
1499
1453
  del
1500
1454
  elsif bool =~ /(OR|ANY)/
1501
1455
  del = true
1502
- tags.each {|tag|
1456
+ tags.each do |tag|
1503
1457
  del = false if item['title'] =~ /@#{tag}/i
1504
- }
1458
+ end
1505
1459
  moved_items.push(item) if del
1506
1460
  del
1507
1461
  end
1508
- }
1509
- moved_items.each {|item|
1510
- if label
1511
- item['title'] = item['title'].sub(/(?:@from\(.*?\))?(.*)$/,"\\1 @from(#{section})") unless section == "Currently"
1462
+ end
1463
+ moved_items.each do |item|
1464
+ if label && !(section == 'Currently')
1465
+ item['title'] =
1466
+ item['title'].sub(/(?:@from\(.*?\))?(.*)$/, "\\1 @from(#{section})")
1512
1467
  end
1513
- }
1468
+ end
1514
1469
  @content[section]['items'] = moved_items
1515
1470
  @content[destination]['items'] += items
1516
1471
  @results.push("Archived #{items.length} items from #{section} to #{destination}")
1517
1472
  else
1518
1473
 
1519
1474
  return if items.length < count
1520
- if count == 0
1521
- @content[section]['items'] = []
1522
- else
1523
- @content[section]['items'] = items[0..count-1]
1524
- end
1525
1475
 
1526
- items.each{|item|
1527
- if label
1528
- item['title'] = item['title'].sub(/(?:@from\(.*?\))?(.*)$/,"\\1 @from(#{section})") unless section == "Currently"
1476
+ @content[section]['items'] = if count == 0
1477
+ []
1478
+ else
1479
+ items[0..count - 1]
1480
+ end
1481
+
1482
+ items.each do |item|
1483
+ if label && !(section == 'Currently')
1484
+ item['title'] =
1485
+ item['title'].sub(/(?:@from\(.*?\))?(.*)$/, "\\1 @from(#{section})")
1529
1486
  end
1530
- }
1487
+ end
1531
1488
 
1532
1489
  @content[destination]['items'] += items[count..-1]
1533
1490
  @results.push("Archived #{items.length - count} items from #{section} to #{destination}")
@@ -1583,7 +1540,6 @@ EOTEMPLATE
1583
1540
  color
1584
1541
  end
1585
1542
 
1586
-
1587
1543
  ##
1588
1544
  ## @brief Show all entries from the current day
1589
1545
  ##
@@ -1591,12 +1547,13 @@ EOTEMPLATE
1591
1547
  ## @param output (String) output format
1592
1548
  ## @param opt (Hash) Options
1593
1549
  ##
1594
- def today(times=true,output=nil,opt={})
1550
+ def today(times = true, output = nil, opt = {})
1595
1551
  opt[:totals] ||= false
1596
1552
  opt[:sort_tags] ||= false
1597
1553
 
1598
1554
  cfg = @config['templates']['today']
1599
- list_section({:section => opt[:section], :wrap_width => cfg['wrap_width'], :count => 0, :format => cfg['date_format'], :template => cfg['template'], :order => "asc", :today => true, :times => times, :output => output, :totals => opt[:totals], :sort_tags => opt[:sort_tags]})
1555
+ list_section({ section: opt[:section], wrap_width: cfg['wrap_width'], count: 0,
1556
+ format: cfg['date_format'], template: cfg['template'], order: 'asc', today: true, times: times, output: output, totals: opt[:totals], sort_tags: opt[:sort_tags] })
1600
1557
  end
1601
1558
 
1602
1559
  ##
@@ -1608,16 +1565,15 @@ EOTEMPLATE
1608
1565
  ## @param output (String) Output format
1609
1566
  ## @param opt (Hash) Additional Options
1610
1567
  ##
1611
- def list_date(dates,section,times=nil,output=nil,opt={})
1568
+ def list_date(dates, section, times = nil, output = nil, opt = {})
1612
1569
  opt[:totals] ||= false
1613
1570
  opt[:sort_tags] ||= false
1614
1571
  section = guess_section(section)
1615
1572
  # :date_filter expects an array with start and end date
1616
- if dates.class == String
1617
- dates = [dates, dates]
1618
- end
1573
+ dates = [dates, dates] if dates.instance_of?(String)
1619
1574
 
1620
- list_section({:section => section, :count => 0, :order => "asc", :date_filter => dates, :times => times, :output => output, :totals => opt[:totals], :sort_tags => opt[:sort_tags] })
1575
+ list_section({ section: section, count: 0, order: 'asc', date_filter: dates, times: times,
1576
+ output: output, totals: opt[:totals], sort_tags: opt[:sort_tags] })
1621
1577
  end
1622
1578
 
1623
1579
  ##
@@ -1628,11 +1584,12 @@ EOTEMPLATE
1628
1584
  ## @param output (String) Output format
1629
1585
  ## @param opt (Hash) Additional Options
1630
1586
  ##
1631
- def yesterday(section,times=nil,output=nil,opt={})
1587
+ def yesterday(section, times = nil, output = nil, opt = {})
1632
1588
  opt[:totals] ||= false
1633
1589
  opt[:sort_tags] ||= false
1634
1590
  section = guess_section(section)
1635
- list_section({:section => section, :count => 0, :order => "asc", :yesterday => true, :times => times, :output => output, :totals => opt[:totals], :sort_tags => opt[:sort_tags] })
1591
+ list_section({ section: section, count: 0, order: 'asc', yesterday: true, times: times,
1592
+ output: output, totals: opt[:totals], sort_tags: opt[:sort_tags] })
1636
1593
  end
1637
1594
 
1638
1595
  ##
@@ -1642,7 +1599,7 @@ EOTEMPLATE
1642
1599
  ## @param section (String) The section to show from, default Currently
1643
1600
  ## @param opt (Hash) Additional Options
1644
1601
  ##
1645
- def recent(count=10,section=nil,opt={})
1602
+ def recent(count = 10, section = nil, opt = {})
1646
1603
  times = opt[:t] || true
1647
1604
  opt[:totals] ||= false
1648
1605
  opt[:sort_tags] ||= false
@@ -1650,7 +1607,8 @@ EOTEMPLATE
1650
1607
  cfg = @config['templates']['recent']
1651
1608
  section ||= @current_section
1652
1609
  section = guess_section(section)
1653
- list_section({:section => section, :wrap_width => cfg['wrap_width'], :count => count, :format => cfg['date_format'], :template => cfg['template'], :order => "asc", :times => times, :totals => opt[:totals], :sort_tags => opt[:sort_tags] })
1610
+ list_section({ section: section, wrap_width: cfg['wrap_width'], count: count,
1611
+ format: cfg['date_format'], template: cfg['template'], order: 'asc', times: times, totals: opt[:totals], sort_tags: opt[:sort_tags] })
1654
1612
  end
1655
1613
 
1656
1614
  ##
@@ -1659,11 +1617,12 @@ EOTEMPLATE
1659
1617
  ## @param times (Bool) Show times
1660
1618
  ## @param section (String) Section to pull from, default Currently
1661
1619
  ##
1662
- def last(times=true,section=nil)
1620
+ def last(times = true, section = nil)
1663
1621
  section ||= @current_section
1664
1622
  section = guess_section(section)
1665
1623
  cfg = @config['templates']['last']
1666
- list_section({:section => section, :wrap_width => cfg['wrap_width'], :count => 1, :format => cfg['date_format'], :template => cfg['template'], :times => times})
1624
+ list_section({ section: section, wrap_width: cfg['wrap_width'], count: 1, format: cfg['date_format'],
1625
+ template: cfg['template'], times: times })
1667
1626
  end
1668
1627
 
1669
1628
  ##
@@ -1671,22 +1630,22 @@ EOTEMPLATE
1671
1630
  ##
1672
1631
  ## @param format (String) return format (html, json, or text)
1673
1632
  ##
1674
- def tag_times(format="text", sort_by_name = false)
1675
- return "" if @timers.empty?
1633
+ def tag_times(format = 'text', sort_by_name = false)
1634
+ return '' if @timers.empty?
1676
1635
 
1677
- max = @timers.keys.sort_by {|k| k.length }.reverse[0].length + 1
1636
+ max = @timers.keys.sort_by { |k| k.length }.reverse[0].length + 1
1678
1637
 
1679
- total = @timers.delete("All")
1638
+ total = @timers.delete('All')
1680
1639
 
1681
- tags_data = @timers.delete_if { |k,v| v == 0}
1682
- if sort_by_name
1683
- sorted_tags_data = tags_data.sort_by{|k,v| k }.reverse
1684
- else
1685
- sorted_tags_data = tags_data.sort_by{|k,v| v }
1686
- end
1640
+ tags_data = @timers.delete_if { |_k, v| v == 0 }
1641
+ sorted_tags_data = if sort_by_name
1642
+ tags_data.sort_by { |k, _v| k }.reverse
1643
+ else
1644
+ tags_data.sort_by { |_k, v| v }
1645
+ end
1687
1646
 
1688
- if format == "html"
1689
- output =<<EOS
1647
+ if format == 'html'
1648
+ output = <<EOS
1690
1649
  <table>
1691
1650
  <caption id="tagtotals">Tag Totals</caption>
1692
1651
  <colgroup>
@@ -1701,10 +1660,12 @@ EOTEMPLATE
1701
1660
  </thead>
1702
1661
  <tbody>
1703
1662
  EOS
1704
- sorted_tags_data.reverse.each {|k,v|
1705
- output += "<tr><td style='text-align:left;'>#{k}</td><td style='text-align:left;'>#{"%02d:%02d:%02d" % fmt_time(v)}</td></tr>\n" if v > 0
1706
- }
1707
- tail =<<EOS
1663
+ sorted_tags_data.reverse.each do |k, v|
1664
+ if v > 0
1665
+ output += "<tr><td style='text-align:left;'>#{k}</td><td style='text-align:left;'>#{'%02d:%02d:%02d' % fmt_time(v)}</td></tr>\n"
1666
+ end
1667
+ end
1668
+ tail = <<EOS
1708
1669
  <tr>
1709
1670
  <td style="text-align:left;" colspan="2"></td>
1710
1671
  </tr>
@@ -1712,34 +1673,34 @@ EOS
1712
1673
  <tfoot>
1713
1674
  <tr>
1714
1675
  <td style="text-align:left;"><strong>Total</strong></td>
1715
- <td style="text-align:left;">#{"%02d:%02d:%02d" % fmt_time(total)}</td>
1676
+ <td style="text-align:left;">#{'%02d:%02d:%02d' % fmt_time(total)}</td>
1716
1677
  </tr>
1717
1678
  </tfoot>
1718
1679
  </table>
1719
1680
  EOS
1720
1681
  output + tail
1721
- elsif format == "json"
1682
+ elsif format == 'json'
1722
1683
  output = []
1723
- sorted_tags_data.reverse.each {|k,v|
1684
+ sorted_tags_data.reverse.each do |k, v|
1724
1685
  output << {
1725
1686
  'tag' => k,
1726
1687
  'seconds' => v,
1727
- 'formatted' => "%02d:%02d:%02d" % fmt_time(v)
1688
+ 'formatted' => '%02d:%02d:%02d' % fmt_time(v)
1728
1689
  }
1729
- }
1690
+ end
1730
1691
  output
1731
1692
  else
1732
1693
  output = []
1733
- sorted_tags_data.reverse.each {|k,v|
1734
- spacer = ""
1694
+ sorted_tags_data.reverse.each do |k, v|
1695
+ spacer = ''
1735
1696
  (max - k.length).times do
1736
- spacer += " "
1697
+ spacer += ' '
1737
1698
  end
1738
- output.push("#{k}:#{spacer}#{"%02d:%02d:%02d" % fmt_time(v)}")
1739
- }
1699
+ output.push("#{k}:#{spacer}#{'%02d:%02d:%02d' % fmt_time(v)}")
1700
+ end
1740
1701
 
1741
- output = output.empty? ? "" : "\n--- Tag Totals ---\n" + output.join("\n")
1742
- output += "\n\nTotal tracked: #{"%02d:%02d:%02d" % fmt_time(total)}\n"
1702
+ output = output.empty? ? '' : "\n--- Tag Totals ---\n" + output.join("\n")
1703
+ output += "\n\nTotal tracked: #{'%02d:%02d:%02d' % fmt_time(total)}\n"
1743
1704
  output
1744
1705
  end
1745
1706
  end
@@ -1753,53 +1714,54 @@ EOS
1753
1714
  def autotag(text)
1754
1715
  return unless text
1755
1716
  return text unless @auto_tag
1717
+
1756
1718
  current_tags = text.scan(/@\w+/)
1757
1719
  whitelisted = []
1758
- @config['autotag']['whitelist'].each {|tag|
1720
+ @config['autotag']['whitelist'].each do |tag|
1721
+ next if text =~ /@#{tag}\b/i
1722
+
1759
1723
  text.sub!(/(?<!@)(#{tag.strip})\b/i) do |m|
1760
1724
  m.downcase! if tag =~ /[a-z]/
1761
1725
  whitelisted.push("@#{m}")
1762
1726
  "@#{m}"
1763
- end unless text =~ /@#{tag}\b/i
1764
- }
1727
+ end
1728
+ end
1765
1729
  tail_tags = []
1766
- @config['autotag']['synonyms'].each {|tag, v|
1767
- v.each {|word|
1768
- if text =~ /\b#{word}\b/i
1769
- unless current_tags.include?("@#{tag}") || whitelisted.include?("@#{tag}")
1770
- tail_tags.push(tag)
1771
- end
1772
- end
1773
- }
1774
- }
1730
+ @config['autotag']['synonyms'].each do |tag, v|
1731
+ v.each do |word|
1732
+ next unless text =~ /\b#{word}\b/i
1733
+
1734
+ tail_tags.push(tag) unless current_tags.include?("@#{tag}") || whitelisted.include?("@#{tag}")
1735
+ end
1736
+ end
1775
1737
  if @config['autotag'].key? 'transform'
1776
- @config['autotag']['transform'].each {|tag|
1777
- if tag =~ /\S+:\S+/
1778
- rx, r = tag.split(/:/)
1779
- r.gsub!(/\$/,'\\')
1780
- rx.sub!(/^@/,'')
1781
- regex = Regexp.new('@' + rx + '\b')
1782
-
1783
- matches = text.scan(regex)
1784
- matches.each {|m|
1785
- new_tag = r
1786
- if m.kind_of?(Array)
1787
- index = 1
1788
- m.each {|v|
1789
- new_tag = new_tag.gsub('\\' + index.to_s, v)
1790
- index = index + 1
1791
- }
1738
+ @config['autotag']['transform'].each do |tag|
1739
+ next unless tag =~ /\S+:\S+/
1740
+
1741
+ rx, r = tag.split(/:/)
1742
+ r.gsub!(/\$/, '\\')
1743
+ rx.sub!(/^@/, '')
1744
+ regex = Regexp.new('@' + rx + '\b')
1745
+
1746
+ matches = text.scan(regex)
1747
+ next unless matches
1748
+
1749
+ matches.each do |m|
1750
+ new_tag = r
1751
+ if m.is_a?(Array)
1752
+ index = 1
1753
+ m.each do |v|
1754
+ new_tag = new_tag.gsub('\\' + index.to_s, v)
1755
+ index += 1
1792
1756
  end
1793
- tail_tags.push(new_tag)
1794
- } if matches
1757
+ end
1758
+ tail_tags.push(new_tag)
1795
1759
  end
1796
- }
1797
- end
1798
- if whitelisted.length > 0
1799
- @results.push("Whitelisted tags: #{whitelisted.join(', ')}")
1760
+ end
1800
1761
  end
1762
+ @results.push("Whitelisted tags: #{whitelisted.join(', ')}") if whitelisted.length > 0
1801
1763
  if tail_tags.length > 0
1802
- tags = tail_tags.uniq.map {|t| '@'+t }.join(' ')
1764
+ tags = tail_tags.uniq.map { |t| '@' + t }.join(' ')
1803
1765
  @results.push("Synonym tags: #{tags}")
1804
1766
  text + ' ' + tags
1805
1767
  else
@@ -1815,43 +1777,43 @@ EOS
1815
1777
  ## @param item (Hash) The entry
1816
1778
  ## @param formatted (Bool) Return human readable time (default seconds)
1817
1779
  ##
1818
- def get_interval(item, formatted=true)
1780
+ def get_interval(item, formatted = true)
1819
1781
  done = nil
1820
1782
  start = nil
1821
1783
 
1822
1784
  if @interval_cache.keys.include? item['title']
1823
1785
  seconds = @interval_cache[item['title']]
1824
- return seconds > 0 ? "%02d:%02d:%02d" % fmt_time(seconds) : false
1786
+ return seconds > 0 ? '%02d:%02d:%02d' % fmt_time(seconds) : false
1825
1787
  end
1826
1788
 
1827
1789
  if item['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/
1828
- done = Time.parse($1)
1790
+ done = Time.parse(Regexp.last_match(1))
1829
1791
  else
1830
1792
  return nil
1831
1793
  end
1832
1794
 
1833
- if item['title'] =~ /@start\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/
1834
- start = Time.parse($1)
1835
- else
1836
- start = item['date']
1837
- end
1795
+ start = if item['title'] =~ /@start\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/
1796
+ Time.parse(Regexp.last_match(1))
1797
+ else
1798
+ item['date']
1799
+ end
1838
1800
 
1839
1801
  seconds = (done - start).to_i
1840
1802
 
1841
- item['title'].scan(/(?mi)@(\S+?)(\(.*\))?(?=\s|$)/).each {|m|
1842
- k = m[0] == "done" ? "All" : m[0].downcase
1803
+ item['title'].scan(/(?mi)@(\S+?)(\(.*\))?(?=\s|$)/).each do |m|
1804
+ k = m[0] == 'done' ? 'All' : m[0].downcase
1843
1805
  if @timers.has_key?(k)
1844
1806
  @timers[k] += seconds
1845
1807
  else
1846
1808
  @timers[k] = seconds
1847
1809
  end
1848
- }
1810
+ end
1849
1811
 
1850
1812
  @interval_cache[item['title']] = seconds
1851
1813
 
1852
1814
  return seconds unless formatted
1853
1815
 
1854
- seconds > 0 ? "%02d:%02d:%02d" % fmt_time(seconds) : false
1816
+ seconds > 0 ? '%02d:%02d:%02d' % fmt_time(seconds) : false
1855
1817
  end
1856
1818
 
1857
1819
  ##
@@ -1860,19 +1822,19 @@ EOS
1860
1822
  ## @param seconds The seconds
1861
1823
  ##
1862
1824
  def fmt_time(seconds)
1863
- if seconds.nil?
1864
- return [0, 0, 0]
1865
- end
1825
+ return [0, 0, 0] if seconds.nil?
1826
+
1866
1827
  if seconds =~ /(\d+):(\d+):(\d+)/
1867
- h, m, s = [$1, $2, $3]
1828
+ h = Regexp.last_match(1)
1829
+ m = Regexp.last_match(2)
1830
+ s = Regexp.last_match(3)
1868
1831
  seconds = (h.to_i * 60 * 60) + (m.to_i * 60) + s.to_i
1869
1832
  end
1870
- minutes = (seconds / 60).to_i
1833
+ minutes = (seconds / 60).to_i
1871
1834
  hours = (minutes / 60).to_i
1872
1835
  days = (hours / 24).to_i
1873
1836
  hours = (hours % 24).to_i
1874
1837
  minutes = (minutes % 60).to_i
1875
1838
  [days, hours, minutes]
1876
1839
  end
1877
-
1878
1840
  end