module Azuki::Command
Constants
- BaseWithApp
Public Class Methods
command_aliases()
click to toggle source
# File lib/azuki/command.rb, line 23 def self.command_aliases @@command_aliases ||= {} end
commands()
click to toggle source
# File lib/azuki/command.rb, line 19 def self.commands @@commands ||= {} end
current_args()
click to toggle source
# File lib/azuki/command.rb, line 51 def self.current_args @current_args end
current_command()
click to toggle source
# File lib/azuki/command.rb, line 43 def self.current_command @current_command end
current_command=(new_current_command)
click to toggle source
# File lib/azuki/command.rb, line 47 def self.current_command=(new_current_command) @current_command = new_current_command end
current_options()
click to toggle source
# File lib/azuki/command.rb, line 55 def self.current_options @current_options ||= {} end
display_warnings()
click to toggle source
# File lib/azuki/command.rb, line 93 def self.display_warnings unless warnings.empty? $stderr.puts(warnings.map {|warning| " ! #{warning}"}.join("\n")) end end
extract_error(body, options={}) { |: "Internal server error.\nRun `azuki status` to check for known platform issues."| ... }
click to toggle source
# File lib/azuki/command.rb, line 265 def self.extract_error(body, options={}) default_error = block_given? ? yield : "Internal server error.\nRun `azuki status` to check for known platform issues." parse_error_xml(body) || parse_error_json(body) || parse_error_plain(body) || default_error end
files()
click to toggle source
# File lib/azuki/command.rb, line 27 def self.files @@files ||= Hash.new {|hash,key| hash[key] = File.readlines(key).map {|line| line.strip}} end
global_option(name, *args, &blk)
click to toggle source
# File lib/azuki/command.rb, line 99 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()
click to toggle source
# File lib/azuki/command.rb, line 59 def self.global_options @global_options ||= [] end
invalid_arguments()
click to toggle source
# File lib/azuki/command.rb, line 63 def self.invalid_arguments @invalid_arguments end
load()
click to toggle source
# File lib/azuki/command.rb, line 12 def self.load Dir[File.join(File.dirname(__FILE__), "command", "*.rb")].each do |file| require file end Azuki::Plugin.load! end
namespaces()
click to toggle source
# File lib/azuki/command.rb, line 31 def self.namespaces @@namespaces ||= {} end
parse(cmd)
click to toggle source
# File lib/azuki/command.rb, line 261 def self.parse(cmd) commands[cmd] || commands[command_aliases[cmd]] end
parse_error_json(body)
click to toggle source
# File lib/azuki/command.rb, line 277 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)
click to toggle source
# File lib/azuki/command.rb, line 289 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)
click to toggle source
# File lib/azuki/command.rb, line 270 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=[])
click to toggle source
# File lib/azuki/command.rb, line 112 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' 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) @current_args = args @current_options = opts @invalid_arguments = invalid_options @anonymous_command = [ARGV.first, *@anonymized_args].join(' ') begin usage_directory = "#{home_directory}/.azuki/usage" FileUtils.mkdir_p(usage_directory) usage_file = usage_directory << "/#{Azuki::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 azuki command.", suggestion(cmd, commands.keys + command_aliases.keys), "See `azuki help` for a list of available commands." ].compact.join("\n")) end end
register_command(command)
click to toggle source
# File lib/azuki/command.rb, line 35 def self.register_command(command) commands[command[:command]] = command end
register_namespace(namespace)
click to toggle source
# File lib/azuki/command.rb, line 39 def self.register_namespace(namespace) namespaces[namespace[:name]] = namespace end
run(cmd, arguments=[])
click to toggle source
# File lib/azuki/command.rb, line 203 def self.run(cmd, arguments=[]) begin object, method = prepare_run(cmd, arguments.dup) object.send(method) rescue Interrupt, StandardError, SystemExit => error # load likely error classes, as they may not be loaded yet due to defered loads require 'azuki-api' require 'rest_client' raise(error) end rescue Azuki::API::Errors::Unauthorized, RestClient::Unauthorized puts "Authentication failure" unless ENV['AZUKI_API_KEY'] run "login" retry end rescue Azuki::API::Errors::VerificationRequired, RestClient::PaymentRequired => e retry if Azuki::Helpers.confirm_billing rescue Azuki::API::Errors::NotFound => e error extract_error(e.response.body) { e.response.body =~ /^([\w\s]+ not found).?$/ ? $1 : "Resource not found" } rescue RestClient::ResourceNotFound => e error extract_error(e.http_body) { e.http_body =~ /^([\w\s]+ not found).?$/ ? $1 : "Resource not found" } rescue Azuki::API::Errors::Locked => e app = e.response.headers[:x_confirmation_required] if confirm_command(app, extract_error(e.response.body)) arguments << '--confirm' << app retry end rescue RestClient::Locked => e app = e.response.headers[:x_confirmation_required] if confirm_command(app, extract_error(e.http_body)) arguments << '--confirm' << app retry end rescue Azuki::API::Errors::Timeout, RestClient::RequestTimeout error "API request timed out. Please try again, or contact support@azukiapp.com if this issue persists." rescue Azuki::API::Errors::ErrorWithResponse => e error extract_error(e.response.body) rescue RestClient::RequestFailed => e error extract_error(e.http_body) rescue CommandFailed => e error e.message rescue OptionParser::ParseError commands[cmd] ? run("help", [cmd]) : run("help") rescue Excon::Errors::SocketError => e if e.message == 'getaddrinfo: nodename nor servname provided, or not known (SocketError)' error("Unable to connect to Azuki API, please check internet connectivity and try again.") else raise(e) end ensure display_warnings end
shift_argument()
click to toggle source
# File lib/azuki/command.rb, line 67 def self.shift_argument # dup argument to get a non-frozen string @invalid_arguments.shift.dup rescue nil end
validate_arguments!()
click to toggle source
# File lib/azuki/command.rb, line 72 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()
click to toggle source
# File lib/azuki/command.rb, line 89 def self.warnings @warnings ||= [] end