Class: Chef::Knife::UI

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/chef/knife/core/ui.rb

Overview

The User Interaction class used by knife.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(stdout, stderr, stdin, config) ⇒ UI

Returns a new instance of UI.



46
47
48
49
# File 'lib/chef/knife/core/ui.rb', line 46

def initialize(stdout, stderr, stdin, config)
  @stdout, @stderr, @stdin, @config = stdout, stderr, stdin, config
  @presenter = Chef::Knife::Core::GenericPresenter.new(self, config)
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



37
38
39
# File 'lib/chef/knife/core/ui.rb', line 37

def config
  @config
end

#presenterObject (readonly)

Returns the value of attribute presenter.



39
40
41
# File 'lib/chef/knife/core/ui.rb', line 39

def presenter
  @presenter
end

#stderrObject (readonly)

Returns the value of attribute stderr.



35
36
37
# File 'lib/chef/knife/core/ui.rb', line 35

def stderr
  @stderr
end

#stdinObject (readonly)

Returns the value of attribute stdin.



36
37
38
# File 'lib/chef/knife/core/ui.rb', line 36

def stdin
  @stdin
end

#stdoutObject (readonly)

Returns the value of attribute stdout.



34
35
36
# File 'lib/chef/knife/core/ui.rb', line 34

def stdout
  @stdout
end

Instance Method Details

#ask(*args, **options, &block) ⇒ Object



176
177
178
# File 'lib/chef/knife/core/ui.rb', line 176

def ask(*args, **options, &block)
  prompt.ask(*args, **options, &block)
end

#ask_question(question, opts = {}) ⇒ Object



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/chef/knife/core/ui.rb', line 197

def ask_question(question, opts = {})
  question += "[#{opts[:default]}] " if opts[:default]

  if opts[:default] && config[:defaults]
    opts[:default]
  else
    stdout.print question
    a = stdin.readline.strip

    if opts[:default]
      a.empty? ? opts[:default] : a
    else
      a
    end
  end
end

#color(string, *colors) ⇒ Object



161
162
163
164
165
166
167
# File 'lib/chef/knife/core/ui.rb', line 161

def color(string, *colors)
  if color?
    pastel.decorate(string, *colors)
  else
    string
  end
end

#color?Boolean

Should colored output be used? For output to a terminal, this is determined by the value of ‘config`. When output is not to a terminal, colored output is never used

Returns:

  • (Boolean)


172
173
174
# File 'lib/chef/knife/core/ui.rb', line 172

def color?
  Chef::Config[:color] && stdout.tty?
end

#confirm(question, append_instructions = true, default_choice = nil) ⇒ Object

Not the ideal signature for a function but we need to stick with this for now until we get a chance to break our API in Chef 12.

question => Question to print before asking for confirmation append_instructions => Should print ‘? (Y/N)’ as instructions default_choice => Set to true for ‘Y’, and false for ‘N’ as default answer



330
331
332
333
334
335
# File 'lib/chef/knife/core/ui.rb', line 330

def confirm(question, append_instructions = true, default_choice = nil)
  unless confirm_without_exit(question, append_instructions, default_choice)
    exit 3
  end
  true
end

#confirm_without_exit(question, append_instructions = true, default_choice = nil) ⇒ Object

See confirm method for argument information



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/chef/knife/core/ui.rb', line 292

def confirm_without_exit(question, append_instructions = true, default_choice = nil)
  return true if config[:yes]

  stdout.print question
  stdout.print confirmation_instructions(default_choice) if append_instructions

  answer = stdin.readline
  answer.chomp!

  case answer
  when "Y", "y"
    true
  when "N", "n"
    msg("You said no, so I'm done here.")
    false
  when ""
    unless default_choice.nil?
      default_choice
    else
      msg("I have no idea what to do with '#{answer}'")
      msg("Just say Y or N, please.")
      confirm_without_exit(question, append_instructions, default_choice)
    end
  else
    msg("I have no idea what to do with '#{answer}'")
    msg("Just say Y or N, please.")
    confirm_without_exit(question, append_instructions, default_choice)
  end
end

#confirmation_instructions(default_choice) ⇒ Object



280
281
282
283
284
285
286
287
288
289
# File 'lib/chef/knife/core/ui.rb', line 280

def confirmation_instructions(default_choice)
  case default_choice
  when true
    "? (Y/n) "
  when false
    "? (y/N) "
  else
    "? (Y/N) "
  end
end

#debug(message) ⇒ Object

Print a Debug

Parameters:

  • message (String)

    the text string



128
129
130
# File 'lib/chef/knife/core/ui.rb', line 128

def debug(message)
  log("#{color("DEBUG:", :blue, :bold)} #{message}")
end

#edit_data(data, parse_output = true, object_class: nil) ⇒ Object



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/chef/knife/core/ui.rb', line 230

def edit_data(data, parse_output = true, object_class: nil)
  output = Chef::JSONCompat.to_json_pretty(data)
  unless config[:disable_editing]
    Tempfile.open([ "knife-edit-", ".json" ]) do |tf|
      tf.sync = true
      tf.puts output
      tf.close
      raise "Please set EDITOR environment variable. See https://docs.chef.io/workstation/knife_setup/#setting-your-text-editor for details." unless system("#{config[:editor]} #{tf.path}")

      output = IO.read(tf.path)
    end
  end

  if parse_output
    if object_class.nil?
      raise ArgumentError, "Please pass in the object class to hydrate or use #edit_hash"
    else
      object_class.from_hash(Chef::JSONCompat.parse(output))
    end
  else
    output
  end
end

#edit_hash(hash) ⇒ Object

Hash -> Hash Works the same as edit_data but returns a hash rather than a JSON string/Fully inflated object



225
226
227
228
# File 'lib/chef/knife/core/ui.rb', line 225

def edit_hash(hash)
  raw = edit_data(hash, false)
  Chef::JSONCompat.parse(raw)
end

#edit_object(klass, name) ⇒ Object



254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/chef/knife/core/ui.rb', line 254

def edit_object(klass, name)
  object = klass.load(name)

  output = edit_data(object, object_class: klass)

  # Only make the save if the user changed the object.
  #
  # Output JSON for the original (object) and edited (output), then parse
  # them without reconstituting the objects into real classes
  # (create_additions=false). Then, compare the resulting simple objects,
  # which will be Array/Hash/String/etc.
  #
  # We wouldn't have to do these shenanigans if all the editable objects
  # implemented to_hash, or if to_json against a hash returned a string
  # with stable key order.
  object_parsed_again = Chef::JSONCompat.parse(Chef::JSONCompat.to_json(object))
  output_parsed_again = Chef::JSONCompat.parse(Chef::JSONCompat.to_json(output))
  if object_parsed_again != output_parsed_again
    output.save
    msg("Saved #{output}")
  else
    msg("Object unchanged, not saving")
  end
  output(format_for_display(object)) if config[:print_after]
end

#error(message) ⇒ Object

Print an error message

Parameters:

  • message (String)

    the text string



142
143
144
# File 'lib/chef/knife/core/ui.rb', line 142

def error(message)
  log("#{color("ERROR:", :red, :bold)} #{message}")
end

#fatal(message) ⇒ Object

Print a message describing a fatal error.

Parameters:

  • message (String)

    the text string



149
150
151
# File 'lib/chef/knife/core/ui.rb', line 149

def fatal(message)
  log("#{color("FATAL:", :red, :bold)} #{message}")
end

#fatal!(message) ⇒ Object

Print a message describing a fatal error and exit 1

Parameters:

  • message (String)

    the text string



156
157
158
159
# File 'lib/chef/knife/core/ui.rb', line 156

def fatal!(message)
  fatal(message)
  exit 1
end

#highlineObject



58
59
60
61
62
63
# File 'lib/chef/knife/core/ui.rb', line 58

def highline
  @highline ||= begin
    require "highline"
    HighLine.new
  end
end

#interchange?Boolean

Determines if the output format is a data interchange format, i.e., JSON or YAML

Returns:

  • (Boolean)


193
194
195
# File 'lib/chef/knife/core/ui.rb', line 193

def interchange?
  @presenter.interchange?
end

#list(*args) ⇒ Object



180
181
182
# File 'lib/chef/knife/core/ui.rb', line 180

def list(*args)
  highline.list(*args)
end

#log(message) ⇒ Object Also known as: info, err

Prints a msg to stderr. Used for info, warn, error, and fatal.

Parameters:

  • message (String)

    the text string



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/chef/knife/core/ui.rb', line 97

def log(message)
  lines = message.split("\n")
  first_line = lines.shift
  stderr.puts first_line
  # If the message is multiple lines,
  # indent subsequent lines to align with the
  # log type prefix ("ERROR: ", etc)
  unless lines.empty?
    prefix, = first_line.split(":", 2)
    return if prefix.nil?

    prefix_len = prefix.length
    prefix_len -= 9 if color? # prefix includes 9 bytes of color escape sequences
    prefix_len += 2 # include room to align to the ": " following PREFIX
    padding = " " * prefix_len
    lines.each do |line|
      stderr.puts "#{padding}#{line}"
    end
  end
rescue Errno::EPIPE => e
  raise e if @config[:verbosity] >= 2

  exit 0
end

#msg(message) ⇒ Object

Prints a message to stdout. Aliased as info for compatibility with the logger API.

Parameters:

  • message (String)

    the text string



86
87
88
89
90
91
92
# File 'lib/chef/knife/core/ui.rb', line 86

def msg(message)
  stdout.puts message
rescue Errno::EPIPE => e
  raise e if @config[:verbosity] >= 2

  exit 0
end

#output(data) ⇒ Object

Formats data using the configured presenter and outputs the result via msg. Formatting can be customized by configuring a different presenter. See use_presenter



187
188
189
# File 'lib/chef/knife/core/ui.rb', line 187

def output(data)
  msg @presenter.format(data)
end

#pastelObject

pastel.decorate is a lightweight replacement for highline.color



75
76
77
78
79
80
# File 'lib/chef/knife/core/ui.rb', line 75

def pastel
  @pastel ||= begin
    require "pastel" unless defined?(Pastel)
    Pastel.new
  end
end

#pretty_print(data) ⇒ Object



214
215
216
217
218
219
220
# File 'lib/chef/knife/core/ui.rb', line 214

def pretty_print(data)
  stdout.puts data
rescue Errno::EPIPE => e
  raise e if @config[:verbosity] >= 2

  exit 0
end

#promptObject

Creates a new object of class TTY::Prompt with interrupt as exit so that it can be terminated with status code.



67
68
69
70
71
72
# File 'lib/chef/knife/core/ui.rb', line 67

def prompt
  @prompt ||= begin
    require "tty-prompt"
    TTY::Prompt.new(interrupt: :exit)
  end
end

#use_presenter(presenter_class) ⇒ Object

Creates a new presenter_class object and uses it to format structured data for display. By default, a Chef::Knife::Core::GenericPresenter object is used.



54
55
56
# File 'lib/chef/knife/core/ui.rb', line 54

def use_presenter(presenter_class)
  @presenter = presenter_class.new(self, config)
end

#warn(message) ⇒ Object

Print a warning message

Parameters:

  • message (String)

    the text string



135
136
137
# File 'lib/chef/knife/core/ui.rb', line 135

def warn(message)
  log("#{color("WARNING:", :yellow, :bold)} #{message}")
end