howzit 2.0.8 → 2.0.11
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 +4 -4
- data/.travis.yml +1 -1
- data/CHANGELOG.md +32 -0
- data/README.md +4 -343
- data/bin/howzit +101 -83
- data/fish/completions/howzit.fish +34 -2
- data/howzit.gemspec +2 -0
- data/lib/howzit/buildnote.rb +161 -33
- data/lib/howzit/colors.rb +3 -0
- data/lib/howzit/config.rb +39 -5
- data/lib/howzit/console_logger.rb +38 -0
- data/lib/howzit/hash.rb +25 -4
- data/lib/howzit/prompt.rb +42 -6
- data/lib/howzit/stringutils.rb +131 -4
- data/lib/howzit/task.rb +10 -6
- data/lib/howzit/topic.rb +72 -10
- data/lib/howzit/util.rb +35 -5
- data/lib/howzit/version.rb +1 -1
- data/lib/howzit.rb +13 -0
- data/spec/cli_spec.rb +27 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/task_spec.rb +6 -1
- metadata +32 -5
- data/lib/howzit/buildnotes.rb +0 -1252
- data/spec/buildnotes.md.bak +0 -22
data/lib/howzit/prompt.rb
CHANGED
@@ -4,34 +4,61 @@ module Howzit
|
|
4
4
|
# Command line prompt utils
|
5
5
|
module Prompt
|
6
6
|
class << self
|
7
|
-
|
7
|
+
|
8
|
+
##
|
9
|
+
## Display and read a Yes/No prompt
|
10
|
+
##
|
11
|
+
## @param prompt [String] The prompt string
|
12
|
+
## @param default [Boolean] default value if
|
13
|
+
## return is pressed or prompt is
|
14
|
+
## skipped
|
15
|
+
##
|
16
|
+
## @return [Boolean] result
|
17
|
+
##
|
18
|
+
def yn(prompt, default: true)
|
8
19
|
return default unless $stdout.isatty
|
9
20
|
|
10
21
|
return default if Howzit.options[:default]
|
11
22
|
|
12
|
-
|
23
|
+
tty_state = `stty -g`
|
24
|
+
system 'stty raw -echo cbreak isig'
|
13
25
|
yn = color_single_options(default ? %w[Y n] : %w[y N])
|
14
26
|
$stdout.syswrite "\e[1;37m#{prompt} #{yn}\e[1;37m? \e[0m"
|
15
27
|
res = $stdin.sysread 1
|
16
28
|
res.chomp!
|
17
29
|
puts
|
18
30
|
system 'stty cooked'
|
31
|
+
system "stty #{tty_state}"
|
19
32
|
res.empty? ? default : res =~ /y/i
|
20
33
|
end
|
21
34
|
|
35
|
+
##
|
36
|
+
## Helper function to colorize the Y/N prompt
|
37
|
+
##
|
38
|
+
## @param choices [Array] The choices with
|
39
|
+
## default capitalized
|
40
|
+
##
|
41
|
+
## @return [String] colorized string
|
42
|
+
##
|
22
43
|
def color_single_options(choices = %w[y n])
|
23
44
|
out = []
|
24
45
|
choices.each do |choice|
|
25
46
|
case choice
|
26
47
|
when /[A-Z]/
|
27
|
-
out.push(Color.template("{
|
48
|
+
out.push(Color.template("{bw}#{choice}{x}"))
|
28
49
|
else
|
29
|
-
out.push(Color.template("{
|
50
|
+
out.push(Color.template("{dw}#{choice}{xg}"))
|
30
51
|
end
|
31
52
|
end
|
32
|
-
Color.template("{
|
53
|
+
Color.template("{xg}[#{out.join('/')}{xg}]{x}")
|
33
54
|
end
|
34
55
|
|
56
|
+
##
|
57
|
+
## Create a numbered list of options. Outputs directly
|
58
|
+
## to console, returns nothing
|
59
|
+
##
|
60
|
+
## @param matches [Array] The list items
|
61
|
+
##
|
35
62
|
def options_list(matches)
|
36
63
|
counter = 1
|
37
64
|
puts
|
@@ -42,6 +69,15 @@ module Howzit
|
|
42
69
|
puts
|
43
70
|
end
|
44
71
|
|
72
|
+
##
|
73
|
+
## Choose from a list of items. If fzf is available,
|
74
|
+
## uses that, otherwise generates its own list of
|
75
|
+
## options and accepts a numeric response
|
76
|
+
##
|
77
|
+
## @param matches [Array] The options list
|
78
|
+
##
|
79
|
+
## @return [Array] the selected results
|
80
|
+
##
|
45
81
|
def choose(matches)
|
46
82
|
if Util.command_exist?('fzf')
|
47
83
|
settings = [
|
@@ -79,7 +115,7 @@ module Howzit
|
|
79
115
|
end
|
80
116
|
line = line == '' ? 1 : line.to_i
|
81
117
|
|
82
|
-
return matches[line - 1] if line.positive? && line <= matches.length
|
118
|
+
return [matches[line - 1]] if line.positive? && line <= matches.length
|
83
119
|
|
84
120
|
puts 'Out of range'
|
85
121
|
options_list(matches)
|
data/lib/howzit/stringutils.rb
CHANGED
@@ -3,7 +3,37 @@
|
|
3
3
|
module Howzit
|
4
4
|
# String Extensions
|
5
5
|
module StringUtils
|
6
|
+
##
|
7
|
+
## Test if the filename matches the conditions to be a build note
|
8
|
+
##
|
9
|
+
## @return [Boolean] true if filename passes test
|
10
|
+
##
|
11
|
+
def build_note?
|
12
|
+
return false if downcase !~ /^(howzit[^.]*|build[^.]+)/
|
13
|
+
|
14
|
+
return false if Howzit.config.should_ignore(self)
|
15
|
+
|
16
|
+
true
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
## Replace slash escaped characters in a string with a
|
21
|
+
## zero-width space that will prevent a shell from
|
22
|
+
## interpreting them when output to console
|
23
|
+
##
|
24
|
+
## @return [String] new string
|
25
|
+
##
|
26
|
+
def preserve_escapes
|
27
|
+
gsub(/\\([a-z])/, '\\1')
|
28
|
+
end
|
29
|
+
|
6
30
|
# Convert a string to a valid YAML value
|
31
|
+
#
|
32
|
+
# @param orig_value The original value from which
|
33
|
+
# type will be determined
|
34
|
+
#
|
35
|
+
# @return coerced value
|
36
|
+
#
|
7
37
|
def to_config_value(orig_value = nil)
|
8
38
|
if orig_value
|
9
39
|
case orig_value.class.to_s
|
@@ -28,10 +58,21 @@ module Howzit
|
|
28
58
|
end
|
29
59
|
end
|
30
60
|
|
61
|
+
##
|
62
|
+
## Shortcut for calling Color.template
|
63
|
+
##
|
64
|
+
## @return [String] colorized string
|
65
|
+
##
|
31
66
|
def c
|
32
67
|
Color.template(self)
|
33
68
|
end
|
34
69
|
|
70
|
+
|
71
|
+
##
|
72
|
+
## Convert a string to a regex object based on matching settings
|
73
|
+
##
|
74
|
+
## @return [Regexp] Receive regex representation of the object.
|
75
|
+
##
|
35
76
|
def to_rx
|
36
77
|
case Howzit.options[:matching]
|
37
78
|
when 'exact'
|
@@ -50,9 +91,17 @@ module Howzit
|
|
50
91
|
gsub(/\e\[[\d;]+m/, '').gsub(/\e\]1337;SetMark/,'')
|
51
92
|
end
|
52
93
|
|
94
|
+
# Wrap text at a specified width.
|
95
|
+
#
|
53
96
|
# Adapted from https://github.com/pazdera/word_wrap/,
|
54
|
-
# copyright (c) 2014, 2015 Radek Pazdera
|
55
|
-
#
|
97
|
+
# copyright (c) 2014, 2015 Radek Pazdera Distributed
|
98
|
+
# under the MIT License
|
99
|
+
#
|
100
|
+
# @param width [Integer] The width at which to
|
101
|
+
# wrap lines
|
102
|
+
#
|
103
|
+
# @return [String] wrapped string
|
104
|
+
#
|
56
105
|
def wrap(width)
|
57
106
|
width ||= 80
|
58
107
|
output = []
|
@@ -84,12 +133,19 @@ module Howzit
|
|
84
133
|
output.join("\n")
|
85
134
|
end
|
86
135
|
|
136
|
+
##
|
137
|
+
## Wrap string in place (destructive)
|
138
|
+
##
|
139
|
+
## @param width [Integer] The width at which to wrap
|
140
|
+
##
|
87
141
|
def wrap!(width)
|
88
142
|
replace(wrap(width))
|
89
143
|
end
|
90
144
|
|
91
145
|
# Truncate string to nearest word
|
92
|
-
#
|
146
|
+
#
|
147
|
+
# @param len [Integer] max length of string
|
148
|
+
#
|
93
149
|
def trunc(len)
|
94
150
|
split(/ /).each_with_object([]) do |x, ob|
|
95
151
|
break ob unless ob.join(' ').length + ' '.length + x.length <= len
|
@@ -98,10 +154,21 @@ module Howzit
|
|
98
154
|
end.join(' ').strip
|
99
155
|
end
|
100
156
|
|
157
|
+
##
|
158
|
+
## Truncate string in place (destructive)
|
159
|
+
##
|
160
|
+
## @param len [Integer] The length to truncate at
|
161
|
+
##
|
101
162
|
def trunc!(len)
|
102
163
|
replace trunc(len)
|
103
164
|
end
|
104
165
|
|
166
|
+
##
|
167
|
+
## Splits a line at nearest word break
|
168
|
+
##
|
169
|
+
## @param width [Integer] The width of the first segment
|
170
|
+
## @param indent [String] The indent string
|
171
|
+
##
|
105
172
|
def split_line(width, indent = '')
|
106
173
|
line = dup
|
107
174
|
at = line.index(/\s/)
|
@@ -119,10 +186,23 @@ module Howzit
|
|
119
186
|
end
|
120
187
|
end
|
121
188
|
|
189
|
+
##
|
190
|
+
## Test if an executable is available on the system
|
191
|
+
##
|
192
|
+
## @return [Boolean] executable is available
|
193
|
+
##
|
122
194
|
def available?
|
123
195
|
Util.valid_command?(self)
|
124
196
|
end
|
125
197
|
|
198
|
+
##
|
199
|
+
## Render [%variable] placeholders in a templated string
|
200
|
+
##
|
201
|
+
## @param vars [Hash] Key/value pairs of variable
|
202
|
+
## values
|
203
|
+
##
|
204
|
+
## @return [String] Rendered string
|
205
|
+
##
|
126
206
|
def render_template(vars)
|
127
207
|
vars.each do |k, v|
|
128
208
|
gsub!(/\[%#{k}(:.*?)?\]/, v)
|
@@ -131,10 +211,20 @@ module Howzit
|
|
131
211
|
gsub(/\[%(.*?):(.*?)\]/, '\2')
|
132
212
|
end
|
133
213
|
|
214
|
+
##
|
215
|
+
## Render [%variable] placeholders in place
|
216
|
+
##
|
217
|
+
## @param vars [Hash] Key/value pairs of variable values
|
218
|
+
##
|
134
219
|
def render_template!(vars)
|
135
220
|
replace render_template(vars)
|
136
221
|
end
|
137
222
|
|
223
|
+
##
|
224
|
+
## Render $X placeholders based on positional arguments
|
225
|
+
##
|
226
|
+
## @return [String] rendered string
|
227
|
+
##
|
138
228
|
def render_arguments
|
139
229
|
return self if Howzit.arguments.nil? || Howzit.arguments.empty?
|
140
230
|
|
@@ -145,15 +235,27 @@ module Howzit
|
|
145
235
|
gsub(/\$[@*]/, Shellwords.join(Howzit.arguments))
|
146
236
|
end
|
147
237
|
|
238
|
+
##
|
239
|
+
## Split the content at the first top-level header and
|
240
|
+
## assume everything before it is metadata. Passes to
|
241
|
+
## #get_metadata for processing
|
242
|
+
##
|
243
|
+
## @return [Hash] key/value pairs
|
244
|
+
##
|
148
245
|
def extract_metadata
|
149
246
|
if File.exist?(self)
|
150
|
-
leader =
|
247
|
+
leader = Util.read_file(self).split(/^#/)[0].strip
|
151
248
|
leader.length > 0 ? leader.get_metadata : {}
|
152
249
|
else
|
153
250
|
{}
|
154
251
|
end
|
155
252
|
end
|
156
253
|
|
254
|
+
##
|
255
|
+
## Examine text for multimarkdown-style metadata and return key/value pairs
|
256
|
+
##
|
257
|
+
## @return [Hash] The metadata as key/value pairs
|
258
|
+
##
|
157
259
|
def get_metadata
|
158
260
|
data = {}
|
159
261
|
scan(/(?mi)^(\S[\s\S]+?): ([\s\S]*?)(?=\n\S[\s\S]*?:|\Z)/).each do |m|
|
@@ -162,6 +264,13 @@ module Howzit
|
|
162
264
|
normalize_metadata(data)
|
163
265
|
end
|
164
266
|
|
267
|
+
##
|
268
|
+
## Autocorrect some keys
|
269
|
+
##
|
270
|
+
## @param meta [Hash] The metadata
|
271
|
+
##
|
272
|
+
## @return [Hash] corrected metadata
|
273
|
+
##
|
165
274
|
def normalize_metadata(meta)
|
166
275
|
data = {}
|
167
276
|
meta.each do |k, v|
|
@@ -177,15 +286,33 @@ module Howzit
|
|
177
286
|
data
|
178
287
|
end
|
179
288
|
|
289
|
+
##
|
290
|
+
## Test if iTerm markers should be output. Requires that
|
291
|
+
## the $TERM_PROGRAM be iTerm and howzit is not running
|
292
|
+
## directives or paginating output
|
293
|
+
##
|
294
|
+
## @return [Boolean] should mark?
|
295
|
+
##
|
180
296
|
def should_mark_iterm?
|
181
297
|
ENV['TERM_PROGRAM'] =~ /^iTerm/ && !Howzit.options[:run] && !Howzit.options[:paginate]
|
182
298
|
end
|
183
299
|
|
300
|
+
##
|
301
|
+
## Output an iTerm marker
|
302
|
+
##
|
303
|
+
## @return [String] ANSI escape sequence for iTerm
|
304
|
+
## marker
|
305
|
+
##
|
184
306
|
def iterm_marker
|
185
307
|
"\e]1337;SetMark\a" if should_mark_iterm?
|
186
308
|
end
|
187
309
|
|
188
310
|
# Make a fancy title line for the topic
|
311
|
+
#
|
312
|
+
# @param opts [Hash] options
|
313
|
+
#
|
314
|
+
# @return [String] formatted string
|
315
|
+
#
|
189
316
|
def format_header(opts = {})
|
190
317
|
title = dup
|
191
318
|
options = {
|
data/lib/howzit/task.rb
CHANGED
@@ -1,14 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Howzit
|
4
|
+
# Task object
|
4
5
|
class Task
|
5
6
|
attr_reader :type, :title, :action, :parent, :optional, :default
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
@
|
8
|
+
##
|
9
|
+
## Initialize a Task object
|
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
|
12
16
|
@optional = optional
|
13
17
|
@default = default
|
14
18
|
end
|
@@ -22,7 +26,7 @@ module Howzit
|
|
22
26
|
end
|
23
27
|
|
24
28
|
def to_list
|
25
|
-
" * #{@type}: #{@title.
|
29
|
+
" * #{@type}: #{@title.preserve_escapes}"
|
26
30
|
end
|
27
31
|
end
|
28
32
|
end
|
data/lib/howzit/topic.rb
CHANGED
@@ -9,6 +9,12 @@ module Howzit
|
|
9
9
|
|
10
10
|
attr_reader :title, :tasks, :prereqs, :postreqs
|
11
11
|
|
12
|
+
##
|
13
|
+
## Initialize a topic object
|
14
|
+
##
|
15
|
+
## @param title [String] The topic title
|
16
|
+
## @param content [String] The raw topic content
|
17
|
+
##
|
12
18
|
def initialize(title, content)
|
13
19
|
@title = title
|
14
20
|
@content = content
|
@@ -17,18 +23,29 @@ module Howzit
|
|
17
23
|
@tasks = gather_tasks
|
18
24
|
end
|
19
25
|
|
26
|
+
##
|
27
|
+
## Search title and contents for a pattern
|
28
|
+
##
|
29
|
+
## @param term [String] the search pattern
|
30
|
+
##
|
20
31
|
def grep(term)
|
21
32
|
@title =~ /#{term}/i || @content =~ /#{term}/i
|
22
33
|
end
|
23
34
|
|
24
|
-
# Handle run command, execute directives
|
35
|
+
# Handle run command, execute directives in topic
|
25
36
|
def run(nested: false)
|
26
37
|
output = []
|
27
38
|
tasks = 0
|
39
|
+
cols = begin
|
40
|
+
TTY::Screen.columns > 60 ? 60 : TTY::Screen.columns
|
41
|
+
rescue StandardError
|
42
|
+
60
|
43
|
+
end
|
44
|
+
|
28
45
|
if @tasks.count.positive?
|
29
46
|
unless @prereqs.empty?
|
30
|
-
puts @prereqs.join("\n\n")
|
31
|
-
res = Prompt.yn('
|
47
|
+
puts TTY::Box.frame("{by}#{@prereqs.join("\n\n").wrap(cols - 4)}{x}".c, width: cols)
|
48
|
+
res = Prompt.yn('Have the above prerequisites been met?', default: true)
|
32
49
|
Process.exit 1 unless res
|
33
50
|
|
34
51
|
end
|
@@ -85,11 +102,16 @@ module Howzit
|
|
85
102
|
end
|
86
103
|
output.push("{bm}Ran #{tasks} #{tasks == 1 ? 'task' : 'tasks'}{x}".c) if Howzit.options[:log_level] < 2 && !nested
|
87
104
|
|
88
|
-
puts postreqs.join("\n\n") unless postreqs.empty?
|
105
|
+
puts TTY::Box.frame("{bw}#{@postreqs.join("\n\n").wrap(cols - 4)}{x}".c, width: cols) unless @postreqs.empty?
|
89
106
|
|
90
107
|
output
|
91
108
|
end
|
92
109
|
|
110
|
+
##
|
111
|
+
## Platform-agnostic copy-to-clipboard
|
112
|
+
##
|
113
|
+
## @param string [String] The string to copy
|
114
|
+
##
|
93
115
|
def os_copy(string)
|
94
116
|
os = RbConfig::CONFIG['target_os']
|
95
117
|
out = "{bg}Copying {bw}#{string}".c
|
@@ -114,6 +136,11 @@ module Howzit
|
|
114
136
|
end
|
115
137
|
end
|
116
138
|
|
139
|
+
##
|
140
|
+
## Platform-agnostic open command
|
141
|
+
##
|
142
|
+
## @param command [String] The command
|
143
|
+
##
|
117
144
|
def os_open(command)
|
118
145
|
os = RbConfig::CONFIG['target_os']
|
119
146
|
out = "{bg}Opening {bw}#{command}".c
|
@@ -136,6 +163,11 @@ module Howzit
|
|
136
163
|
end
|
137
164
|
|
138
165
|
# Output a topic with fancy title and bright white text.
|
166
|
+
#
|
167
|
+
# @param options [Hash] The options
|
168
|
+
#
|
169
|
+
# @return [Array] array of formatted lines
|
170
|
+
#
|
139
171
|
def print_out(options = {})
|
140
172
|
defaults = { single: false, header: true }
|
141
173
|
opt = defaults.merge(options)
|
@@ -206,7 +238,7 @@ module Howzit
|
|
206
238
|
"\u{279A}"
|
207
239
|
end
|
208
240
|
|
209
|
-
output.push("{bmK}#{icon} {bwK}#{title.
|
241
|
+
output.push("{bmK}#{icon} {bwK}#{title.preserve_escapes}{x}#{option}".c)
|
210
242
|
when /(?<fence>`{3,})run(?<optional>[!?]{1,2})? *(?<title>.*?)$/i
|
211
243
|
m = Regexp.last_match.named_captures.symbolize_keys
|
212
244
|
optional = m[:optional] =~ /[?!]+/ ? true : false
|
@@ -239,6 +271,11 @@ module Howzit
|
|
239
271
|
|
240
272
|
private
|
241
273
|
|
274
|
+
##
|
275
|
+
## Collect all directives in the topic content
|
276
|
+
##
|
277
|
+
## @return [Array] array of Task objects
|
278
|
+
##
|
242
279
|
def gather_tasks
|
243
280
|
runnable = []
|
244
281
|
@prereqs = @content.scan(/(?<=@before\n).*?(?=\n@end)/im).map(&:strip)
|
@@ -258,7 +295,12 @@ module Howzit
|
|
258
295
|
default = c[:optional2] =~ /!/ ? false : true
|
259
296
|
title = c[:title2].nil? ? '' : c[:title2].strip
|
260
297
|
block = c[:block]&.strip
|
261
|
-
runnable << Howzit::Task.new(:
|
298
|
+
runnable << Howzit::Task.new({ type: :block,
|
299
|
+
title: title,
|
300
|
+
action: block,
|
301
|
+
parent: nil },
|
302
|
+
optional: optional,
|
303
|
+
default: default)
|
262
304
|
else
|
263
305
|
cmd = c[:cmd]
|
264
306
|
optional = c[:optional] =~ /[?!]{1,2}/ ? true : false
|
@@ -277,15 +319,35 @@ module Howzit
|
|
277
319
|
# end
|
278
320
|
# runnable.concat(tasks)
|
279
321
|
# end
|
280
|
-
runnable << Howzit::Task.new(:
|
322
|
+
runnable << Howzit::Task.new({ type: :include,
|
323
|
+
title: title,
|
324
|
+
action: obj,
|
325
|
+
parent: nil },
|
326
|
+
optional: optional,
|
327
|
+
default: default)
|
281
328
|
when /run/i
|
282
329
|
# warn "{bg}Running {bw}#{obj}{x}".c if Howzit.options[:log_level] < 2
|
283
|
-
runnable << Howzit::Task.new(:
|
330
|
+
runnable << Howzit::Task.new({ type: :run,
|
331
|
+
title: title,
|
332
|
+
action: obj,
|
333
|
+
parent: nil },
|
334
|
+
optional: optional,
|
335
|
+
default: default)
|
284
336
|
when /copy/i
|
285
337
|
# warn "{bg}Copied {bw}#{obj}{bg} to clipboard{x}".c if Howzit.options[:log_level] < 2
|
286
|
-
runnable << Howzit::Task.new(:
|
338
|
+
runnable << Howzit::Task.new({ type: :copy,
|
339
|
+
title: title,
|
340
|
+
action: Shellwords.escape(obj),
|
341
|
+
parent: nil },
|
342
|
+
optional: optional,
|
343
|
+
default: default)
|
287
344
|
when /open|url/i
|
288
|
-
runnable << Howzit::Task.new(:
|
345
|
+
runnable << Howzit::Task.new({ type: :open,
|
346
|
+
title: title,
|
347
|
+
action: obj,
|
348
|
+
parent: nil },
|
349
|
+
optional: optional,
|
350
|
+
default: default)
|
289
351
|
end
|
290
352
|
end
|
291
353
|
end
|
data/lib/howzit/util.rb
CHANGED
@@ -1,21 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Howzit
|
4
|
+
# Util class
|
2
5
|
module Util
|
3
6
|
class << self
|
7
|
+
|
8
|
+
##
|
9
|
+
## Read a file with UTF-8 encoding and
|
10
|
+
## leading/trailing whitespace removed
|
11
|
+
##
|
12
|
+
## @param path [String] The path to read
|
13
|
+
##
|
14
|
+
## @return [String] UTF-8 encoded string
|
15
|
+
##
|
16
|
+
def read_file(path)
|
17
|
+
IO.read(path).force_encoding('utf-8').strip
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
## Test if an external command exists and is
|
22
|
+
## executable. Removes additional arguments and passes
|
23
|
+
## just the executable to #command_exist?
|
24
|
+
##
|
25
|
+
## @param command [String] The command
|
26
|
+
##
|
27
|
+
## @return [Boolean] command is valid
|
28
|
+
##
|
4
29
|
def valid_command?(command)
|
5
30
|
cmd = command.split(' ')[0]
|
6
31
|
command_exist?(cmd)
|
7
32
|
end
|
8
|
-
|
33
|
+
|
34
|
+
##
|
35
|
+
## Test if external command exists
|
36
|
+
##
|
37
|
+
## @param command [String] The command
|
38
|
+
##
|
39
|
+
## @return [Boolean] command exists
|
40
|
+
##
|
9
41
|
def command_exist?(command)
|
10
42
|
exts = ENV.fetch('PATHEXT', '').split(::File::PATH_SEPARATOR)
|
11
43
|
if Pathname.new(command).absolute?
|
12
|
-
::File.exist?(command) ||
|
13
|
-
exts.any? { |ext| ::File.exist?("#{command}#{ext}") }
|
44
|
+
::File.exist?(command) || exts.any? { |ext| ::File.exist?("#{command}#{ext}") }
|
14
45
|
else
|
15
46
|
ENV.fetch('PATH', '').split(::File::PATH_SEPARATOR).any? do |dir|
|
16
47
|
file = ::File.join(dir, command)
|
17
|
-
::File.exist?(file) ||
|
18
|
-
exts.any? { |ext| ::File.exist?("#{file}#{ext}") }
|
48
|
+
::File.exist?(file) || exts.any? { |ext| ::File.exist?("#{file}#{ext}") }
|
19
49
|
end
|
20
50
|
end
|
21
51
|
end
|
data/lib/howzit/version.rb
CHANGED
data/lib/howzit.rb
CHANGED
@@ -21,15 +21,28 @@ require 'tempfile'
|
|
21
21
|
require 'yaml'
|
22
22
|
|
23
23
|
require 'tty/screen'
|
24
|
+
require 'tty/box'
|
24
25
|
# require 'tty/prompt'
|
25
26
|
|
27
|
+
# Main config dir
|
26
28
|
CONFIG_DIR = '~/.config/howzit'
|
29
|
+
|
30
|
+
# Config file name
|
27
31
|
CONFIG_FILE = 'howzit.yaml'
|
32
|
+
|
33
|
+
# Ignore file name
|
28
34
|
IGNORE_FILE = 'ignore.yaml'
|
35
|
+
|
36
|
+
# Available options for matching method
|
29
37
|
MATCHING_OPTIONS = %w[partial exact fuzzy beginswith].freeze
|
38
|
+
|
39
|
+
# Available options for multiple_matches method
|
30
40
|
MULTIPLE_OPTIONS = %w[first best all choose].freeze
|
41
|
+
|
42
|
+
# Available options for header formatting
|
31
43
|
HEADER_FORMAT_OPTIONS = %w[border block].freeze
|
32
44
|
|
45
|
+
# Main module for howzit
|
33
46
|
module Howzit
|
34
47
|
class << self
|
35
48
|
attr_accessor :arguments, :cli_args
|
data/spec/cli_spec.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
# https://github.com/thoiberg/cli-test
|
6
|
+
describe 'CLI' do
|
7
|
+
include CliTest
|
8
|
+
|
9
|
+
it 'executes successfully' do
|
10
|
+
execute_script('bin/howzit', use_bundler: true)
|
11
|
+
expect(last_execution).to be_successful
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'lists available topics' do
|
15
|
+
execute_script('bin/howzit', use_bundler: true, args: %w[-L])
|
16
|
+
expect(last_execution).to be_successful
|
17
|
+
expect(last_execution.stdout).to match(/Topic Balogna/)
|
18
|
+
expect(last_execution.stdout.split(/\n/).count).to eq 3
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'lists available tasks' do
|
22
|
+
execute_script('bin/howzit', use_bundler: true, args: %w[-T])
|
23
|
+
expect(last_execution).to be_successful
|
24
|
+
expect(last_execution.stdout).to match(/Topic Balogna/)
|
25
|
+
expect(last_execution.stdout.split(/\n/).count).to eq 2
|
26
|
+
end
|
27
|
+
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/task_spec.rb
CHANGED
@@ -3,7 +3,12 @@
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
describe Howzit::Task do
|
6
|
-
subject(:task)
|
6
|
+
subject(:task) do
|
7
|
+
Howzit::Task.new({ type: :run,
|
8
|
+
title: 'List Directory',
|
9
|
+
action: 'ls',
|
10
|
+
parent: nil })
|
11
|
+
end
|
7
12
|
|
8
13
|
describe ".new" do
|
9
14
|
it "makes a new task instance" do
|