lib/airbrussh/rake/context.rb
module Airbrussh
module Rake
# Maintains information about what Rake task is currently being invoked,
# in order to be able to decorate SSHKit commands with additional
# context-sensitive information. Works via a monkey patch to Rake::Task,
# which can be disabled via by setting
# Airbrussh.configuration.monkey_patch_rake = false.
#
# Note that this class is not thread-safe. Normally this is not a problem,
# but some Capistrano users are known to use `invoke` to switch the Rake
# task in the middle of an SSHKit thread, which causes Context to get very
# confused. It such scenarios Context is not reliable and may return `nil`
# for the `position` of a command.
#
class Context
class << self
attr_accessor :current_task_name
end
def initialize(config=Airbrussh.configuration)
@history = []
@enabled = config.monkey_patch_rake
self.class.install_monkey_patch if enabled?
end
# Returns the name of the currently-executing rake task, if it can be
# determined. If monkey patching is disabled, this will be nil.
def current_task_name
return nil unless enabled?
self.class.current_task_name
end
# Update the context when a new command starts by:
# * Clearing the command history if the rake task has changed
# * Recording the command in the history
#
# Returns whether or not this command was the first execution
# of this command in the current rake task
def register_new_command(command)
reset_history_if_task_changed
first_execution = !history.include?(command.to_s)
history << command.to_s
history.uniq!
first_execution
end
# The zero-based position of the specified command in the current rake
# task. May be `nil` in certain multi-threaded scenarios, so be careful!
def position(command)
history.index(command.to_s)
end
if Object.respond_to?(:prepend)
module Patch
def execute(args=nil)
::Airbrussh::Rake::Context.current_task_name = name.to_s
super
end
end
def self.install_monkey_patch
require "rake"
return if ::Rake::Task.include?(::Airbrussh::Rake::Context::Patch)
::Rake::Task.prepend(::Airbrussh::Rake::Context::Patch)
end
else
def self.install_monkey_patch
require "rake"
return if ::Rake::Task.instance_methods.include?(:_airbrussh_execute)
::Rake::Task.class_exec do
alias_method :_airbrussh_execute, :execute
def execute(args=nil)
::Airbrussh::Rake::Context.current_task_name = name.to_s
_airbrussh_execute(args)
end
end
end
end
private
attr_reader :history
attr_accessor :last_task_name
def reset_history_if_task_changed
history.clear if last_task_name != current_task_name
self.last_task_name = current_task_name
end
def enabled?
@enabled
end
end
end
end