doing 1.0.65 → 1.0.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a774718572df78395293b6294bcfa4bac94cfe2cde0ffec43e8bbea60b874534
4
- data.tar.gz: fba4b65439084eae5b291bd65f5a1d14d49e5b5c4c8b70abd5e8f73fa39b3545
3
+ metadata.gz: bf5235acbab516c74ea7ff28c610dcbc49b9d9f108056c1b3c60ac0100bd9ec5
4
+ data.tar.gz: ca6abc5bd66a950d78ab16aabc781660b0ea5ee3f6dc50bb8a9fd0b986cce21e
5
5
  SHA512:
6
- metadata.gz: 01bf52dbc6e7077e6ff2a67ab22504dda4be927f31da8be7e5383136b5cac500cd7920203b7383ebe302af5ddce375e0ee7389273a275edfde27971de8ae87bb
7
- data.tar.gz: 79e7b8dcf00223118a0e3f5bb648ed33e920dc915909f62d1c21f2a19332f3d35336d3e5ca85a31632416b9fb9364d58db2fb6a4f90e7a200bc7cb706f76e14b
6
+ metadata.gz: b9f62baa0a4348212c54c1f9f3aa5bca041d81bc5061221310a85747b05c0b9b7af9036d309fcffd78e192a7a415f462ceeb96f8c276cef320249651f4890d35
7
+ data.tar.gz: cc61feaf78e9a3d6865bbb3d5fecdff00659b9b00a7430971012bfca0856e45ee87efd864dde603a7a6e6abf6a571f01f0e8d60afbbb7ebe060364f65c908b6b
data/README.md CHANGED
@@ -29,7 +29,7 @@ _Side note:_ I actually use the library behind this utility as part of another s
29
29
 
30
30
  ## Installation
31
31
 
32
- The current version of `doing` is <!--VER-->1.0.64<!--END VER-->.
32
+ The current version of `doing` is <!--VER-->1.0.68<!--END VER-->.
33
33
 
34
34
  $ [sudo] gem install doing
35
35
 
@@ -464,6 +464,7 @@ You can also include a `--no-date` switch to add `@done` without a finish date,
464
464
 
465
465
  By default `doing finish` works on a single entry, the last entry or the most recent entry matching a `--tag` or `--search` query. Specifying `doing finish 10` would finish any unfinished entries within the last 10 entries. In the case of `--tag` or `--search` queries, the count serves as the maximum number of matches doing will act on, sorted in reverse date order (most recent first). A count of 0 will disable the limit entirely, acting on all matching entries.
466
466
 
467
+ Both `finish` and `cancel` accept `--unfinished` as an argument. This causes them to act on the last entry not already marked @done, no matter how far back it's dated or how many @done entries come after it. You can use `doing finish --unfinished X -s SECTION` to finish the last X unfinished entries in SECTION.
467
468
 
468
469
  ##### Tagging and Autotagging
469
470
 
data/bin/doing CHANGED
@@ -52,7 +52,7 @@ flag %i[f doing_file]
52
52
 
53
53
  desc 'Add an entry'
54
54
  arg_name 'ENTRY'
55
- command [:now, :next] do |c|
55
+ command %i[now next] do |c|
56
56
  c.desc 'Section'
57
57
  c.arg_name 'NAME'
58
58
  c.flag %i[s section], default_value: wwid.current_section
@@ -105,7 +105,8 @@ command [:now, :next] do |c|
105
105
  wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:f] })
106
106
  wwid.write(wwid.doing_file)
107
107
  elsif $stdin.stat.size.positive?
108
- title, note = wwid.format_input($stdin.read)
108
+ input = $stdin.read
109
+ title, note = wwid.format_input(input)
109
110
  note.push(options[:n]) if options[:n]
110
111
  wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:f] })
111
112
  wwid.write(wwid.doing_file)
@@ -239,7 +240,7 @@ long_desc %(
239
240
 
240
241
  Example `doing template HAML > ~/styles/my_doing.haml`
241
242
  )
242
- arg_name 'TYPE', must_match: /^(html|haml|css)/i
243
+ arg_name 'TYPE', must_match: /^(?:html|haml|css)/i
243
244
  command :template do |c|
244
245
  c.action do |_global_options, options, args|
245
246
  raise 'No type specified, use `doing template [HAML|CSS]`' if args.empty?
@@ -443,7 +444,10 @@ command :cancel do |c|
443
444
 
444
445
  c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters'
445
446
  c.arg_name 'BOOLEAN'
446
- c.flag [:bool], must_match: /^(and|or|not)$/i, default_value: 'AND'
447
+ c.flag [:bool], must_match: /(?:and|all|any|or|not|none)/i, default_value: 'AND'
448
+
449
+ c.desc 'Cancel last entry (or entries) not already marked @done'
450
+ c.switch %i[u unfinished], negatable: false, default_value: false
447
451
 
448
452
  c.action do |_global_options, options, args|
449
453
  section = wwid.guess_section(options[:s]) || options[:s].cap_first
@@ -452,7 +456,16 @@ command :cancel do |c|
452
456
  tags = []
453
457
  else
454
458
  tags = options[:tag].split(/ *, */).map { |t| t.strip.sub(/^@/, '') }
455
- options[:bool] = options[:bool] =~ /^(and|or|not)$/i ? options[:bool].upcase : 'AND'
459
+ options[:bool] = case options[:bool]
460
+ when /(and|all)/i
461
+ 'AND'
462
+ when /(any|or)/i
463
+ 'OR'
464
+ when /(not|none)/i
465
+ 'NOT'
466
+ else
467
+ 'AND'
468
+ end
456
469
  end
457
470
 
458
471
  raise 'Only one argument allowed' if args.length > 1
@@ -468,7 +481,8 @@ command :cancel do |c|
468
481
  sequential: false,
469
482
  tag: tags,
470
483
  tag_bool: options[:bool],
471
- tags: ['done']
484
+ tags: ['done'],
485
+ unfinished: options[:unfinished]
472
486
  }
473
487
  wwid.tag_last(opts)
474
488
  end
@@ -500,7 +514,10 @@ command :finish do |c|
500
514
 
501
515
  c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters'
502
516
  c.arg_name 'BOOLEAN'
503
- c.flag [:bool], must_match: /^(and|or|not)$/i, default_value: 'AND'
517
+ c.flag [:bool], must_match: /(?:and|all|any|or|not|none)/i, default_value: 'AND'
518
+
519
+ c.desc 'Finish last entry (or entries) not already marked @done'
520
+ c.switch %i[u unfinished], negatable: false, default_value: false
504
521
 
505
522
  c.desc %(Auto-generate finish dates from next entry's start time.
506
523
  Automatically generate completion dates 1 minute before next start date.
@@ -537,7 +554,16 @@ command :finish do |c|
537
554
  tags = []
538
555
  else
539
556
  tags = options[:tag].split(/ *, */).map { |t| t.strip.sub(/^@/, '') }
540
- options[:bool] = options[:bool] =~ /^(and|or|not)$/i ? options[:bool].upcase : 'AND'
557
+ options[:bool] = case options[:bool]
558
+ when /(and|all)/i
559
+ 'AND'
560
+ when /(any|or)/i
561
+ 'OR'
562
+ when /(not|none)/i
563
+ 'NOT'
564
+ else
565
+ 'AND'
566
+ end
541
567
  end
542
568
 
543
569
  raise 'Only one argument allowed' if args.length > 1
@@ -555,7 +581,8 @@ command :finish do |c|
555
581
  sequential: options[:auto],
556
582
  tag: tags,
557
583
  tag_bool: options[:bool],
558
- tags: ['done']
584
+ tags: ['done'],
585
+ unfinished: options[:unfinished]
559
586
  }
560
587
  wwid.tag_last(opts)
561
588
  end
@@ -582,7 +609,7 @@ command [:again, :resume] do |c|
582
609
 
583
610
  c.desc 'Boolean used to combine multiple tags'
584
611
  c.arg_name 'BOOLEAN'
585
- c.flag [:bool], must_match: /^(and|or|not)$/i, default_value: 'AND'
612
+ c.flag [:bool], must_match: /(?:and|all|any|or|not|none)/i, default_value: 'AND'
586
613
 
587
614
  c.desc 'Note'
588
615
  c.arg_name 'TEXT'
@@ -590,6 +617,16 @@ command [:again, :resume] do |c|
590
617
 
591
618
  c.action do |_global_options, options, _args|
592
619
  tags = options[:tag].nil? ? [] : options[:tag].split(/ *, */).map { |t| t.sub(/^@/, '').strip }
620
+ options[:bool] = case options[:bool]
621
+ when /(and|all)/i
622
+ 'AND'
623
+ when /(any|or)/i
624
+ 'OR'
625
+ when /(not|none)/i
626
+ 'NOT'
627
+ else
628
+ 'AND'
629
+ end
593
630
  opts = {
594
631
  in: options[:in],
595
632
  note: options[:n],
@@ -619,6 +656,9 @@ command :tag do |c|
619
656
  c.desc 'Remove given tag(s)'
620
657
  c.switch %i[r remove], negatable: false, default_value: false
621
658
 
659
+ c.desc 'Tag last entry (or entries) not marked @done'
660
+ c.switch %i[u unfinished], negatable: false, default_value: false
661
+
622
662
  c.desc 'Autotag entries based on autotag configuration in ~/.doingrc'
623
663
  c.switch %i[a autotag], negatable: false, default_value: false
624
664
 
@@ -662,7 +702,8 @@ command :tag do |c|
662
702
  date: options[:date],
663
703
  remove: options[:r],
664
704
  section: section,
665
- tags: tags
705
+ tags: tags,
706
+ unfinished: options[:unfinished]
666
707
  }
667
708
  wwid.tag_last(opts)
668
709
  end
@@ -677,9 +718,17 @@ command [:mark, :flag] do |c|
677
718
  c.desc 'Remove mark'
678
719
  c.switch %i[r remove], negatable: false, default_value: false
679
720
 
721
+ c.desc 'Mark last entry not marked @done'
722
+ c.switch %i[u unfinished], negatable: false, default_value: false
723
+
680
724
  c.action do |_global_options, options, _args|
681
725
  mark = wwid.config['marker_tag'] || 'flagged'
682
- wwid.tag_last({ tags: [mark], section: options[:s], remove: options[:r] })
726
+ wwid.tag_last({
727
+ remove: options[:r],
728
+ section: options[:s],
729
+ tags: [mark],
730
+ unfinished: options[:unfinished]
731
+ })
683
732
  end
684
733
  end
685
734
 
@@ -694,9 +743,9 @@ command :show do |c|
694
743
  c.arg_name 'TAG'
695
744
  c.flag [:tag]
696
745
 
697
- c.desc 'Tag boolean (AND,OR,NONE)'
746
+ c.desc 'Tag boolean (AND,OR,NOT)'
698
747
  c.arg_name 'BOOLEAN'
699
- c.flag %i[b bool], must_match: /^(and|or|not)$/i, default_value: 'OR'
748
+ c.flag %i[b bool], must_match: /(?:and|all|any|or|not|none)/i, default_value: 'OR'
700
749
 
701
750
  c.desc 'Max count to show'
702
751
  c.arg_name 'MAX'
@@ -708,7 +757,7 @@ command :show do |c|
708
757
 
709
758
  c.desc 'Sort order (asc/desc)'
710
759
  c.arg_name 'ORDER'
711
- c.flag %i[s sort], must_match: /^(a|d)/i, default_value: 'ASC'
760
+ c.flag %i[s sort], must_match: /^[ad].*/i, default_value: 'ASC'
712
761
 
713
762
  c.desc %(
714
763
  Date range to show, or a single day to filter date on.
@@ -728,14 +777,14 @@ command :show do |c|
728
777
  default = 'time'
729
778
  default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
730
779
  c.arg_name 'KEY'
731
- c.flag [:tag_sort], must_match: /^(name|time)/i, default_value: default
780
+ c.flag [:tag_sort], must_match: /^(?:name|time)/i, default_value: default
732
781
 
733
782
  c.desc 'Only show items with recorded time intervals'
734
783
  c.switch [:only_timed], default_value: false, negatable: false
735
784
 
736
785
  c.desc 'Output to export format (csv|html|json|template|timeline)'
737
786
  c.arg_name 'FORMAT'
738
- c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
787
+ c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline)$/i
739
788
  c.action do |_global_options, options, args|
740
789
  tag_filter = false
741
790
  tags = []
@@ -757,13 +806,9 @@ command :show do |c|
757
806
  end
758
807
  if args.length.positive?
759
808
  args.each do |arg|
760
- if arg =~ /,/
761
- arg.split(/,/).each do |tag|
762
- tags.push(tag.strip.sub(/^@/, ''))
763
- end
764
- else
765
- tags.push(arg.strip.sub(/^@/, ''))
766
- end
809
+ arg.split(/,/).each do |tag|
810
+ tags.push(tag.strip.sub(/^@/, ''))
811
+ end
767
812
  end
768
813
  end
769
814
  else
@@ -771,11 +816,21 @@ command :show do |c|
771
816
  end
772
817
 
773
818
  tags.concat(options[:tag].split(/ *, */).map { |t| t.sub(/^@/, '').strip }) if options[:tag]
819
+ options[:bool] = case options[:bool]
820
+ when /(and|all)/i
821
+ 'AND'
822
+ when /(any|or)/i
823
+ 'OR'
824
+ when /(not|none)/i
825
+ 'NOT'
826
+ else
827
+ 'AND'
828
+ end
774
829
 
775
830
  unless tags.empty?
776
831
  tag_filter = {
777
832
  'tags' => tags,
778
- 'bool' => options[:b]
833
+ 'bool' => options[:bool]
779
834
  }
780
835
  end
781
836
 
@@ -832,7 +887,7 @@ command [:grep, :search] do |c|
832
887
 
833
888
  c.desc 'Output to export format (csv|html|json|template|timeline)'
834
889
  c.arg_name 'FORMAT'
835
- c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
890
+ c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline)$/i
836
891
 
837
892
  c.desc 'Show time intervals on @done tasks'
838
893
  c.switch %i[t times], default_value: true
@@ -844,7 +899,7 @@ command [:grep, :search] do |c|
844
899
  default = 'time'
845
900
  default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
846
901
  c.arg_name 'KEY'
847
- c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
902
+ c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
848
903
 
849
904
  c.desc 'Only show items with recorded time intervals'
850
905
  c.switch [:only_timed], default_value: false, negatable: false
@@ -891,7 +946,7 @@ command :recent do |c|
891
946
  default = 'time'
892
947
  default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
893
948
  c.arg_name 'KEY'
894
- c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
949
+ c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
895
950
 
896
951
  c.action do |global_options, options, args|
897
952
  section = wwid.guess_section(options[:s]) || options[:s].cap_first
@@ -931,11 +986,11 @@ command :today do |c|
931
986
  default = 'time'
932
987
  default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
933
988
  c.arg_name 'KEY'
934
- c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
989
+ c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
935
990
 
936
991
  c.desc 'Output to export format (csv|html|json|template|timeline)'
937
992
  c.arg_name 'FORMAT'
938
- c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
993
+ c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline)$/i
939
994
 
940
995
  c.action do |_global_options, options, _args|
941
996
  options[:t] = true if options[:totals]
@@ -966,11 +1021,11 @@ command :on do |c|
966
1021
  default = 'time'
967
1022
  default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
968
1023
  c.arg_name 'KEY'
969
- c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
1024
+ c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
970
1025
 
971
1026
  c.desc 'Output to export format (csv|html|json|template|timeline)'
972
1027
  c.arg_name 'FORMAT'
973
- c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
1028
+ c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline)$/i
974
1029
 
975
1030
  c.action do |_global_options, options, args|
976
1031
  exit_now! 'Missing date argument' if args.empty?
@@ -1008,7 +1063,7 @@ command :yesterday do |c|
1008
1063
 
1009
1064
  c.desc 'Output to export format (csv|html|json|template|timeline)'
1010
1065
  c.arg_name 'FORMAT'
1011
- c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
1066
+ c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline)$/i
1012
1067
 
1013
1068
  c.desc 'Show time intervals on @done tasks'
1014
1069
  c.switch %i[t times], default_value: true
@@ -1020,7 +1075,7 @@ command :yesterday do |c|
1020
1075
  default = 'time'
1021
1076
  default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
1022
1077
  c.arg_name 'KEY'
1023
- c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
1078
+ c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
1024
1079
 
1025
1080
  c.action do |_global_options, options, _args|
1026
1081
  options[:sort_tags] = options[:tag_sort] =~ /^n/i
@@ -1044,7 +1099,7 @@ command :last do |c|
1044
1099
 
1045
1100
  c.desc 'Tag boolean'
1046
1101
  c.arg_name 'BOOLEAN'
1047
- c.flag [:bool], must_match: /(and|or|not)/i, default_value: 'AND'
1102
+ c.flag [:bool], must_match: /(?:and|all|any|or|not|none)/i, default_value: 'AND'
1048
1103
 
1049
1104
  c.desc 'Search filter, surround with slashes for regex (/query/)'
1050
1105
  c.arg_name 'QUERY'
@@ -1057,7 +1112,15 @@ command :last do |c|
1057
1112
  tags = []
1058
1113
  else
1059
1114
  tags = options[:tag].split(/ *, */).map { |t| t.strip.sub(/^@/, '') }
1060
- options[:bool] = options[:bool] =~ /^(and|or|not)$/i ? options[:bool].upcase : 'AND'
1115
+ options[:bool] = case options[:bool]
1116
+ when /(any|or)/i
1117
+ :or
1118
+ when /(not|none)/i
1119
+ :not
1120
+ else
1121
+ :and
1122
+ end
1123
+
1061
1124
  end
1062
1125
 
1063
1126
  if options[:e]
@@ -1130,7 +1193,7 @@ command :view do |c|
1130
1193
 
1131
1194
  c.desc 'Output to export format (csv|html|json|template|timeline)'
1132
1195
  c.arg_name 'FORMAT'
1133
- c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
1196
+ c.flag %i[o output], must_match: /^(?:template|html|csv|json|timeline)$/i
1134
1197
 
1135
1198
  c.desc 'Show time intervals on @done tasks'
1136
1199
  c.switch %i[t times], default_value: true
@@ -1145,7 +1208,7 @@ command :view do |c|
1145
1208
  default = 'time'
1146
1209
  default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
1147
1210
  c.arg_name 'KEY'
1148
- c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
1211
+ c.flag [:tag_sort], must_match: /^(?:name|time)$/i, default_value: default
1149
1212
 
1150
1213
  c.desc 'Only show items with recorded time intervals'
1151
1214
  c.switch [:only_timed], default_value: false, negatable: true
@@ -1178,7 +1241,7 @@ command :view do |c|
1178
1241
  else
1179
1242
  view['tags'].gsub(/[, ]+/, ' ').split(' ').map(&:strip)
1180
1243
  end
1181
- tag_filter['bool'] = view.key?('tags_bool') && !view['tags_bool'].nil? ? view['tags_bool'].upcase : 'OR'
1244
+ tag_filter['bool'] = view.key?('tags_bool') && !view['tags_bool'].nil? ? view['tags_bool'].normalize_bool : :or
1182
1245
  end
1183
1246
 
1184
1247
  # If the -o/--output flag was specified, override any default in the view template
@@ -1257,7 +1320,7 @@ command :archive do |c|
1257
1320
 
1258
1321
  c.desc 'Tag boolean (AND|OR|NOT)'
1259
1322
  c.arg_name 'BOOLEAN'
1260
- c.flag [:bool], must_match: /(and|or|not)/i, default_value: 'AND'
1323
+ c.flag [:bool], must_match: /(?:and|all|any|or|not|none)/i, default_value: 'AND'
1261
1324
 
1262
1325
  c.desc 'Search filter'
1263
1326
  c.arg_name 'QUERY'
@@ -1281,6 +1344,16 @@ command :archive do |c|
1281
1344
 
1282
1345
  tags.concat(options[:tag].split(/ *, */).map { |t| t.sub(/^@/, '').strip }) if options[:tag]
1283
1346
 
1347
+ options[:bool] = case options[:bool]
1348
+ when /(and|all)/i
1349
+ 'AND'
1350
+ when /(any|or)/i
1351
+ 'OR'
1352
+ when /(not|none)/i
1353
+ 'NOT'
1354
+ else
1355
+ 'AND'
1356
+ end
1284
1357
  opts = {
1285
1358
  bool: options[:bool],
1286
1359
  destination: options[:to],
data/lib/doing.rb CHANGED
@@ -8,4 +8,5 @@ require 'tempfile'
8
8
  require 'chronic'
9
9
  require 'haml'
10
10
  require 'json'
11
+ require 'doing/helpers.rb'
11
12
  require 'doing/wwid.rb'
@@ -0,0 +1,121 @@
1
+ ##
2
+ ## @brief Hash helpers
3
+ ##
4
+ class ::Hash
5
+ def has_tags?(tags, bool = :and)
6
+ tags = tags.split(/ *, */) if tags.is_a? String
7
+ bool = bool.normalize_bool if bool.is_a? String
8
+ item = self
9
+ tags.map! {|t| t.strip.sub(/^@/, '')}
10
+ case bool
11
+ when :and
12
+ result = true
13
+ tags.each do |tag|
14
+ unless item['title'] =~ /@#{tag}/
15
+ result = false
16
+ break
17
+ end
18
+ end
19
+ result
20
+ when :not
21
+ result = true
22
+ tags.each do |tag|
23
+ if item['title'] =~ /@#{tag}/
24
+ result = false
25
+ break
26
+ end
27
+ end
28
+ result
29
+ else
30
+ result = false
31
+ tags.each do |tag|
32
+ if item['title'] =~ /@#{tag}/
33
+ result = true
34
+ break
35
+ end
36
+ end
37
+ result
38
+ end
39
+ end
40
+
41
+ def matches_search?(search)
42
+ item = self
43
+ text = item['note'] ? item['title'] + item['note'].join(' ') : item['title']
44
+ pattern = if search.strip =~ %r{^/.*?/$}
45
+ search.sub(%r{/(.*?)/}, '\1')
46
+ else
47
+ search.split('').join('.{0,3}')
48
+ end
49
+ text =~ /#{pattern}/i ? true : false
50
+ end
51
+ end
52
+
53
+ ##
54
+ ## @brief String helpers
55
+ ##
56
+ class ::String
57
+ def cap_first
58
+ sub(/^\w/) do |m|
59
+ m.upcase
60
+ end
61
+ end
62
+
63
+
64
+ ##
65
+ ## @brief Convert a boolean string to a symbol
66
+ ##
67
+ ## @return Symbol :and, :or, or :not
68
+ ##
69
+ def normalize_bool!
70
+ replace normalize_bool
71
+ end
72
+
73
+ def normalize_bool(default = :and)
74
+ case self
75
+ when /(and|all)/i
76
+ :and
77
+ when /(any|or)/i
78
+ :or
79
+ when /(not|none)/i
80
+ :not
81
+ else
82
+ default.is_a?(Symbol) ? default : default.normalize_bool
83
+ end
84
+ end
85
+
86
+
87
+ ##
88
+ ## @brief Turn raw urls into HTML links
89
+ ##
90
+ ## @param opt (Hash) Additional Options
91
+ ##
92
+ def link_urls(opt = {})
93
+ opt[:format] ||= :html
94
+ if opt[:format] == :html
95
+ gsub(%r{(?mi)((http|https)://)?([\w\-_]+(\.[\w\-_]+)+)([\w\-.,@?^=%&amp;:/~+#]*[\w\-@^=%&amp;/~+#])?}) do |_match|
96
+ m = Regexp.last_match
97
+ proto = m[1].nil? ? 'http://' : ''
98
+ %(<a href="#{proto}#{m[0]}" title="Link to #{m[0]}">[#{m[3]}]</a>)
99
+ end.gsub(/<(\w+:.*?)>/) do |match|
100
+ m = Regexp.last_match
101
+ if m[1] =~ /<a href/
102
+ match
103
+ else
104
+ %(<a href="#{m[1]}" title="Link to #{m[1]}">[link]</a>)
105
+ end
106
+ end
107
+ else
108
+ self
109
+ end
110
+ end
111
+ end
112
+
113
+ class ::Symbol
114
+ def normalize_bool!
115
+ replace normalize_bool
116
+ end
117
+
118
+ def normalize_bool
119
+ to_s.normalize_bool
120
+ end
121
+ end
data/lib/doing/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Doing
2
- VERSION = '1.0.65'
2
+ VERSION = '1.0.69'
3
3
  end
data/lib/doing/wwid.rb CHANGED
@@ -4,92 +4,6 @@ require 'deep_merge'
4
4
  require 'open3'
5
5
  require 'pp'
6
6
 
7
- ##
8
- ## @brief Hash helpers
9
- ##
10
- class ::Hash
11
- def has_tags?(tags, bool = 'AND')
12
- tags = tags.split(/ *, */) if tags.is_a? String
13
- item = self
14
- case bool
15
- when 'AND'
16
- result = true
17
- tags.each do |tag|
18
- unless item['title'] =~ /@#{tag}/
19
- result = false
20
- break
21
- end
22
- end
23
- result
24
- when 'NOT'
25
- result = true
26
- tags.each do |tag|
27
- if item['title'] =~ /@#{tag}/
28
- result = false
29
- break
30
- end
31
- end
32
- result
33
- else
34
- result = false
35
- tags.each do |tag|
36
- if item['title'] =~ /@#{tag}/
37
- result = true
38
- break
39
- end
40
- end
41
- result
42
- end
43
- end
44
-
45
- def matches_search?(search)
46
- item = self
47
- text = item['note'] ? item['title'] + item['note'].join(' ') : item['title']
48
- pattern = if search.strip =~ %r{^/.*?/$}
49
- search.sub(%r{/(.*?)/}, '\1')
50
- else
51
- search.split('').join('.{0,3}')
52
- end
53
- text =~ /#{pattern}/i ? true : false
54
- end
55
- end
56
-
57
- ##
58
- ## @brief String helpers
59
- ##
60
- class String
61
- def cap_first
62
- sub(/^\w/) do |m|
63
- m.upcase
64
- end
65
- end
66
-
67
- ##
68
- ## @brief Turn raw urls into HTML links
69
- ##
70
- ## @param opt (Hash) Additional Options
71
- ##
72
- def link_urls(opt = {})
73
- opt[:format] ||= :html
74
- if opt[:format] == :html
75
- gsub(%r{(?mi)((http|https)://)?([\w\-_]+(\.[\w\-_]+)+)([\w\-.,@?^=%&amp;:/~+#]*[\w\-@^=%&amp;/~+#])?}) do |_match|
76
- m = Regexp.last_match
77
- proto = m[1].nil? ? 'http://' : ''
78
- %(<a href="#{proto}#{m[0]}" title="Link to #{m[0]}">[#{m[3]}]</a>)
79
- end.gsub(/<(\w+:.*?)>/) do |match|
80
- m = Regexp.last_match
81
- if m[1] =~ /<a href/
82
- match
83
- else
84
- %(<a href="#{m[1]}" title="Link to #{m[1]}">[link]</a>)
85
- end
86
- end
87
- else
88
- self
89
- end
90
- end
91
- end
92
-
93
7
  ##
94
8
  ## @brief Main "What Was I Doing" methods
95
9
  ##
@@ -791,7 +705,7 @@ class WWID
791
705
  opt[:section] ||= 'all'
792
706
  opt[:note] ||= []
793
707
  opt[:tag] ||= []
794
- opt[:tag_bool] ||= 'AND'
708
+ opt[:tag_bool] ||= :and
795
709
 
796
710
  last = last_entry(opt)
797
711
  if last.nil?
@@ -817,7 +731,7 @@ class WWID
817
731
  ## @param opt (Hash) Additional Options
818
732
  ##
819
733
  def last_entry(opt = {})
820
- opt[:tag_bool] ||= 'AND'
734
+ opt[:tag_bool] ||= :and
821
735
  opt[:section] ||= @current_section
822
736
 
823
737
  sec_arr = []
@@ -867,6 +781,7 @@ class WWID
867
781
  opt[:autotag] ||= false
868
782
  opt[:back] ||= false
869
783
  opt[:took] ||= nil
784
+ opt[:unfinished] ||= false
870
785
 
871
786
  sec_arr = []
872
787
 
@@ -902,10 +817,11 @@ class WWID
902
817
  items.map! do |item|
903
818
  break if idx == count
904
819
 
820
+ finished = opt[:unfinished] && item.has_tags?('done', :and)
905
821
  tag_match = opt[:tag].nil? || opt[:tag].empty? ? true : item.has_tags?(opt[:tag], opt[:tag_bool])
906
822
  search_match = opt[:search].nil? || opt[:search].empty? ? true : item.matches_search?(opt[:search])
907
823
 
908
- if tag_match && search_match
824
+ if tag_match && search_match && !finished
909
825
  if opt[:autotag]
910
826
  new_title = autotag(item['title']) if @auto_tag
911
827
  if new_title == item['title']
@@ -1308,7 +1224,7 @@ class WWID
1308
1224
  @content.each do |_k, v|
1309
1225
  combined['items'] += v['items']
1310
1226
  end
1311
- section = if opt[:tag_filter] && opt[:tag_filter]['bool'] != 'NONE'
1227
+ section = if opt[:tag_filter] && opt[:tag_filter]['bool'].normalize_bool != :not
1312
1228
  opt[:tag_filter]['tags'].map do |tag|
1313
1229
  "@#{tag}"
1314
1230
  end.join(' + ')
@@ -1339,31 +1255,7 @@ class WWID
1339
1255
  end
1340
1256
 
1341
1257
  if opt[:tag_filter] && !opt[:tag_filter]['tags'].empty?
1342
- items.delete_if do |item|
1343
- case opt[:tag_filter]['bool']
1344
- when /(AND|ALL)/
1345
- del = false
1346
- opt[:tag_filter]['tags'].each do |tag|
1347
- unless item['title'] =~ /@#{tag}/
1348
- del = true
1349
- break
1350
- end
1351
- end
1352
- del
1353
- when /NONE/
1354
- del = false
1355
- opt[:tag_filter]['tags'].each do |tag|
1356
- del = true if item['title'] =~ /@#{tag}/
1357
- end
1358
- del
1359
- when /(OR|ANY)/
1360
- del = true
1361
- opt[:tag_filter]['tags'].each do |tag|
1362
- del = false if item['title'] =~ /@#{tag}/
1363
- end
1364
- del
1365
- end
1366
- end
1258
+ items.select! { |item| item.has_tags?(opt[:tag_filter]['tags'], opt[:tag_filter]['bool']) }
1367
1259
  end
1368
1260
 
1369
1261
  if opt[:search]
@@ -1655,7 +1547,7 @@ class WWID
1655
1547
  count = options[:keep] || 0
1656
1548
  destination = options[:destination] || 'Archive'
1657
1549
  tags = options[:tags] || []
1658
- bool = options[:bool] || 'AND'
1550
+ bool = options[:bool] || :and
1659
1551
 
1660
1552
  section = choose_section if section.nil? || section =~ /choose/i
1661
1553
  archive_all = section =~ /^all$/i # && !(tags.nil? || tags.empty?)
@@ -1683,7 +1575,7 @@ class WWID
1683
1575
  def do_archive(sect, destination, opt = {})
1684
1576
  count = opt[:count] || 0
1685
1577
  tags = opt[:tags] || []
1686
- bool = opt[:bool] || 'AND'
1578
+ bool = opt[:bool] || :and
1687
1579
  label = opt[:label] || true
1688
1580
 
1689
1581
  if sect =~ /^all$/i
@@ -1696,7 +1588,7 @@ class WWID
1696
1588
  counter = 0
1697
1589
 
1698
1590
  all_sections.each do |section|
1699
- items = @content[section]['items']
1591
+ items = @content[section]['items'].dup
1700
1592
 
1701
1593
  moved_items = []
1702
1594
  if !tags.empty? || opt[:search]
@@ -1720,13 +1612,7 @@ class WWID
1720
1612
  @content[destination]['items'].concat(moved_items)
1721
1613
  @results.push("Archived #{moved_items.length} items from #{section} to #{destination}")
1722
1614
  else
1723
- count = items.length if count == 0 || items.length < count
1724
-
1725
- @content[section]['items'] = if count.zero?
1726
- []
1727
- else
1728
- items[0..count - 1]
1729
- end
1615
+ count = items.length if items.length < count
1730
1616
 
1731
1617
  items.map! do |item|
1732
1618
  if label && section != 'Currently'
@@ -1735,12 +1621,19 @@ class WWID
1735
1621
  end
1736
1622
  item
1737
1623
  end
1624
+
1738
1625
  if items.count > count
1739
1626
  @content[destination]['items'].concat(items[count..-1])
1740
1627
  else
1741
1628
  @content[destination]['items'].concat(items)
1742
1629
  end
1743
1630
 
1631
+ @content[section]['items'] = if count.zero?
1632
+ []
1633
+ else
1634
+ items[0..count - 1]
1635
+ end
1636
+
1744
1637
  @results.push("Archived #{items.length - count} items from #{section} to #{destination}")
1745
1638
  end
1746
1639
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: doing
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.65
4
+ version: 1.0.69
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-24 00:00:00.000000000 Z
11
+ date: 2021-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -165,6 +165,7 @@ files:
165
165
  - README.md
166
166
  - bin/doing
167
167
  - lib/doing.rb
168
+ - lib/doing/helpers.rb
168
169
  - lib/doing/version.rb
169
170
  - lib/doing/wwid.rb
170
171
  - lib/templates/doing.css