Module: Conify::Command

Extended by:
Command, Helpers
Included in:
Command
Defined in:
lib/conify/command.rb

Defined Under Namespace

Classes: AbstractCommand, Global

Constant Summary collapse

CMD_BLACKLIST =
[]

Instance Method Summary collapse

Methods included from Helpers

allow_user_response, ask_for_conflux_creds, ask_for_password, ask_for_password_on_windows, camelize, display, echo_off, echo_on, error, exclusive_deep_merge, format_with_bang, host, host_url, kensa_manifest_name, kensa_manifest_path, manifest_content, manifest_filename, manifest_path, manually_added_methods, open_url, running_on_a_mac?, running_on_windows?, site_url, to_table, with_tty

Instance Method Details

#command_description(command_info_module) ⇒ Object



170
171
172
# File 'lib/conify/command.rb', line 170

def command_description(command_info_module)
  command_info_module.const_defined?('DESCRIPTION') ? command_info_module::DESCRIPTION : ''
end

#command_file_pathsObject

Feturn an array of all command file paths, with the exception of abstract_command.rb



267
268
269
270
# File 'lib/conify/command.rb', line 267

def command_file_paths
  abstract_file = File.join(File.dirname(__FILE__), 'command', 'abstract_command.rb')
  Dir[File.join(File.dirname(__FILE__), 'command', '*.rb')] - [abstract_file]
end

#command_valid_args(command_info_module) ⇒ Object



166
167
168
# File 'lib/conify/command.rb', line 166

def command_valid_args(command_info_module)
  command_info_module.const_defined?('VALID_ARGS') ? command_info_module::VALID_ARGS : []
end

#commandsObject



272
273
274
# File 'lib/conify/command.rb', line 272

def commands
  @@commands ||= {}
end

#create_commands_mapObject

Create a commands map to respond to ‘conify help` with.



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/conify/command.rb', line 196

def create_commands_map
  # Require all the ruby command files
  command_file_paths.each do |file|
    require file

    # Get basename for the file without the extension
    basename = get_basename_from_file(file)

    # Camelcase the basename to be the klass name
    klass_name = camelize(basename)

    # return the command klass for this klass_name
    command_klass = Conify::Command.const_get(klass_name)

    # For each of the user-defined methods inside this class, create a command for it
    manually_added_methods(command_klass).each { |method|
      register_command(basename, method.to_s, command_klass, global: basename == 'global')
    }
  end
end

#error_no_commandObject



120
121
122
123
124
125
# File 'lib/conify/command.rb', line 120

def error_no_command
  error([
    "`#{@current_cmd}` is not a conify command.",
    "Type `conify help` for a list of available commands."
  ].compact.join("\n"))
end

#file_for_command(command) ⇒ Object

Create a command file path from the name of a command



111
112
113
# File 'lib/conify/command.rb', line 111

def file_for_command(command)
  File.join(File.dirname(__FILE__), 'command', "#{command}.rb")
end

#find_command(cmd, args = []) ⇒ Object

Finds file/method for command



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/conify/command.rb', line 14

def find_command(cmd, args = [])
  @current_cmd = cmd
  @current_args = args

  respond_with_help if seeking_help?
  respond_with_version if seeking_version?

  # Separate out primary/secondary commands based on if command was namespaced
  # e.g. `conify services vs. conify services:add`
  primary_cmd, secondary_cmd = @current_cmd.split(':')

  # Get the command file path (string) for the primary command
  primary_cmd_file = file_for_command(primary_cmd)

  # If the primary command has it's own file, require it
  primary_cmd_file_exists = File.exists?(primary_cmd_file)
  require primary_cmd_file if primary_cmd_file_exists

  # If a secondary command exists, the primary_cmd_file must be where our command method lies
  if !secondary_cmd.nil?
    error_no_command if !primary_cmd_file_exists

    # Get command_klass for file path. Example response --> Conify::Command::Services
    command_klass = klass_for_file(primary_cmd_file)

    # Error out if the command klass doesn't have a method named <secondary_cmd>
    error_no_command if !klass_has_method?(command_klass, secondary_cmd)

    run(command_klass, secondary_cmd)

  # If there's no secondary command, there are 2 options for where the command method could be (in order of priority):
  # (1) Inside the primary command file as the 'index' method
  # (2) Inside the global command file, as a method named <primary_cmd>
  else
    # Store lambda for later
    try_global = lambda {
      require 'conify/command/global'
      command_klass = Conify::Command::Global
      error_no_command if !klass_has_method?(command_klass, primary_cmd)
      run(command_klass, primary_cmd)
    }

    # Number 1 above. If primary_cmd file exists, call the index method on it if it exists.
    # If index method doens't exist, check to see if method is a global command.
    if primary_cmd_file_exists
      # Get command_klass for file path. Example response --> Conify::Command::Services
      command_klass = klass_for_file(primary_cmd_file)

      klass_has_method?(command_klass, 'index') ? run(command_klass, 'index') : try_global.call

    # Number 2 above. Check to see if method is a global command inside command/global.rb
    else
      try_global.call
    end
  end
end

#get_basename_from_file(file) ⇒ Object

Return just the basename for a file, no extensions.



261
262
263
264
# File 'lib/conify/command.rb', line 261

def get_basename_from_file(file)
  basename = Pathname.new(file).basename.to_s
  basename[0..(basename.rindex('.') - 1)]
end

#handle_invalid_args(command_info_module) ⇒ Object

Respond to the user in the instance of invalid arguments.



152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/conify/command.rb', line 152

def handle_invalid_args(command_info_module)
  if !@invalid_args.empty?
    message = 'Invalid argument'
    message += 's' if @invalid_args.length > 1
    args = @invalid_args.map { |arg| "\"#{arg}\"" }.join(', ')

    puts " !    #{message}: #{args}"
  else
    puts " !    Invalid command usage"
  end

  respond_with_command_help(command_info_module)
end

#klass_for_file(file) ⇒ Object

Get a command klass back from a file path: Example I/O: ‘command/bundles’ –> Conify::Command::Bundles



99
100
101
102
103
104
105
106
107
108
# File 'lib/conify/command.rb', line 99

def klass_for_file(file)
  # Get basename for the file without the extension
  basename = get_basename_from_file(file)

  # Camelcase the basename to be the klass name
  klass_name = camelize(basename)

  # return the command klass for this klass_name
  Conify::Command.const_get(klass_name)
end

#klass_has_method?(klass, method) ⇒ Boolean

Check to see if user-defined method exists on a klass

Returns:

  • (Boolean)


116
117
118
# File 'lib/conify/command.rb', line 116

def klass_has_method?(klass, method)
  manually_added_methods(klass).include?(method.to_sym)
end

#register_command(basename, action, command_class, global: false) ⇒ Object

register a command’s info to the @@commands map - utilized when calling ‘conify help`



277
278
279
280
281
282
283
# File 'lib/conify/command.rb', line 277

def register_command(basename, action, command_class, global: false)
  command = global ? action : (action == 'index' ? basename : "#{basename}:#{action}")

  command_info_module = command_class::CommandInfo.const_get(camelize(action))

  commands[command] = { description: command_description(command_info_module) }
end

#respond_with_command_help(command_info_module) ⇒ Object

Respond to command-specific help



240
241
242
243
244
245
246
247
248
# File 'lib/conify/command.rb', line 240

def respond_with_command_help(command_info_module)
  help = "\nValid Command Formats:\n\n"

  command_valid_args(command_info_module).each { |format|
    help += "#  conify #{@current_cmd} #{format.join(' ')}\n"
  }

  puts "#{help}\n"
end

#respond_with_helpObject



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/conify/command.rb', line 179

def respond_with_help
  create_commands_map

  header = [
    'Usage: conify COMMAND [command-specific-arguments]',
    'Type "conify COMMAND --help" for more details about each command',
    'Commands:'
  ].join("\n\n")

  commands_info = usage_info(commands)

  puts "\n#{header}"
  puts "\n#{commands_info}\n\n"
  exit(0)
end

#respond_with_versionObject



255
256
257
258
# File 'lib/conify/command.rb', line 255

def respond_with_version
  display "conify #{Conify::VERSION}"
  exit(0)
end

#run(klass, method) ⇒ Object

Call a method on a klass with certain arguments. Will validate arguments first before calling method.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/conify/command.rb', line 73

def run(klass, method)
  # Get the command info for this method on this klass
  command_info_module = klass::CommandInfo.const_get(camelize(method))

  # If seeking help for this command with --help or -h
  if seeking_command_help?(@current_args)
    puts "\nPurpose: #{command_description(command_info_module)}\n"

    # respond with command-specific help
    respond_with_command_help(command_info_module)
    return
  end

  # get the valid arguments defined for this comand
  valid_args = command_valid_args(command_info_module)

  if !valid_args?(valid_args)
    handle_invalid_args(command_info_module)
    return
  end

  klass.new(@current_args.dup).send(method)
end

#seeking_command_help?(args) ⇒ Boolean

Seeking command-specific help. e.g. ‘conify bundles –help`

Returns:

  • (Boolean)


235
236
237
# File 'lib/conify/command.rb', line 235

def seeking_command_help?(args)
  args.include?('-h') || args.include?('--help')
end

#seeking_help?Boolean

stdin is ‘conify help` or `conify -h`

Returns:

  • (Boolean)


175
176
177
# File 'lib/conify/command.rb', line 175

def seeking_help?
  @current_args.length == 0 && (@current_cmd.empty? || ['help', '--help', '-h'].include?(@current_cmd))
end

#seeking_version?Boolean

stdin is ‘conify –version` or `conify -v`

Returns:

  • (Boolean)


251
252
253
# File 'lib/conify/command.rb', line 251

def seeking_version?
  @current_args.length == 0 && (@current_cmd == '--version' || @current_cmd == '-v')
end

#usage_info(map) ⇒ Object

Format a map of commands into help output format



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/conify/command.rb', line 218

def usage_info(map)
  keys = map.keys
  commands_column_width = keys.max_by(&:length).length + 1
  commands_column_width += 2 if commands_column_width < 12

  # iterate through each of the commands, create an array
  # of strings in a `<command>  #  <description>` format. Sort
  # them alphabetically, and then join them with new lines.
  keys.map { |key|
    command = "  #{key}"
    command += (' ' * (commands_column_width - key.length + 1))
    command += "#  #{map[key][:description]}"
    command
  }.sort_by{ |k| k.downcase }.join("\n")
end

#valid_args?(accepted_arg_formats) ⇒ Boolean

Check if passed-in arguments are valid for a specific format

Returns:

  • (Boolean)


128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/conify/command.rb', line 128

def valid_args?(accepted_arg_formats)
  valid_args = false

  accepted_arg_formats.each { |format|
    # if no arguments exist, and no arguments is an accepted format, args are valid.
    if format.empty? && @current_args.empty?
      valid_args = true
    else
      passed_in_args = @current_args.clone

      format.each_with_index { |arg, i|
        passed_in_args[i] = arg if CMD_BLACKLIST.include?(arg)
      }

      @invalid_args = passed_in_args - format - [nil]

      valid_args = true if passed_in_args == format
    end
  }

  valid_args
end