Module: Zillabyte::Command

Extended by:
Helpers
Defined in:
lib/zillabyte/command.rb,
lib/zillabyte/cli/base.rb

Defined Under Namespace

Classes: Apps, Auth, Base, CommandFailed, Components, Data, Flows, Git, Help, Keys, Nuke, Query, Repl, Version

Constant Summary collapse

BaseWithApp =
Base

Class Method Summary collapse

Methods included from Helpers

add_git_remote, app, ask, create_git_remote, display, error, extract_app_from_git_config, extract_app_in_dir, format_with_bang, get_flow_name, git, handle_downloading_manifest, has_git?, longest, read_multiline, with_tty

Class Method Details

.command_aliases ⇒ Object



24
25
26
# File 'lib/zillabyte/command.rb', line 24

def self.command_aliases
  @@command_aliases ||= {}
end

.commands ⇒ Object



20
21
22
# File 'lib/zillabyte/command.rb', line 20

def self.commands
  @@commands ||= {}
end

.current_args ⇒ Object



58
59
60
# File 'lib/zillabyte/command.rb', line 58

def self.current_args
  @current_args
end

.current_command ⇒ Object



50
51
52
# File 'lib/zillabyte/command.rb', line 50

def self.current_command
  @current_command
end

.current_command=(new_current_command) ⇒ Object



54
55
56
# File 'lib/zillabyte/command.rb', line 54

def self.current_command=(new_current_command)
  @current_command = new_current_command
end

.current_options ⇒ Object



62
63
64
# File 'lib/zillabyte/command.rb', line 62

def self.current_options
  @current_options ||= {}
end

.display_warnings ⇒ Object



100
101
102
103
104
# File 'lib/zillabyte/command.rb', line 100

def self.display_warnings
  unless warnings.empty?
    $stderr.puts(warnings.map {|warning| " !    #{warning}"}.join("\n"))
  end
end

.extract_error(body, options = {}) ⇒ Object



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

def self.extract_error(body, options={})
  default_error = block_given? ? yield : "Internal server error.\nRun `zillabyte status` to check for known platform issues."
  parse_error_xml(body) || parse_error_json(body) || parse_error_plain(body) || default_error
end

.files ⇒ Object



28
29
30
# File 'lib/zillabyte/command.rb', line 28

def self.files
  @@files ||= Hash.new {|hash,key| hash[key] = File.readlines(key).map {|line| line.strip}}
end

.global_option(name, *args, &blk) ⇒ Object



106
107
108
109
# File 'lib/zillabyte/command.rb', line 106

def self.global_option(name, *args, &blk)
  # args.sort.reverse gives -l, --long order
  global_options << { :name => name.to_s, :args => args.sort.reverse, :proc => blk }
end

.global_options ⇒ Object



66
67
68
# File 'lib/zillabyte/command.rb', line 66

def self.global_options
  @global_options ||= []
end

.invalid_arguments ⇒ Object



70
71
72
# File 'lib/zillabyte/command.rb', line 70

def self.invalid_arguments
  @invalid_arguments
end

.load ⇒ Object



10
11
12
13
14
15
16
17
18
# File 'lib/zillabyte/command.rb', line 10

def self.load
  Dir[File.join(File.dirname(__FILE__), "cli", "*.rb")].each do |file|
    require file
  end
  Dir[File.join(File.dirname(__FILE__), "cli", "helpers", "*.rb")].each do |file|
    require file
  end
  unregister_commands_made_private_after_the_fact
end

.namespaces ⇒ Object



32
33
34
# File 'lib/zillabyte/command.rb', line 32

def self.namespaces
  @@namespaces ||= {}
end

.parse(cmd) ⇒ Object



231
232
233
# File 'lib/zillabyte/command.rb', line 231

def self.parse(cmd)
  commands[cmd] || commands[command_aliases[cmd]]
end

.parse_error_json(body) ⇒ Object



247
248
249
250
251
252
253
254
255
256
257
# File 'lib/zillabyte/command.rb', line 247

def self.parse_error_json(body)
  json = json_decode(body.to_s) rescue false
  case json
  when Array
    json.first.join(' ') # message like [['base', 'message']]
  when Hash
    json['error']   # message like {'error' => 'message'}
  else
    nil
  end
end

.parse_error_plain(body) ⇒ Object



259
260
261
262
# File 'lib/zillabyte/command.rb', line 259

def self.parse_error_plain(body)
  return unless body.respond_to?(:headers) && body.headers[:content_type].to_s.include?("text/plain")
  body.to_s
end

.parse_error_xml(body) ⇒ Object



240
241
242
243
244
245
# File 'lib/zillabyte/command.rb', line 240

def self.parse_error_xml(body)
  xml_errors = REXML::Document.new(body).elements.to_a("//errors/error")
  msg = xml_errors.map { |a| a.text }.join(" / ")
  return msg unless msg.empty?
rescue Exception
end

.prepare_run(cmd, args = []) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/zillabyte/command.rb', line 116

def self.prepare_run(cmd, args=[])
  command = parse(cmd)

  if args.include?('-h') || args.include?('--help')
    args.unshift(cmd) unless cmd =~ /^-.*/
    cmd = 'help'
    command = parse(cmd)
  end

  if cmd == '--version' or cmd == '-version'
    cmd = 'version'
    command = parse(cmd)
  end

  @current_command = cmd
  @anonymized_args, @normalized_args = [], []

  opts = {}
  invalid_options = []

  parser = OptionParser.new do |parser|
    # remove OptionParsers Officious['version'] to avoid conflicts
    # see: https://github.com/ruby/ruby/blob/trunk/lib/optparse.rb#L814
    parser.base.long.delete('version')
    (global_options + (command && command[:options] || [])).each do |option|
      parser.on(*option[:args]) do |value|
        if option[:proc]
          option[:proc].call(value)
        end
        opts[option[:name].gsub('-', '_').to_sym] = value
        ARGV.join(' ') =~ /(#{option[:args].map {|arg| arg.split(' ', 2).first}.join('|')})/
        @anonymized_args << "#{$1} _"
        @normalized_args << "#{option[:args].last.split(' ', 2).first} _"
      end
    end
  end

  begin
    parser.order!(args) do |nonopt|
      invalid_options << nonopt
      @anonymized_args << '!'
      @normalized_args << '!'
    end
  rescue OptionParser::InvalidOption => ex
    invalid_options << ex.args.first
    @anonymized_args << '!'
    @normalized_args << '!'
    retry
  end

  args.concat(invalid_options)

  # Make the --json command universal.. 
  if invalid_options.include?("--json")
    invalid_options.delete("--json")
    opts[:output_type] = "json"
  end

  @current_args = args
  @current_options = opts
  @invalid_arguments = invalid_options
  
  @anonymous_command = [ARGV.first, *@anonymized_args].join(' ')
  begin
    usage_directory = "#{home_directory}/.zillabyte/usage"
    FileUtils.mkdir_p(usage_directory)
    usage_file = usage_directory << "/#{Zillabyte::CLI::VERSION}"
    usage = if File.exists?(usage_file)
      json_decode(File.read(usage_file))
    else
      {}
    end
    usage[@anonymous_command] ||= 0
    usage[@anonymous_command] += 1
    File.write(usage_file, json_encode(usage) + "\n")
  rescue
    # usage writing is not important, allow failures
  end

  if command
    command_instance = command[:klass].new(args.dup, opts.dup)

    if !@normalized_args.include?('--app _') && (implied_app = command_instance.app rescue nil)
      @normalized_args << '--app _'
    end
    @normalized_command = [ARGV.first, @normalized_args.sort_by {|arg| arg.gsub('-', '')}].join(' ')

    [ command_instance, command[:method] ]
  else
    error([
      "`#{cmd}` is not a zillabyte command.",
      "See `zillabyte help` for a list of available commands."
    ].compact.join("\n"))
  end
end

.register_command(command) ⇒ Object



36
37
38
# File 'lib/zillabyte/command.rb', line 36

def self.register_command(command)
  commands[command[:command]] = command
end

.register_namespace(namespace) ⇒ Object



46
47
48
# File 'lib/zillabyte/command.rb', line 46

def self.register_namespace(namespace)
  namespaces[namespace[:name]] = namespace
end

.run(cmd, arguments = []) ⇒ Object



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/zillabyte/command.rb', line 212

def self.run(cmd, arguments=[])
  begin
    object, method = prepare_run(cmd, arguments.dup)
    object.send(method)
  rescue Excon::Errors::SocketError => e
    if ENV['BUBBLE_EXCEPTIONS']
      raise e
    else
      error "remote server error: #{e.message}"
    end
  rescue Errno::ECONNREFUSED => e
    if ENV['BUBBLE_EXCEPTIONS']
      raise e
    else
      error "remote server error: #{e.message}"
    end
  end
end

.shift_argument ⇒ Object



74
75
76
77
# File 'lib/zillabyte/command.rb', line 74

def self.shift_argument
  # dup argument to get a non-frozen string
  @invalid_arguments.shift.dup rescue nil
end

.unregister_commands_made_private_after_the_fact ⇒ Object



40
41
42
43
44
# File 'lib/zillabyte/command.rb', line 40

def self.unregister_commands_made_private_after_the_fact
  commands.values \
    .select { |c| c[:klass].private_method_defined? c[:method] } \
    .each   { |c| commands.delete c[:command] }
end

.validate_arguments! ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/zillabyte/command.rb', line 79

def self.validate_arguments!
  unless invalid_arguments.empty?
    arguments = invalid_arguments.map {|arg| "\"#{arg}\""}
    if arguments.length == 1
      message = "Invalid argument: #{arguments.first}"
    elsif arguments.length > 1
      message = "Invalid arguments: "
      message << arguments[0...-1].join(", ")
      message << " and "
      message << arguments[-1]
    end
    $stderr.puts(format_with_bang(message))
    run(current_command, ["--help"])
    exit(1)
  end
end

.warnings ⇒ Object



96
97
98
# File 'lib/zillabyte/command.rb', line 96

def self.warnings
  @warnings ||= []
end