howzit 2.0.6 → 2.0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -10,7 +10,7 @@ module Howzit
10
10
  def initialize(file: nil, args: [])
11
11
  @topics = []
12
12
  create_note if note_file.nil?
13
- @metadata = IO.read(note_file).split(/^#/)[0].strip.get_metadata
13
+ @metadata = Util.read_file(note_file).split(/^#/)[0].strip.get_metadata
14
14
 
15
15
  read_help(file)
16
16
  end
@@ -89,7 +89,7 @@ module Howzit
89
89
  # Create a buildnotes skeleton
90
90
  def create_note
91
91
  trap('SIGINT') do
92
- warn "\nCanceled"
92
+ Howzit.console.info "\nCancelled"
93
93
  exit!
94
94
  end
95
95
  default = !$stdout.isatty || Howzit.options[:default]
@@ -210,9 +210,11 @@ module Howzit
210
210
  buildnotes.reverse
211
211
  end
212
212
 
213
- def is_build_notes(filename)
214
- return false if filename.downcase !~ /(^howzit[^.]*|build[^.]+)/
213
+ def build_note?(filename)
214
+ return false if filename.downcase !~ /^(howzit[^.]*|build[^.]+)/
215
+
215
216
  return false if Howzit.config.should_ignore(filename)
217
+
216
218
  true
217
219
  end
218
220
 
@@ -222,7 +224,7 @@ module Howzit
222
224
  # with "build" and have an extension of txt, md, or markdown.
223
225
 
224
226
  Dir.glob('*.{txt,md,markdown}').each do |f|
225
- if is_build_notes(f)
227
+ if build_note?(f)
226
228
  filename = f
227
229
  break
228
230
  end
@@ -262,15 +264,15 @@ module Howzit
262
264
  end
263
265
 
264
266
  def ensure_requirements(template)
265
- t_leader = IO.read(template).split(/^#/)[0].strip
267
+ t_leader = Util.read_file(template).split(/^#/)[0].strip
266
268
  if t_leader.length > 0
267
269
  t_meta = t_leader.get_metadata
268
270
  if t_meta.key?('required')
269
271
  required = t_meta['required'].strip.split(/\s*,\s*/)
270
272
  required.each do |req|
271
273
  unless @metadata.keys.include?(req.downcase)
272
- warn %({xr}ERROR: Missing required metadata key from template '{bw}#{File.basename(template, '.md')}{xr}'{x}).c
273
- warn %({xr}Please define {by}#{req.downcase}{xr} in build notes{x}).c
274
+ Howzit.console.error %({xr}ERROR: Missing required metadata key from template '{bw}#{File.basename(template, '.md')}{xr}'{x}).c
275
+ Howzit.console.error %({xr}Please define {by}#{req.downcase}{xr} in build notes{x}).c
274
276
  Process.exit 1
275
277
  end
276
278
  end
@@ -330,7 +332,7 @@ module Howzit
330
332
 
331
333
  return m[0] unless File.exist?(file)
332
334
 
333
- content = IO.read(file)
335
+ content = Util.read_file(file)
334
336
  home = ENV['HOME']
335
337
  short_path = File.dirname(file.sub(/^#{home}/, '~'))
336
338
  prefix = "#{short_path}/#{File.basename(file)}:"
@@ -344,7 +346,7 @@ module Howzit
344
346
  end
345
347
 
346
348
  def note_title(truncate = 0)
347
- help = IO.read(note_file).strip
349
+ help = Util.read_file(note_file)
348
350
  title = help.match(/(?:^(\S.*?)(?=\n==)|^# ?(.*?)$)/)
349
351
  title = if title
350
352
  title[1].nil? ? title[2] : title[1]
@@ -361,7 +363,7 @@ module Howzit
361
363
 
362
364
  filename = path.nil? ? note_file : path
363
365
 
364
- help = IO.read(filename)
366
+ help = Util.read_file(filename)
365
367
 
366
368
  @title = note_title
367
369
 
data/lib/howzit/config.rb CHANGED
@@ -3,6 +3,7 @@ module Howzit
3
3
  class Config
4
4
  attr_reader :options
5
5
 
6
+ # Configuration defaults
6
7
  DEFAULTS = {
7
8
  color: true,
8
9
  config_editor: ENV['EDITOR'] || nil,
@@ -22,18 +23,31 @@ module Howzit
22
23
  wrap: 0
23
24
  }.deep_freeze
24
25
 
26
+ ##
27
+ ## Initialize a config object
28
+ ##
25
29
  def initialize
26
30
  load_options
27
31
  end
28
32
 
33
+ ##
34
+ ## Write a config to a file
35
+ ##
36
+ ## @param config The configuration
37
+ ##
29
38
  def write_config(config)
30
39
  File.open(config_file, 'w') { |f| f.puts config.to_yaml }
31
40
  end
32
41
 
42
+ ##
43
+ ## Test if a file should be ignored based on YAML file
44
+ ##
45
+ ## @param filename The filename to test
46
+ ##
33
47
  def should_ignore(filename)
34
48
  return false unless File.exist?(ignore_file)
35
49
 
36
- @ignore_patterns ||= YAML.safe_load(IO.read(ignore_file))
50
+ @ignore_patterns ||= YAML.safe_load(Util.read_file(ignore_file))
37
51
 
38
52
  ignore = false
39
53
 
@@ -47,16 +61,29 @@ module Howzit
47
61
  ignore
48
62
  end
49
63
 
64
+ ##
65
+ ## Find the template folder
66
+ ##
67
+ ## @return [String] path to template folder
68
+ ##
50
69
  def template_folder
51
70
  File.join(config_dir, 'templates')
52
71
  end
53
72
 
73
+ ##
74
+ ## Initiate the editor for the config
75
+ ##
54
76
  def editor
55
77
  edit_config(DEFAULTS)
56
78
  end
57
79
 
58
80
  private
59
81
 
82
+ ##
83
+ ## Load command line options
84
+ ##
85
+ ## @return [Hash] options with command line flags merged in
86
+ ##
60
87
  def load_options
61
88
  Color.coloring = $stdout.isatty
62
89
  flags = {
@@ -77,40 +104,68 @@ module Howzit
77
104
  @options = flags.merge(config)
78
105
  end
79
106
 
107
+ ##
108
+ ## Get the config directory
109
+ ##
110
+ ## @return [String] path to config directory
111
+ ##
80
112
  def config_dir
81
113
  File.expand_path(CONFIG_DIR)
82
114
  end
83
115
 
116
+ ##
117
+ ## Get the config file
118
+ ##
119
+ ## @return [String] path to config file
120
+ ##
84
121
  def config_file
85
122
  File.join(config_dir, CONFIG_FILE)
86
123
  end
87
124
 
125
+ ##
126
+ ## Get the ignore config file
127
+ ##
128
+ ## @return [String] path to ignore config file
129
+ ##
88
130
  def ignore_file
89
131
  File.join(config_dir, IGNORE_FILE)
90
132
  end
91
133
 
92
- def create_config(d)
134
+ ##
135
+ ## Create a new config file (and directory if needed)
136
+ ##
137
+ ## @param default [Hash] default configuration to write
138
+ ##
139
+ def create_config(default)
93
140
  unless File.directory?(config_dir)
94
- warn "Creating config directory at #{config_dir}"
141
+ Howzit::ConsoleLogger.new(1).info "Creating config directory at #{config_dir}"
95
142
  FileUtils.mkdir_p(config_dir)
96
143
  end
97
144
 
98
145
  unless File.exist?(config_file)
99
- warn "Writing fresh config file to #{config_file}"
100
- write_config(d)
146
+ Howzit::ConsoleLogger.new(1).info "Writing fresh config file to #{config_file}"
147
+ write_config(default)
101
148
  end
102
149
  config_file
103
150
  end
104
151
 
152
+ ##
153
+ ## Load the config file
154
+ ##
155
+ ## @return [Hash] configuration object
156
+ ##
105
157
  def load_config
106
158
  file = create_config(DEFAULTS)
107
- config = YAML.load(IO.read(file))
159
+ config = YAML.load(Util.read_file(file))
108
160
  newconfig = config ? DEFAULTS.merge(config) : DEFAULTS
109
161
  write_config(newconfig)
110
162
  newconfig.dup
111
163
  end
112
164
 
113
- def edit_config(d)
165
+ ##
166
+ ## Open the config in an editor
167
+ ##
168
+ def edit_config
114
169
  editor = Howzit.options.fetch(:config_editor, ENV['EDITOR'])
115
170
 
116
171
  raise 'No config_editor defined' if editor.nil?
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ LOG_LEVELS = {
4
+ debug: 0,
5
+ info: 1,
6
+ warn: 2,
7
+ error: 3
8
+ }.deep_freeze
9
+
10
+ module Howzit
11
+ # Console logging
12
+ class ConsoleLogger
13
+ attr_accessor :log_level
14
+
15
+ ##
16
+ ## Init the console logging object
17
+ ##
18
+ ## @param level [Integer] log level
19
+ ##
20
+ def initialize(level = nil)
21
+ @log_level = level.to_i || Howzit.options[:log_level]
22
+ end
23
+
24
+ ##
25
+ ## Get the log level from options
26
+ ##
27
+ ## @return [Integer] log level
28
+ ##
29
+ def reset_level
30
+ @log_level = Howzit.options[:log_level]
31
+ end
32
+
33
+ ##
34
+ ## Write a message to the console based on the urgency
35
+ ## level and user's log level setting
36
+ ##
37
+ ## @param msg [String] The message
38
+ ## @param level [Symbol] The level
39
+ ##
40
+ def write(msg, level = :info)
41
+ $stderr.puts msg if LOG_LEVELS[level] >= @log_level
42
+ end
43
+
44
+ ##
45
+ ## Write a message at debug level
46
+ ##
47
+ ## @param msg The message
48
+ ##
49
+ def debug(msg)
50
+ write msg, :debug
51
+ end
52
+
53
+ ##
54
+ ## Write a message at info level
55
+ ##
56
+ ## @param msg The message
57
+ ##
58
+ def info(msg)
59
+ write msg, :info
60
+ end
61
+
62
+ ##
63
+ ## Write a message at warn level
64
+ ##
65
+ ## @param msg The message
66
+ ##
67
+ def warn(msg)
68
+ write msg, :warn
69
+ end
70
+
71
+ ##
72
+ ## Write a message at error level
73
+ ##
74
+ ## @param msg The message
75
+ ##
76
+ def error(msg)
77
+ write msg, :error
78
+ end
79
+ end
80
+ end
data/lib/howzit/prompt.rb CHANGED
@@ -8,14 +8,15 @@ module Howzit
8
8
  return default unless $stdout.isatty
9
9
 
10
10
  return default if Howzit.options[:default]
11
-
12
- system 'stty cbreak'
11
+ tty_state = `stty -g`
12
+ system 'stty raw -echo cbreak isig'
13
13
  yn = color_single_options(default ? %w[Y n] : %w[y N])
14
14
  $stdout.syswrite "\e[1;37m#{prompt} #{yn}\e[1;37m? \e[0m"
15
15
  res = $stdin.sysread 1
16
16
  res.chomp!
17
17
  puts
18
18
  system 'stty cooked'
19
+ system "stty #{tty_state}"
19
20
  res.empty? ? default : res =~ /y/i
20
21
  end
21
22
 
@@ -24,12 +25,12 @@ module Howzit
24
25
  choices.each do |choice|
25
26
  case choice
26
27
  when /[A-Z]/
27
- out.push(Color.template("{bg}#{choice}{xg}"))
28
+ out.push(Color.template("{bw}#{choice}{x}"))
28
29
  else
29
- out.push(Color.template("{w}#{choice}"))
30
+ out.push(Color.template("{dw}#{choice}{xg}"))
30
31
  end
31
32
  end
32
- Color.template("{g}[#{out.join('/')}{g}]{x}")
33
+ Color.template("{xg}[#{out.join('/')}{xg}]{x}")
33
34
  end
34
35
 
35
36
  def options_list(matches)
@@ -54,7 +55,7 @@ module Howzit
54
55
  ]
55
56
  res = `echo #{Shellwords.escape(matches.join("\n"))} | fzf #{settings.join(' ')}`.strip
56
57
  if res.nil? || res.empty?
57
- warn 'Cancelled'
58
+ Howzit.console.info 'Cancelled'
58
59
  Process.exit 0
59
60
  end
60
61
  return res.split(/\n/)
@@ -147,7 +147,7 @@ module Howzit
147
147
 
148
148
  def extract_metadata
149
149
  if File.exist?(self)
150
- leader = IO.read(self).split(/^#/)[0].strip
150
+ leader = Util.read_file(self).split(/^#/)[0].strip
151
151
  leader.length > 0 ? leader.get_metadata : {}
152
152
  else
153
153
  {}
data/lib/howzit/task.rb CHANGED
@@ -13,6 +13,10 @@ module Howzit
13
13
  @default = default
14
14
  end
15
15
 
16
+ def inspect
17
+ %(<#Howzit::Task @type=:#{@type} @title="#{@title}" @block?=#{@action.split(/\n/).count > 1}>)
18
+ end
19
+
16
20
  def to_s
17
21
  @title
18
22
  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('This topic has prerequisites, have they been met?', default: true)
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
@@ -42,7 +59,7 @@ module Howzit
42
59
  end
43
60
 
44
61
  if task.type == :block
45
- warn "{bg}Running block {bw}#{title}{x}".c if Howzit.options[:log_level] < 2
62
+ Howzit.console.info "{bg}Running block {bw}#{title}{x}".c if Howzit.options[:log_level] < 2
46
63
  block = task.action
47
64
  script = Tempfile.new('howzit_script')
48
65
  begin
@@ -81,11 +98,11 @@ module Howzit
81
98
  end
82
99
  end
83
100
  else
84
- warn "{r}--run: No {br}@directive{xr} found in {bw}#{@title}{x}".c
101
+ Howzit.console.warn "{r}--run: No {br}@directive{xr} found in {bw}#{@title}{x}".c
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
@@ -95,21 +112,21 @@ module Howzit
95
112
  out = "{bg}Copying {bw}#{string}".c
96
113
  case os
97
114
  when /darwin.*/i
98
- warn "#{out} (macOS){x}".c if Howzit.options[:log_level] < 2
115
+ $stderr.puts "#{out} (macOS){x}".c if Howzit.options[:log_level].zero?
99
116
  `echo #{Shellwords.escape(string)}'\\c'|pbcopy`
100
117
  when /mingw|mswin/i
101
- warn "#{out} (Windows){x}".c if Howzit.options[:log_level] < 2
118
+ $stderr.puts "#{out} (Windows){x}".c if Howzit.options[:log_level].zero?
102
119
  `echo #{Shellwords.escape(string)} | clip`
103
120
  else
104
121
  if 'xsel'.available?
105
- warn "#{out} (Linux, xsel){x}".c if Howzit.options[:log_level] < 2
122
+ $stderr.puts "#{out} (Linux, xsel){x}".c if Howzit.options[:log_level].zero?
106
123
  `echo #{Shellwords.escape(string)}'\\c'|xsel -i`
107
124
  elsif 'xclip'.available?
108
- warn "#{out} (Linux, xclip){x}".c if Howzit.options[:log_level] < 2
125
+ $stderr.puts "#{out} (Linux, xclip){x}".c if Howzit.options[:log_level].zero?
109
126
  `echo #{Shellwords.escape(string)}'\\c'|xclip -i`
110
127
  else
111
- warn out if Howzit.options[:log_level] < 2
112
- warn 'Unable to determine executable for clipboard.'
128
+ $stderr.puts out if Howzit.options[:log_level].zero?
129
+ $stderr.puts 'Unable to determine executable for clipboard.'
113
130
  end
114
131
  end
115
132
  end
@@ -119,18 +136,18 @@ module Howzit
119
136
  out = "{bg}Opening {bw}#{command}".c
120
137
  case os
121
138
  when /darwin.*/i
122
- warn "#{out} (macOS){x}".c if Howzit.options[:log_level] < 2
139
+ Howzit.console.debug "#{out} (macOS){x}".c if Howzit.options[:log_level] < 2
123
140
  `open #{Shellwords.escape(command)}`
124
141
  when /mingw|mswin/i
125
- warn "#{out} (Windows){x}".c if Howzit.options[:log_level] < 2
142
+ Howzit.console.debug "#{out} (Windows){x}".c if Howzit.options[:log_level] < 2
126
143
  `start #{Shellwords.escape(command)}`
127
144
  else
128
145
  if 'xdg-open'.available?
129
- warn "#{out} (Linux){x}".c if Howzit.options[:log_level] < 2
146
+ Howzit.console.debug "#{out} (Linux){x}".c if Howzit.options[:log_level] < 2
130
147
  `xdg-open #{Shellwords.escape(command)}`
131
148
  else
132
- warn out if Howzit.options[:log_level] < 2
133
- warn 'Unable to determine executable for `open`.'
149
+ Howzit.console.debug out if Howzit.options[:log_level] < 2
150
+ Howzit.console.debug 'Unable to determine executable for `open`.'
134
151
  end
135
152
  end
136
153
  end
data/lib/howzit/util.rb CHANGED
@@ -1,21 +1,26 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Howzit
4
+ # Util class
2
5
  module Util
3
6
  class << self
7
+ def read_file(path)
8
+ IO.read(path).force_encoding('utf-8').strip
9
+ end
10
+
4
11
  def valid_command?(command)
5
12
  cmd = command.split(' ')[0]
6
13
  command_exist?(cmd)
7
14
  end
8
-
15
+
9
16
  def command_exist?(command)
10
17
  exts = ENV.fetch('PATHEXT', '').split(::File::PATH_SEPARATOR)
11
18
  if Pathname.new(command).absolute?
12
- ::File.exist?(command) ||
13
- exts.any? { |ext| ::File.exist?("#{command}#{ext}") }
19
+ ::File.exist?(command) || exts.any? { |ext| ::File.exist?("#{command}#{ext}") }
14
20
  else
15
21
  ENV.fetch('PATH', '').split(::File::PATH_SEPARATOR).any? do |dir|
16
22
  file = ::File.join(dir, command)
17
- ::File.exist?(file) ||
18
- exts.any? { |ext| ::File.exist?("#{file}#{ext}") }
23
+ ::File.exist?(file) || exts.any? { |ext| ::File.exist?("#{file}#{ext}") }
19
24
  end
20
25
  end
21
26
  end
@@ -40,7 +45,7 @@ module Howzit
40
45
  if hl.available?
41
46
  Howzit.options[:highlighter]
42
47
  else
43
- warn Color.template("{Rw}Error:{xbw} Specified highlighter (#{Howzit.options[:highlighter]}) not found, switching to auto")
48
+ Howzit.console.error Color.template("{Rw}Error:{xbw} Specified highlighter (#{Howzit.options[:highlighter]}) not found, switching to auto")
44
49
  Howzit.options[:highlighter] = 'auto'
45
50
  which_highlighter
46
51
  end
@@ -78,7 +83,7 @@ module Howzit
78
83
  if pg.available?
79
84
  Howzit.options[:pager]
80
85
  else
81
- warn Color.template("{Rw}Error:{xbw} Specified pager (#{Howzit.options[:pager]}) not found, switching to auto")
86
+ Howzit.console.error Color.template("{Rw}Error:{xbw} Specified pager (#{Howzit.options[:pager]}) not found, switching to auto")
82
87
  Howzit.options[:pager] = 'auto'
83
88
  which_pager
84
89
  end
@@ -104,7 +109,7 @@ module Howzit
104
109
  begin
105
110
  exec(pager)
106
111
  rescue SystemCallError => e
107
- @log.error(e)
112
+ Howzit.console.error(e)
108
113
  exit 1
109
114
  end
110
115
  end
@@ -3,5 +3,5 @@
3
3
  # Primary module for this gem.
4
4
  module Howzit
5
5
  # Current Howzit version.
6
- VERSION = '2.0.6'
6
+ VERSION = '2.0.9'
7
7
  end
data/lib/howzit.rb CHANGED
@@ -5,8 +5,9 @@ require_relative 'howzit/prompt'
5
5
  require_relative 'howzit/colors'
6
6
  require_relative 'howzit/stringutils'
7
7
 
8
- require_relative 'howzit/util'
9
8
  require_relative 'howzit/hash'
9
+ require_relative 'howzit/console_logger'
10
+ require_relative 'howzit/util'
10
11
  require_relative 'howzit/config'
11
12
  require_relative 'howzit/task'
12
13
  require_relative 'howzit/topic'
@@ -20,6 +21,7 @@ require 'tempfile'
20
21
  require 'yaml'
21
22
 
22
23
  require 'tty/screen'
24
+ require 'tty/box'
23
25
  # require 'tty/prompt'
24
26
 
25
27
  CONFIG_DIR = '~/.config/howzit'
@@ -29,6 +31,7 @@ MATCHING_OPTIONS = %w[partial exact fuzzy beginswith].freeze
29
31
  MULTIPLE_OPTIONS = %w[first best all choose].freeze
30
32
  HEADER_FORMAT_OPTIONS = %w[border block].freeze
31
33
 
34
+ # Main module for howzit
32
35
  module Howzit
33
36
  class << self
34
37
  attr_accessor :arguments, :cli_args
@@ -52,5 +55,9 @@ module Howzit
52
55
  def buildnote
53
56
  @buildnote ||= BuildNote.new
54
57
  end
58
+
59
+ def console
60
+ @console ||= Howzit::ConsoleLogger.new(options[:log_level])
61
+ end
55
62
  end
56
63
  end
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
@@ -10,6 +10,7 @@
10
10
  # end
11
11
 
12
12
  require 'howzit'
13
+ require 'cli-test'
13
14
 
14
15
  RSpec.configure do |c|
15
16
  c.expect_with(:rspec) { |e| e.syntax = :expect }