howzit 2.0.15 → 2.0.18

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: bb673909d87ac094a2111c1f6fa364fb54183059267e009886562c89a81310d7
4
- data.tar.gz: ec766bf5d2f3b3454a289f429e28c12f19545a79dfe6f10d29f0afb04abdc9b2
3
+ metadata.gz: 16fc4dc237a8f226da219e66cbaee59984e8931bfe8e03fe5b0126451fb0d0d0
4
+ data.tar.gz: a169f5a88a1a6b345b6732c4782d166d7c933a6ce986a66b14e9df35602e1b7b
5
5
  SHA512:
6
- metadata.gz: b5f2e3624bad256848b1c2d8a4bc95dcbbee5d8cf0e2c63efa79b139ca3a330ce773edb2e68375ea72158de0ce69ffaedd7673f36cbee5cd937c255ccaa3e5fe
7
- data.tar.gz: f024cd0ffb6b41ab6184cf36ef65e644e90f5ab7e5c57b8a2c9cc1be404802f992f708464a3a2ab984122fddb4880ec704cda85dd371aeee735b417e15d942c2
6
+ metadata.gz: d197932f7a501e0a4ffb5eb76ca60660223b98cee1346c573028922b05790652a8e8c08a4624c72645423145d342d0b3866580d9b05552fe72a87edda47b4a08
7
+ data.tar.gz: 4e318240e7db8d6117fec620fd5505afab5ba35427b391e6a1dc90b4eccb978f6865054d300d00dad574ea4edc5ccb006c74112fca78f00dce14dc07c55095da
data/CHANGELOG.md CHANGED
@@ -1,3 +1,27 @@
1
+ ### 2.0.18
2
+
3
+ 2022-08-08 03:48
4
+
5
+ #### NEW
6
+
7
+ - Use --ask to require confirmation of all tasks when running a topic
8
+
9
+ ### 2.0.17
10
+
11
+ 2022-08-07 06:09
12
+
13
+ ### 2.0.16
14
+
15
+ 2022-08-07 06:05
16
+
17
+ #### NEW
18
+
19
+ - --edit-template NAME will open a template in your editor, offering to create it if missing
20
+
21
+ #### FIXED
22
+
23
+ - If --show-code is given (or :show_all_code: is set to true), show content of @directives instead of title
24
+
1
25
  ### 2.0.15
2
26
 
3
27
  2022-08-05 17:16
data/bin/howzit CHANGED
@@ -20,6 +20,10 @@ OptionParser.new do |opts|
20
20
 
21
21
  opts.separator " Behavior:\n\n" #=================================================================== BEHAVIOR
22
22
 
23
+ opts.on('--ask', 'Request confirmation for all tasks when running a topic') do
24
+ Howzit.options[:ask] = true
25
+ end
26
+
23
27
  opts.on('--default', 'Answer all prompts with default response') do
24
28
  Howzit.options[:default] = true
25
29
  end
@@ -94,6 +98,11 @@ OptionParser.new do |opts|
94
98
  Process.exit 0
95
99
  end
96
100
 
101
+ opts.on('--edit-template NAME', 'Create or edit a template') do |template|
102
+ Howzit.buildnote.edit_template(template)
103
+ Process.exit 0
104
+ end
105
+
97
106
  opts.on('--config-get [KEY]', 'Display the configuration settings or setting for a specific key') do |k|
98
107
  if k.nil?
99
108
  Howzit::Config::DEFAULTS.sort_by { |key, _| key }.each do |key, _|
@@ -11,23 +11,15 @@ module Howzit
11
11
  ## Initialize a build note
12
12
  ##
13
13
  ## @param file [String] The path to the build note file
14
- ## @param args [Array] additional args
15
14
  ##
16
- def initialize(file: nil, args: [])
15
+ def initialize(file: nil)
17
16
  @topics = []
18
- if note_file.nil?
19
- res = Prompt.yn('No build notes file found, create one?', default: true)
17
+ create_note(prompt: true) if note_file.nil?
20
18
 
21
- create_note if res
22
- Process.exit 0
23
- end
24
19
  content = Util.read_file(note_file)
25
- if content.nil? || content.empty?
26
- Howzit.console.error("{br}No content found in build note (#{note_file}){x}".c)
27
- Process.exit 1
28
- else
29
- @metadata = content.split(/^#/)[0].strip.get_metadata
30
- end
20
+ raise "{br}No content found in build note (#{note_file}){x}".c if content.nil? || content.empty?
21
+
22
+ @metadata = content.split(/^#/)[0].strip.get_metadata
31
23
 
32
24
  read_help(file)
33
25
  end
@@ -55,6 +47,17 @@ module Howzit
55
47
  edit_note
56
48
  end
57
49
 
50
+ ##
51
+ ## Public method to open a template in the editor
52
+ ##
53
+ ## @param template [String] The template title
54
+ ##
55
+ def edit_template(template)
56
+ file = template.sub(/(\.md)?$/i, '.md')
57
+ file = File.join(Howzit.config.template_folder, file)
58
+ edit_template_file(file)
59
+ end
60
+
58
61
  ##
59
62
  ## Find a topic based on a fuzzy match
60
63
  ##
@@ -62,6 +65,7 @@ module Howzit
62
65
  ##
63
66
  def find_topic(term = nil)
64
67
  return @topics if term.nil?
68
+
65
69
  @topics.filter do |topic|
66
70
  rx = term.to_rx
67
71
  topic.title.downcase =~ rx
@@ -137,15 +141,14 @@ module Howzit
137
141
  find_topic(Howzit.options[:for_topic]).each do |topic|
138
142
  s_out = []
139
143
 
140
- topic.tasks.each do |task|
141
- s_out.push(task.to_list)
142
- end
144
+ topic.tasks.each { |task| s_out.push(task.to_list) }
143
145
 
144
- unless s_out.empty?
145
- output.push("- {bw}#{topic.title}{x}".c)
146
- output.push(s_out.join("\n"))
147
- end
146
+ next if s_out.empty?
147
+
148
+ output.push("- {bw}#{topic.title}{x}".c)
149
+ output.push(s_out.join("\n"))
148
150
  end
151
+
149
152
  output.join("\n")
150
153
  end
151
154
 
@@ -158,22 +161,75 @@ module Howzit
158
161
  read_help_file(file)
159
162
  end
160
163
 
164
+ ##
165
+ ## Create a template file
166
+ ##
167
+ ## @param file [String] file path
168
+ ## @param prompt [Boolean] confirm file creation?
169
+ ##
170
+ def create_template_file(file, prompt: false)
171
+ trap('SIGINT') do
172
+ Howzit.console.info "\nCancelled"
173
+ exit!
174
+ end
175
+
176
+ default = !$stdout.isatty || Howzit.options[:default]
177
+
178
+ if prompt && !default && !File.exist?(file)
179
+ res = Prompt.yn("{bg}Template {bw}#{File.basename(file)}{bg} not found, create it?{x}".c, default: true)
180
+ Process.exit 0 unless res
181
+ end
182
+
183
+ title = File.basename(file, '.md')
184
+
185
+ note = <<~EOBUILDNOTES
186
+ # #{title}
187
+
188
+ ## Template Topic
189
+
190
+ EOBUILDNOTES
191
+
192
+ if File.exist?(file) && !default
193
+ file = "{by}#{file}".c
194
+ unless Prompt.yn("Are you sure you want to overwrite #{file}", default: false)
195
+ puts 'Cancelled'
196
+ Process.exit 0
197
+ end
198
+ end
199
+
200
+ File.open(file, 'w') do |f|
201
+ f.puts note
202
+ puts "{by}Template {bw}#{title}{by} written to {bw}#{file}{x}".c
203
+ end
204
+
205
+ if File.exist?(file) && !default && Prompt.yn("{bg}Do you want to open {bw}#{file} {bg}for editing?{x}".c,
206
+ default: false)
207
+ edit_template_file(file)
208
+ end
209
+
210
+ Process.exit 0
211
+ end
212
+
161
213
  # Create a buildnotes skeleton
162
- def create_note
214
+ def create_note(prompt: false)
163
215
  trap('SIGINT') do
164
216
  Howzit.console.info "\nCancelled"
165
217
  exit!
166
218
  end
219
+
167
220
  default = !$stdout.isatty || Howzit.options[:default]
221
+
222
+ if prompt && !default
223
+ res = Prompt.yn('No build notes file found, create one?', default: true)
224
+ Process.exit 0 unless res
225
+ end
226
+
168
227
  # First make sure there isn't already a buildnotes file
169
228
  if note_file
170
229
  fname = "{by}#{note_file}{bw}".c
171
230
  unless default
172
231
  res = Prompt.yn("#{fname} exists and appears to be a build note, continue anyway?", default: false)
173
- unless res
174
- puts 'Canceled'
175
- Process.exit 0
176
- end
232
+ Process.exit 0 unless res
177
233
  end
178
234
  end
179
235
 
@@ -228,9 +284,7 @@ module Howzit
228
284
 
229
285
  if File.exist?(fname) && !default
230
286
  file = "{by}#{fname}".c
231
- res = Prompt.yn("Are you absolutely sure you want to overwrite #{file}", default: false)
232
-
233
- unless res
287
+ unless Prompt.yn("Are you absolutely sure you want to overwrite #{file}", default: false)
234
288
  puts 'Canceled'
235
289
  Process.exit 0
236
290
  end
@@ -238,13 +292,12 @@ module Howzit
238
292
 
239
293
  File.open(fname, 'w') do |f|
240
294
  f.puts note
241
- puts "{by}Build notes for #{title} written to #{fname}".c
295
+ puts "{by}Build notes for {bw}#{title}{by} written to {bw}#{fname}{x}".c
242
296
  end
243
297
 
244
- if File.exist?(fname) && !default
245
- res = Prompt.yn("{bg}Do you want to open {bw}#{file} {bg}for editing?{x}".c, default: false)
246
-
247
- edit_note if res
298
+ if File.exist?(fname) && !default && Prompt.yn("{bg}Do you want to open {bw}#{fname} {bg}for editing?{x}".c,
299
+ default: false)
300
+ edit_note
248
301
  end
249
302
 
250
303
  Process.exit 0
@@ -261,6 +314,139 @@ module Howzit
261
314
 
262
315
  private
263
316
 
317
+ ##
318
+ ## Import the contents of a filename as new topics
319
+ ##
320
+ ## @param mtch [MatchData] the filename match from
321
+ ## the include directive
322
+ ##
323
+ def include_file(mtch)
324
+ file = File.expand_path(mtch[1])
325
+
326
+ return mtch[0] unless File.exist?(file)
327
+
328
+ content = Util.read_file(file)
329
+ home = ENV['HOME']
330
+ short_path = File.dirname(file.sub(/^#{home}/, '~'))
331
+ prefix = "#{short_path}/#{File.basename(file)}:"
332
+ parts = content.split(/^##+/)
333
+ parts.shift
334
+ if parts.empty?
335
+ content
336
+ else
337
+ "## #{parts.join('## ')}".gsub(/^(##+ *)(?=\S)/, "\\1#{prefix}")
338
+ end
339
+ end
340
+
341
+ ##
342
+ ## Test to ensure that any `required` metadata in a
343
+ ## template is fulfilled by the build note
344
+ ##
345
+ ## @param template [String] The template to read
346
+ ## from
347
+ ##
348
+ def ensure_requirements(template)
349
+ t_leader = Util.read_file(template).split(/^#/)[0].strip
350
+ if t_leader.length > 0
351
+ t_meta = t_leader.get_metadata
352
+ if t_meta.key?('required')
353
+ required = t_meta['required'].strip.split(/\s*,\s*/)
354
+ required.each do |req|
355
+ unless @metadata.keys.include?(req.downcase)
356
+ Howzit.console.error %({bRw}ERROR:{xbr} Missing required metadata key from template '{bw}#{File.basename(template, '.md')}{xr}'{x}).c
357
+ Howzit.console.error %({br}Please define {by}#{req.downcase}{xr} in build notes{x}).c
358
+ Process.exit 1
359
+ end
360
+ end
361
+ end
362
+ end
363
+ end
364
+
365
+ ##
366
+ ## Test a template string for bracketed subtopics
367
+ ##
368
+ ## @param template [String] The template name
369
+ ##
370
+ ## @return [Array] [[String] updated template name, [Array]
371
+ ## subtopic titles]
372
+ ##
373
+ def detect_subtopics(template)
374
+ subtopics = nil
375
+
376
+ if template =~ /\[(.*?)\]$/
377
+ subtopics = Regexp.last_match[1].split(/\s*\|\s*/).map { |t| t.gsub(/\*/, '.*?')}
378
+ template.sub!(/\[.*?\]$/, '').strip
379
+ end
380
+
381
+ [template, subtopics]
382
+ end
383
+
384
+ ##
385
+ ## Enumerate templates and read their associated files
386
+ ## into topics
387
+ ##
388
+ ## @param templates [Array] The templates to read
389
+ ##
390
+ ## @return [Array] template topics
391
+ ##
392
+ def gather_templates(templates)
393
+ template_topics = []
394
+
395
+ templates.each do |template|
396
+ template, subtopics = detect_subtopics(template)
397
+
398
+ file = template.sub(/(\.md)?$/i, '.md')
399
+ file = File.join(Howzit.config.template_folder, file)
400
+
401
+ next unless File.exist?(file)
402
+
403
+ ensure_requirements(file)
404
+
405
+ template_topics.concat(read_template(template, file, subtopics))
406
+ end
407
+
408
+ template_topics
409
+ end
410
+
411
+ ##
412
+ ## Filter topics based on subtopic titles
413
+ ##
414
+ ## @param note [BuildNote] The note
415
+ ## @param subtopics [Array] The subtopics to
416
+ ## extract
417
+ ##
418
+ ## @return [Array] extracted subtopics
419
+ ##
420
+ def extract_subtopics(note, subtopics)
421
+ template_topics = []
422
+
423
+ subtopics.each do |subtopic|
424
+ note.topics.each { |topic| template_topics.push(topic) if topic.title =~ /^(.*?:)?#{subtopic}$/i }
425
+ end
426
+
427
+ template_topics
428
+ end
429
+
430
+ ##
431
+ ## Read a template file
432
+ ##
433
+ ## @param template [String] The template title
434
+ ## @param file [String] The file path
435
+ ## @param subtopics [Array] The subtopics to
436
+ ## extract, nil to return all
437
+ ##
438
+ ## @return [Array] extracted topics
439
+ ##
440
+ def read_template(template, file, subtopics = nil)
441
+ note = BuildNote.new(file: file)
442
+
443
+ template_topics = subtopics.nil? ? note.topics : extract_subtopics(note, subtopics)
444
+ template_topics.map do |topic|
445
+ topic.parent = template
446
+ topic
447
+ end
448
+ end
449
+
264
450
  ##
265
451
  ## Traverse up directory tree looking for build notes
266
452
  ##
@@ -341,30 +527,6 @@ module Howzit
341
527
  topics_dict
342
528
  end
343
529
 
344
- ##
345
- ## Test to ensure that any `required` metadata in a
346
- ## template is fulfilled by the build note
347
- ##
348
- ## @param template [String] The template to read
349
- ## from
350
- ##
351
- def ensure_requirements(template)
352
- t_leader = Util.read_file(template).split(/^#/)[0].strip
353
- if t_leader.length > 0
354
- t_meta = t_leader.get_metadata
355
- if t_meta.key?('required')
356
- required = t_meta['required'].strip.split(/\s*,\s*/)
357
- required.each do |req|
358
- unless @metadata.keys.include?(req.downcase)
359
- Howzit.console.error %({bRw}ERROR:{xbr} Missing required metadata key from template '{bw}#{File.basename(template, '.md')}{xr}'{x}).c
360
- Howzit.console.error %({br}Please define {by}#{req.downcase}{xr} in build notes{x}).c
361
- Process.exit 1
362
- end
363
- end
364
- end
365
- end
366
- end
367
-
368
530
  ##
369
531
  ## Read a list of topics from an included template
370
532
  ##
@@ -375,87 +537,17 @@ module Howzit
375
537
 
376
538
  template_topics = []
377
539
 
378
- if leader.length > 0
379
- data = leader.get_metadata
380
-
381
- if data.key?('template')
382
- templates = data['template'].strip.split(/\s*,\s*/)
383
- templates.each do |t|
384
- tasks = nil
385
- if t =~ /\[(.*?)\]$/
386
- tasks = Regexp.last_match[1].split(/\s*,\s*/).map {|t| t.gsub(/\*/, '.*?')}
387
- t = t.sub(/\[.*?\]$/, '').strip
388
- end
389
-
390
- t_file = t.sub(/(\.md)?$/, '.md')
391
- template = File.join(Howzit.config.template_folder, t_file)
392
- if File.exist?(template)
393
- ensure_requirements(template)
394
-
395
- t_topics = BuildNote.new(file: template)
396
- if tasks
397
- tasks.each do |task|
398
- t_topics.topics.each do |topic|
399
- if topic.title =~ /^(.*?:)?#{task}$/i
400
- topic.parent = t
401
- template_topics.push(topic)
402
- end
403
- end
404
- end
405
- else
406
- t_topics.topics.map! do |topic|
407
- topic.parent = t
408
- topic
409
- end
410
-
411
- template_topics.concat(t_topics.topics)
412
- end
413
- end
414
- end
415
- end
416
- end
417
- template_topics
418
- end
540
+ return template_topics if leader.empty?
419
541
 
420
- ##
421
- ## Import the contents of a filename as new topics
422
- ##
423
- ## @param mtch [MatchData] the filename match from
424
- ## the include directive
425
- ##
426
- def include_file(mtch)
427
- file = File.expand_path(mtch[1])
542
+ data = leader.get_metadata
428
543
 
429
- return mtch[0] unless File.exist?(file)
544
+ if data.key?('template')
545
+ templates = data['template'].strip.split(/\s*,\s*/)
430
546
 
431
- content = Util.read_file(file)
432
- home = ENV['HOME']
433
- short_path = File.dirname(file.sub(/^#{home}/, '~'))
434
- prefix = "#{short_path}/#{File.basename(file)}:"
435
- parts = content.split(/^##+/)
436
- parts.shift
437
- if parts.empty?
438
- content
439
- else
440
- "## #{parts.join('## ')}".gsub(/^(##+ *)(?=\S)/, "\\1#{prefix}")
547
+ template_topics.concat(gather_templates(templates))
441
548
  end
442
- end
443
-
444
- ##
445
- ## Get the title of the build note (top level header)
446
- ##
447
- ## @param truncate [Integer] Truncate to width
448
- ##
449
- def note_title(truncate = 0)
450
- help = Util.read_file(note_file)
451
- title = help.match(/(?:^(\S.*?)(?=\n==)|^# ?(.*?)$)/)
452
- title = if title
453
- title[1].nil? ? title[2] : title[1]
454
- else
455
- note_file.sub(/(\.\w+)?$/, '')
456
- end
457
549
 
458
- title && truncate.positive? ? title.trunc(truncate) : title
550
+ template_topics
459
551
  end
460
552
 
461
553
  # Read in the build notes file and output a hash of
@@ -477,7 +569,7 @@ module Howzit
477
569
  Process.exit 1
478
570
  end
479
571
 
480
- @title = note_title
572
+ @title = help.note_title(filename)
481
573
 
482
574
  help.gsub!(/@include\((.*?)\)/) do
483
575
  include_file(Regexp.last_match)
@@ -547,14 +639,33 @@ module Howzit
547
639
 
548
640
  raise "Invalid editor (#{editor})" unless Util.valid_command?(editor)
549
641
 
550
- if note_file.nil?
551
- res = Prompt.yn('No build notes file found, create one?', default: true)
642
+ create_note(prompt: true) if note_file.nil?
643
+ `#{editor} "#{note_file}"`
644
+ end
552
645
 
553
- create_note if res
554
- edit_note
555
- else
556
- `#{editor} "#{note_file}"`
557
- end
646
+ ##
647
+ ## Public method to create a new template
648
+ ##
649
+ ## @param template [String] The template name
650
+ ##
651
+ def create_template(template)
652
+ file = template.sub(/(\.md)?$/i, '.md')
653
+ file = File.join(Howzit.config.template_folder, file)
654
+ create_template_file(file, prompt: false)
655
+ end
656
+
657
+ ##
658
+ ## Open template in editor
659
+ ##
660
+ def edit_template_file(file)
661
+ editor = Howzit.options.fetch(:editor, ENV['EDITOR'])
662
+
663
+ raise 'No editor defined' if editor.nil?
664
+
665
+ raise "Invalid editor (#{editor})" unless Util.valid_command?(editor)
666
+
667
+ create_template_file(file, prompt: true) unless File.exist?(file)
668
+ `#{editor} "#{file}"`
558
669
  end
559
670
 
560
671
  ##
@@ -589,17 +700,11 @@ module Howzit
589
700
  unless note_file
590
701
  Process.exit 0 if Howzit.options[:list_runnable_titles] || Howzit.options[:list_topic_titles]
591
702
 
592
- # clear the buffer
593
- ARGV.length.times do
594
- ARGV.shift
595
- end
596
- res = yn("No build notes file found, create one?", false)
597
- create_note if res
598
- Process.exit 1
703
+ create_note(prompt: true)
599
704
  end
600
705
 
601
706
  if Howzit.options[:title_only]
602
- out = note_title(20)
707
+ out = Util.read_file(note_file).note_title(note_file, 20)
603
708
  $stdout.print(out.strip)
604
709
  Process.exit(0)
605
710
  elsif Howzit.options[:output_title] && !Howzit.options[:run]
data/lib/howzit/config.rb CHANGED
@@ -87,6 +87,7 @@ module Howzit
87
87
  def load_options
88
88
  Color.coloring = $stdout.isatty
89
89
  flags = {
90
+ ask: false,
90
91
  choose: false,
91
92
  default: false,
92
93
  for_topic: nil,
data/lib/howzit/prompt.rb CHANGED
@@ -75,16 +75,24 @@ module Howzit
75
75
  ## options and accepts a numeric response
76
76
  ##
77
77
  ## @param matches [Array] The options list
78
+ ## @param height [Symbol] height of fzf menu
79
+ ## (:auto adjusts height to
80
+ ## number of options, anything
81
+ ## else gets max height for
82
+ ## terminal)
78
83
  ##
79
84
  ## @return [Array] the selected results
80
85
  ##
81
86
  def choose(matches, height: :auto)
82
- height = if height == :auto
83
- matches.count + 3
84
- else
85
- TTY::Screen.rows
86
- end
87
+ return [] if !$stdout.isatty || matches.count.zero?
88
+
87
89
  if Util.command_exist?('fzf')
90
+ height = if height == :auto
91
+ matches.count + 3
92
+ else
93
+ TTY::Screen.rows
94
+ end
95
+
88
96
  settings = [
89
97
  '-0',
90
98
  '-1',
@@ -102,6 +110,8 @@ module Howzit
102
110
  return res.split(/\n/)
103
111
  end
104
112
 
113
+ return matches if matches.count == 1
114
+
105
115
  res = matches[0..9]
106
116
  stty_save = `stty -g`.chomp
107
117
 
@@ -126,7 +136,7 @@ module Howzit
126
136
  puts 'Out of range'
127
137
  options_list(matches)
128
138
  end
129
- rescue Interrupt
139
+ ensure
130
140
  system('stty', stty_save)
131
141
  exit
132
142
  end
@@ -16,6 +16,22 @@ module Howzit
16
16
  true
17
17
  end
18
18
 
19
+ ##
20
+ ## Get the title of the build note (top level header)
21
+ ##
22
+ ## @param truncate [Integer] Truncate to width
23
+ ##
24
+ def note_title(file, truncate = 0)
25
+ title = match(/(?:^(\S.*?)(?=\n==)|^# ?(.*?)$)/)
26
+ title = if title
27
+ title[1].nil? ? title[2] : title[1]
28
+ else
29
+ file.sub(/(\.\w+)?$/, '')
30
+ end
31
+
32
+ title && truncate.positive? ? title.trunc(truncate) : title
33
+ end
34
+
19
35
  ##
20
36
  ## Replace slash escaped characters in a string with a
21
37
  ## zero-width space that will prevent a shell from
@@ -275,9 +291,9 @@ module Howzit
275
291
  data = {}
276
292
  meta.each do |k, v|
277
293
  case k
278
- when /^templ\w+$/
294
+ when /^te?m?pl(ate)?s?$/
279
295
  data['template'] = v
280
- when /^req\w+$/
296
+ when /^req\w*$/
281
297
  data['required'] = v
282
298
  else
283
299
  data[k] = v
data/lib/howzit/task.rb CHANGED
@@ -8,11 +8,21 @@ module Howzit
8
8
  ##
9
9
  ## Initialize a Task object
10
10
  ##
11
- def initialize(params, optional: false, default: true)
12
- @type = params[:type]
13
- @title = params[:title]
14
- @action = params[:action].render_arguments
15
- @parent = params[:parent] || nil
11
+ ## @param attributes [Hash] the task attributes
12
+ ## @param optional [Boolean] Task requires
13
+ ## confirmation
14
+ ## @param default [Boolean] Default response
15
+ ## for confirmation dialog
16
+ ##
17
+ ## @option attributes :type [Symbol] task type (:block, :run, :include, :copy)
18
+ ## @option attributes :title [String] task title
19
+ ## @option attributes :action [String] task action
20
+ ## @option attributes :parent [String] title of nested (included) topic origin
21
+ def initialize(attributes, optional: false, default: true)
22
+ @type = attributes[:type] || :run
23
+ @title = attributes[:title] || nil
24
+ @action = attributes[:action].render_arguments || nil
25
+ @parent = attributes[:parent] || nil
16
26
  @optional = optional
17
27
  @default = default
18
28
  end
@@ -56,16 +66,16 @@ module Howzit
56
66
  ##
57
67
  ## Execute an include task
58
68
  ##
59
- ## @return [Integer] number of tasks executed
69
+ ## @return [Array] [[Array] output, [Integer] number of tasks executed]
60
70
  ##
61
71
  def run_include
62
72
  output = []
63
73
  matches = Howzit.buildnote.find_topic(@action)
64
74
  raise "Topic not found: #{@action}" if matches.empty?
65
75
 
66
- $stderr.puts "{by}Running tasks from {bw}#{matches[0].title}{x}".c if Howzit.options[:log_level] < 2
76
+ Howzit.console.info("{by}Running tasks from {bw}#{matches[0].title}{x}".c)
67
77
  output.concat(matches[0].run(nested: true))
68
- $stderr.puts "{by}End include: #{matches[0].tasks.count} tasks{x}".c if Howzit.options[:log_level] < 2
78
+ Howzit.console.info("{by}End include: #{matches[0].tasks.count} tasks{x}".c)
69
79
  [output, matches[0].tasks.count]
70
80
  end
71
81
 
@@ -74,7 +84,7 @@ module Howzit
74
84
  ##
75
85
  def run_run
76
86
  title = Howzit.options[:show_all_code] ? @action : @title
77
- $stderr.puts "{bg}Running {bw}#{title}{x}".c if Howzit.options[:log_level] < 2
87
+ Howzit.console.info("{bg}Running {bw}#{title}{x}".c)
78
88
  system(@action)
79
89
  end
80
90
 
@@ -83,7 +93,7 @@ module Howzit
83
93
  ##
84
94
  def run_copy
85
95
  title = Howzit.options[:show_all_code] ? @action : @title
86
- $stderr.puts "{bg}Copied {bw}#{title}{bg} to clipboard{x}".c if Howzit.options[:log_level] < 2
96
+ Howzit.console.info("{bg}Copied {bw}#{title}{bg} to clipboard{x}".c)
87
97
  os_copy(@action)
88
98
  end
89
99
 
@@ -97,21 +107,21 @@ module Howzit
97
107
  out = "{bg}Copying {bw}#{string}".c
98
108
  case os
99
109
  when /darwin.*/i
100
- $stderr.puts "#{out} (macOS){x}".c if Howzit.options[:log_level].zero?
110
+ Howzit.console.debug("#{out} (macOS){x}".c)
101
111
  `echo #{Shellwords.escape(string)}'\\c'|pbcopy`
102
112
  when /mingw|mswin/i
103
- $stderr.puts "#{out} (Windows){x}".c if Howzit.options[:log_level].zero?
113
+ Howzit.console.debug("#{out} (Windows){x}".c)
104
114
  `echo #{Shellwords.escape(string)} | clip`
105
115
  else
106
116
  if 'xsel'.available?
107
- $stderr.puts "#{out} (Linux, xsel){x}".c if Howzit.options[:log_level].zero?
117
+ Howzit.console.debug("#{out} (Linux, xsel){x}".c)
108
118
  `echo #{Shellwords.escape(string)}'\\c'|xsel -i`
109
119
  elsif 'xclip'.available?
110
- $stderr.puts "#{out} (Linux, xclip){x}".c if Howzit.options[:log_level].zero?
120
+ Howzit.console.debug("#{out} (Linux, xclip){x}".c)
111
121
  `echo #{Shellwords.escape(string)}'\\c'|xclip -i`
112
122
  else
113
- $stderr.puts out if Howzit.options[:log_level].zero?
114
- $stderr.puts 'Unable to determine executable for clipboard.'
123
+ Howzit.console.debug(out)
124
+ Howzit.console.warn('Unable to determine executable for clipboard.')
115
125
  end
116
126
  end
117
127
  end
data/lib/howzit/topic.rb CHANGED
@@ -51,7 +51,7 @@ module Howzit
51
51
  end
52
52
 
53
53
  @tasks.each do |task|
54
- if task.optional
54
+ if task.optional || Howzit.options[:ask]
55
55
  note = if task.type == :include
56
56
  task_count = Howzit.buildnote.find_topic(task.action)[0].tasks.count
57
57
  " (#{task_count} tasks)"
@@ -137,6 +137,7 @@ module Howzit
137
137
  cmd = m[:cmd]
138
138
  obj = m[:action]
139
139
  title = m[:title].empty? ? obj : m[:title].strip
140
+ title = Howzit.options[:show_all_code] ? obj : title
140
141
  optional = m[:optional] =~ /[?!]+/ ? true : false
141
142
  default = m[:optional] =~ /!/ ? false : true
142
143
  option = if optional
@@ -222,7 +223,7 @@ module Howzit
222
223
  default = c[:optional] =~ /!/ ? false : true
223
224
  obj = c[:action]
224
225
  title = c[:title].nil? ? obj : c[:title].strip
225
-
226
+ title = Howzit.options[:show_all_code] ? obj : title
226
227
  case cmd
227
228
  when /include/i
228
229
  # matches = Howzit.buildnote.find_topic(obj)
data/lib/howzit/util.rb CHANGED
@@ -116,6 +116,11 @@ module Howzit
116
116
 
117
117
  # Paginate the output
118
118
  def page(text)
119
+ unless $stdout.isatty
120
+ puts text
121
+ return
122
+ end
123
+
119
124
  read_io, write_io = IO.pipe
120
125
 
121
126
  input = $stdin
@@ -3,5 +3,5 @@
3
3
  # Primary module for this gem.
4
4
  module Howzit
5
5
  # Current Howzit version.
6
- VERSION = '2.0.15'
6
+ VERSION = '2.0.18'
7
7
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Howzit::Prompt do
6
+ subject(:prompt) { Howzit::Prompt }
7
+
8
+ describe '.yn' do
9
+ it 'returns default response' do
10
+ Howzit.options[:default] = true
11
+ expect(prompt.yn('Test prompt', default: true)).to be_truthy
12
+ expect(prompt.yn('Test prompt', default: false)).not_to be_truthy
13
+ end
14
+ end
15
+
16
+ describe '.color_single_options' do
17
+ it 'returns uncolored string' do
18
+ Howzit::Color.coloring = false
19
+ expect(prompt.color_single_options(%w[y n])).to eq "[y/n]"
20
+ end
21
+ end
22
+
23
+ describe '.options_list' do
24
+ it 'creates a formatted list of options' do
25
+ options = %w[one two three four five].each_with_object([]) do |x, arr|
26
+ arr << "Option item #{x}"
27
+ end
28
+ expect { prompt.options_list(options) }.to output(/ 2 \) Option item two/).to_stdout
29
+ end
30
+ end
31
+
32
+ describe '.choose' do
33
+ it 'returns a single match' do
34
+ expect(prompt.choose(['option 1']).count).to eq 1
35
+ end
36
+ end
37
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,13 +1,11 @@
1
- # require 'simplecov'
1
+ # frozen_string_literal: true
2
2
 
3
- # SimpleCov.start
4
-
5
- # if ENV['CI'] == 'true'
6
- # require 'codecov'
7
- # SimpleCov.formatter = SimpleCov::Formatter::Codecov
8
- # else
9
- # SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter
10
- # end
3
+ unless ENV['CI'] == 'true'
4
+ # SimpleCov::Formatter::Codecov # For CI
5
+ require 'simplecov'
6
+ SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter
7
+ SimpleCov.start
8
+ end
11
9
 
12
10
  require 'howzit'
13
11
  require 'cli-test'
data/spec/task_spec.rb CHANGED
@@ -6,8 +6,7 @@ describe Howzit::Task do
6
6
  subject(:task) do
7
7
  Howzit::Task.new({ type: :run,
8
8
  title: 'List Directory',
9
- action: 'ls',
10
- parent: nil })
9
+ action: 'ls &> /dev/null' })
11
10
  end
12
11
 
13
12
  describe ".new" do
data/spec/topic_spec.rb CHANGED
@@ -7,14 +7,14 @@ describe Howzit::Topic do
7
7
  content = 'Test Content'
8
8
  subject(:topic) { Howzit::Topic.new(title, content) }
9
9
 
10
- describe ".new" do
11
- it "makes a new topic instance" do
10
+ describe '.new' do
11
+ it 'makes a new topic instance' do
12
12
  expect(topic).to be_a Howzit::Topic
13
13
  end
14
- it "has the correct title" do
14
+ it 'has the correct title' do
15
15
  expect(topic.title).to eq title
16
16
  end
17
- it "has the correct content" do
17
+ it 'has the correct content' do
18
18
  expect(topic.content).to eq content
19
19
  end
20
20
  end
@@ -23,34 +23,49 @@ end
23
23
  describe Howzit::Topic do
24
24
  subject(:topic) { @hz.find_topic('Topic Balogna')[0] }
25
25
 
26
- describe ".title" do
27
- it "has the correct title" do
28
- expect(topic.title).to match /Topic Balogna/
26
+ describe '.title' do
27
+ it 'has the correct title' do
28
+ expect(topic.title).to match(/Topic Balogna/)
29
29
  end
30
30
  end
31
31
 
32
- describe ".tasks" do
33
- it "has 2 tasks" do
32
+ describe '.tasks' do
33
+ it 'has 2 tasks' do
34
34
  expect(topic.tasks.count).to eq 2
35
35
  end
36
36
  end
37
37
 
38
- describe ".prereqs" do
39
- it "has prereq" do
38
+ describe '.prereqs' do
39
+ it 'has prereq' do
40
40
  expect(topic.prereqs.count).to eq 1
41
41
  end
42
- it "has postreq" do
42
+ end
43
+
44
+ describe '.postreqs' do
45
+ it 'has postreq' do
43
46
  expect(topic.postreqs.count).to eq 1
44
47
  end
45
48
  end
46
49
 
47
- describe ".run" do
50
+ describe '.grep' do
51
+ it 'returns true for matching pattern in content' do
52
+ expect(topic.grep('prereq.*?ite')).to be_truthy
53
+ end
54
+ it 'returns true for matching pattern in title' do
55
+ expect(topic.grep('bal.*?na')).to be_truthy
56
+ end
57
+ it 'fails on bad pattern' do
58
+ expect(topic.grep('xxx+')).to_not be_truthy
59
+ end
60
+ end
61
+
62
+ describe '.run' do
48
63
  Howzit.options[:default] = true
49
- it "shows prereq and postreq" do
64
+ it 'shows prereq and postreq' do
50
65
  expect { topic.run }.to output(/prerequisite/).to_stdout
51
66
  expect { topic.run }.to output(/postrequisite/).to_stdout
52
67
  end
53
- it "Copies to clipboard" do
68
+ it 'Copies to clipboard' do
54
69
  expect {
55
70
  ENV['RUBYOPT'] = '-W1'
56
71
  Howzit.options[:log_level] = 0
@@ -58,4 +73,19 @@ describe Howzit::Topic do
58
73
  }.to output(/Copied/).to_stderr
59
74
  end
60
75
  end
76
+
77
+ describe '.print_out' do
78
+ Howzit.options[:header_format] = :block
79
+ Howzit.options[:color] = false
80
+ it 'prints the topic title' do
81
+ expect(topic.print_out({single: true, header: true}).join("\n").uncolor).to match(/▌Topic Balogna/)
82
+ end
83
+ it 'prints a task title' do
84
+ expect(topic.print_out({single: true, header: true}).join("\n").uncolor).to match(/▶ Null Output/)
85
+ end
86
+ it 'prints task action with --show-code' do
87
+ Howzit.options[:show_all_code] = true
88
+ expect(topic.print_out({single: true, header: true}).join("\n").uncolor).to match(/▶ ls -1/)
89
+ end
90
+ end
61
91
  end
data/spec/util_spec.rb ADDED
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Howzit::Util do
6
+ subject(:util) { Howzit::Util }
7
+
8
+ describe '.read_file' do
9
+ it 'reads file to a string' do
10
+ buildnote = util.read_file('builda.md')
11
+ expect(buildnote).not_to be_empty
12
+ expect(buildnote).to be_a String
13
+ end
14
+ end
15
+
16
+ describe '.valid_command?' do
17
+ it 'finds a command' do
18
+ expect(util.command_exist?('ls')).to be_truthy
19
+ end
20
+ it 'validates a command' do
21
+ expect(util.valid_command?('ls -1')).to be_truthy
22
+ end
23
+ end
24
+
25
+ describe '.which_highlighter' do
26
+ it 'finds mdless' do
27
+ Howzit.options[:highlighter] = 'mdless'
28
+ expect(util.which_highlighter).to eq 'mdless'
29
+ end
30
+ end
31
+
32
+ describe '.which_pager' do
33
+ it 'finds the more utility' do
34
+ Howzit.options[:pager] = 'more'
35
+ expect(util.which_pager).to eq 'more'
36
+ Howzit.options[:pager] = 'auto'
37
+ expect(util.which_pager).to_not eq 'more'
38
+ end
39
+ end
40
+
41
+ describe '.show' do
42
+ it 'prints output' do
43
+ buildnote = util.read_file('builda.md')
44
+ expect { util.show(buildnote) }.to output(/Balogna/).to_stdout
45
+ end
46
+
47
+ it 'pages output' do
48
+ buildnote = util.read_file('builda.md')
49
+ expect { util.page(buildnote) }.to output(/Balogna/).to_stdout
50
+ end
51
+ end
52
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: howzit
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.15
4
+ version: 2.0.18
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-08-05 00:00:00.000000000 Z
11
+ date: 2022-08-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -331,10 +331,12 @@ files:
331
331
  - spec/.rubocop.yml
332
332
  - spec/buildnote_spec.rb
333
333
  - spec/cli_spec.rb
334
+ - spec/prompt_spec.rb
334
335
  - spec/ruby_gem_spec.rb
335
336
  - spec/spec_helper.rb
336
337
  - spec/task_spec.rb
337
338
  - spec/topic_spec.rb
339
+ - spec/util_spec.rb
338
340
  - update_readmes.rb
339
341
  homepage: https://github.com/ttscoff/howzit
340
342
  licenses:
@@ -364,7 +366,9 @@ test_files:
364
366
  - spec/.rubocop.yml
365
367
  - spec/buildnote_spec.rb
366
368
  - spec/cli_spec.rb
369
+ - spec/prompt_spec.rb
367
370
  - spec/ruby_gem_spec.rb
368
371
  - spec/spec_helper.rb
369
372
  - spec/task_spec.rb
370
373
  - spec/topic_spec.rb
374
+ - spec/util_spec.rb