class Heroku::Command::Base

Attributes

args[R]
options[R]

Public Class Methods

namespace() click to toggle source
# File lib/heroku/command/base.rb, line 10
def self.namespace
  self.to_s.split("::").last.downcase
end
new(args=[], options={}) click to toggle source
# File lib/heroku/command/base.rb, line 17
def initialize(args=[], options={})
  @args = args
  @options = options
end

Protected Class Methods

alias_command(new, old) click to toggle source
# File lib/heroku/command/base.rb, line 120
def self.alias_command(new, old)
  raise "no such command: #{old}" unless Heroku::Command.commands[old]
  Heroku::Command.command_aliases[new] = old
end
extract_banner(help) click to toggle source
# File lib/heroku/command/base.rb, line 170
def self.extract_banner(help)
  help.first
end
extract_description(help) click to toggle source
# File lib/heroku/command/base.rb, line 178
def self.extract_description(help)
  help.reject do |line|
    line =~ /^\s+-(.+)#(.+)/
  end.join("\n")
end
extract_help(file, line_number) click to toggle source
# File lib/heroku/command/base.rb, line 152
def self.extract_help(file, line_number)
  buffer = []
  lines = Heroku::Command.files[file]

  (line_number.to_i-2).downto(0) do |i|
    line = lines[i]
    case line[0..0]
      when ""
      when "#"
        buffer.unshift(line[1..-1])
      else
        break
    end
  end

  buffer
end
extract_help_from_caller(line) click to toggle source

Parse the caller format and identify the file and line number as identified in : www.ruby-doc.org/core/classes/Kernel.html#M001397. This will look for a colon followed by a digit as the delimiter. The biggest complication is windows paths, which have a colon after the drive letter. This regex will match paths as anything from the beginning to a colon directly followed by a number (the line number).

Examples of the caller format :

  • c:/Ruby192/lib/…/lib/heroku/command/addons.rb:8:in `<module:Command>'

  • c:/Ruby192/lib/…/heroku-2.0.1/lib/heroku/command/pg.rb:96:in `<class:Pg>'

  • /Users/ph7/.…./xray-1.1/lib/xray/thread_dump_signal_handler.rb:9

# File lib/heroku/command/base.rb, line 143
def self.extract_help_from_caller(line)
  # pull out of the caller the information for the file path and line number
  if line =~ /^(.+?):(\d+)/
    extract_help($1, $2)
  else
    raise("unable to extract help from caller: #{line}")
  end
end
extract_options(help) click to toggle source
# File lib/heroku/command/base.rb, line 184
def self.extract_options(help)
  help.select do |line|
    line =~ /^\s+-(.+)#(.+)/
  end.inject([]) do |options, line|
    args = line.split('#', 2).first
    args = args.split(/,\s*/).map {|arg| arg.strip}.sort.reverse
    name = args.last.split(' ', 2).first[2..-1]
    options << { :name => name, :args => args }
  end
end
extract_summary(help) click to toggle source
# File lib/heroku/command/base.rb, line 174
def self.extract_summary(help)
  extract_description(help).split("\n")[2].to_s.split("\n").first
end
inherited(klass) click to toggle source
# File lib/heroku/command/base.rb, line 84
def self.inherited(klass)
  unless klass == Heroku::Command::Base
    help = extract_help_from_caller(caller.first)

    Heroku::Command.register_namespace(
      :name => klass.namespace,
      :description => help.first
    )
  end
end
method_added(method) click to toggle source
# File lib/heroku/command/base.rb, line 95
def self.method_added(method)
  return if self == Heroku::Command::Base
  return if private_method_defined?(method)
  return if protected_method_defined?(method)

  help = extract_help_from_caller(caller.first)
  resolved_method = (method.to_s == "index") ? nil : method.to_s
  command = [ self.namespace, resolved_method ].compact.join(":")
  banner = extract_banner(help) || command

  Heroku::Command.register_command(
    :klass       => self,
    :method      => method,
    :namespace   => self.namespace,
    :command     => command,
    :banner      => banner.strip,
    :help        => help.join("\n"),
    :summary     => extract_summary(help),
    :description => extract_description(help),
    :options     => extract_options(help)
  )

  alias_command command.gsub(/_/, '-'), command if command =~ /_/
end

Public Instance Methods

api() click to toggle source
# File lib/heroku/command/base.rb, line 70
def api
  Heroku::Auth.api
end
app() click to toggle source
# File lib/heroku/command/base.rb, line 22
def app
  @app ||= if options[:confirm].is_a?(String)
    if options[:app] && (options[:app] != options[:confirm])
      error("Mismatch between --app and --confirm")
    end
    options[:confirm]
  elsif options[:app].is_a?(String)
    options[:app]
  elsif ENV.has_key?('HEROKU_APP')
    ENV['HEROKU_APP']
  elsif app_from_dir = extract_app_in_dir(Dir.pwd)
    app_from_dir
  else
    # raise instead of using error command to enable rescuing when app is optional
    raise Heroku::Command::CommandFailed.new("No app specified.\nRun this command from an app folder or specify which app to use with --app APP.") unless options[:ignore_no_app]
  end
end
heroku() click to toggle source
# File lib/heroku/command/base.rb, line 78
def heroku
  Heroku::Auth.client
end
org() click to toggle source
# File lib/heroku/command/base.rb, line 40
def org
  @nil = false
  options[:ignore_no_app] = true

  @org ||= if skip_org?
    nil
  elsif options[:org].is_a?(String)
    options[:org]
  elsif options[:personal] || @nil
    nil
  elsif org_from_app = extract_org_from_app
    org_from_app
  else
    response = org_api.get_orgs.body
    default = response['user']['default_organization']
    if default
      options[:using_default_org] = true
      default
    elsif options[:ignore_no_org]
      nil
    else
      # raise instead of using error command to enable rescuing when app is optional
      raise Heroku::Command::CommandFailed.new("No org specified.\nRun this command from an app folder which belongs to an org or specify which org to use with --org ORG.")
    end
  end

  @nil = true if @org == nil
  @org
end
org_api() click to toggle source
# File lib/heroku/command/base.rb, line 74
def org_api
  Heroku::Client::Organizations.api
end

Protected Instance Methods

current_command() click to toggle source
# File lib/heroku/command/base.rb, line 195
def current_command
  Heroku::Command.current_command
end
escape(value) click to toggle source
# File lib/heroku/command/base.rb, line 284
def escape(value)
  heroku.escape(value)
end
extract_app() click to toggle source
# File lib/heroku/command/base.rb, line 125
def extract_app
  output_with_bang "Command::Base#extract_app has been deprecated. Please use Command::Base#app instead.  #{caller.first}"
  app
end
extract_app_from_git_config() click to toggle source
# File lib/heroku/command/base.rb, line 232
def extract_app_from_git_config
  remote = git("config heroku.remote")
  remote == "" ? nil : remote
end
extract_app_in_dir(dir) click to toggle source
# File lib/heroku/command/base.rb, line 215
def extract_app_in_dir(dir)
  return unless remotes = git_remotes(dir)

  if remote = options[:remote]
    remotes[remote]
  elsif remote = extract_app_from_git_config
    remotes[remote]
  else
    apps = remotes.values.uniq
    if apps.size == 1
      apps.first
    else
      raise(Heroku::Command::CommandFailed, "Multiple apps in folder and no app specified.\nSpecify app with --app APP.") unless options[:ignore_no_app]
    end
  end
end
extract_option(key) click to toggle source
# File lib/heroku/command/base.rb, line 199
def extract_option(key)
  options[key.dup.gsub('-','_').to_sym]
end
extract_org_from_app() click to toggle source
# File lib/heroku/command/base.rb, line 237
def extract_org_from_app
  return unless app

  begin
    owner = api.get_app(app).body["owner_email"].split("@")
    if owner.last == Heroku::Helpers.org_host
      owner.first
    else
      nil
    end
  rescue
    nil
  end
end
git_remotes(base_dir=Dir.pwd) click to toggle source
# File lib/heroku/command/base.rb, line 263
def git_remotes(base_dir=Dir.pwd)
  remotes = {}
  original_dir = Dir.pwd
  Dir.chdir(base_dir)

  return unless File.exists?(".git")
  git("remote -v").split("\n").each do |remote|
    name, url, method = remote.split(/\s/)
    if url =~ /^git@#{Heroku::Auth.git_host}(?:[\.\w]*):([\w\d-]+)\.git$/
      remotes[name] = $1
    end
  end

  Dir.chdir(original_dir)
  if remotes.empty?
    nil
  else
    remotes
  end
end
invalid_arguments() click to toggle source
# File lib/heroku/command/base.rb, line 203
def invalid_arguments
  Heroku::Command.invalid_arguments
end
org_from_app!() click to toggle source
# File lib/heroku/command/base.rb, line 252
def org_from_app!
  options[:org] = extract_org_from_app
  options[:personal] = true unless options[:org]
end
shift_argument() click to toggle source
# File lib/heroku/command/base.rb, line 207
def shift_argument
  Heroku::Command.shift_argument
end
skip_org?() click to toggle source
# File lib/heroku/command/base.rb, line 257
def skip_org?
  return false if ENV['HEROKU_CLOUD'].nil? || ENV['HEROKU_MANAGER_URL']

  !%w{default production prod}.include? ENV['HEROKU_CLOUD']
end
validate_arguments!() click to toggle source
# File lib/heroku/command/base.rb, line 211
def validate_arguments!
  Heroku::Command.validate_arguments!
end