All Files
(92.31%
covered at
2.62
hits/line)
12 files in total.
442 relevant lines.
408 lines covered and
34 lines missed
-
1
require 'mattock/tasklib'
-
1
require 'mattock/command-line'
-
1
require 'mattock/remote-command-task'
-
1
require 'mattock/template-host'
-
1
require 'mattock/configurable'
-
-
1
module Mattock
-
1
module CascadingDefinition
-
1
include Configurable
-
-
1
def initialize(*tasklibs)
-
6
setup_defaults
-
6
default_configuration(*tasklibs)
-
-
6
yield self if block_given?
-
-
6
resolve_configuration
-
6
confirm_configuration
-
-
6
define
-
end
-
-
1
def default_configuration(*tasklibs)
-
end
-
-
1
def resolve_configuration
-
end
-
-
1
def confirm_configuration
-
6
check_required
-
end
-
-
1
def define
-
end
-
end
-
end
-
1
module Mattock
-
1
class CommandRunResult
-
1
def initialize(command, status, streams)
-
5
@command = command
-
5
@process_status = status
-
5
@streams = streams
-
end
-
1
attr_reader :process_status, :streams
-
-
1
def stdout
-
1
@streams[1]
-
end
-
-
1
def stderr
-
@streams[2]
-
end
-
-
1
def exit_code
-
11
@process_status.exitstatus
-
end
-
1
alias exit_status exit_code
-
-
1
def succeeded?
-
3
must_succeed!
-
1
return true
-
rescue
-
2
return false
-
end
-
-
1
def must_succeed!
-
5
case exit_code
-
when 0
-
3
return exit_code
-
else
-
2
fail "Command #{@command.inspect} failed with exit status #{$?.exitstatus}: \n#{streams.inspect}"
-
end
-
end
-
end
-
-
1
class CommandLine
-
1
def initialize(executable, *options)
-
14
@executable = executable
-
14
@options = options
-
14
@redirections = []
-
14
yield self if block_given?
-
end
-
-
1
attr_accessor :name, :executable, :options
-
-
1
def verbose
-
21
Rake.verbose && Rake.verbose != Rake::FileUtilsExt::DEFAULT
-
end
-
-
1
def name
-
1
@name || executable
-
end
-
-
1
def command
-
12
([executable] + options + @redirections).join(" ")
-
end
-
-
1
def redirect_to(stream, path)
-
2
@redirections << "#{stream}>#{path}"
-
end
-
-
1
def redirect_from(path, stream)
-
@redirections << "#{stream}<#{path}"
-
end
-
-
1
def copy_stream_to(from, to)
-
@redirections << "#{from}>&#{to}"
-
end
-
-
1
def redirect_stdout(path)
-
1
redirect_to(1, path)
-
end
-
-
1
def redirect_stderr(path)
-
1
redirect_to(2, path)
-
end
-
-
1
def redirect_stdin(path)
-
redirect_from(path, 0)
-
end
-
-
1
def self.execute(command)
-
5
pipe = IO.popen(command)
-
5
pid = pipe.pid
-
5
pid, status = Process.wait2(pid)
-
5
result = CommandRunResult.new(command, status, {1 => pipe.read})
-
5
pipe.close
-
5
return result
-
end
-
-
1
def run
-
7
print command + " " if verbose
-
7
result = self.class.execute(command)
-
7
print "=> #{result.exit_code}" if verbose
-
7
return result
-
ensure
-
7
puts if verbose
-
end
-
-
1
def succeeds?
-
3
run.succeeded?
-
end
-
-
1
def must_succeed!
-
2
run.must_succeed!
-
end
-
end
-
-
1
class ShellEscaped < CommandLine
-
1
def initialize(cmd)
-
2
@escaped = cmd
-
end
-
-
1
def command
-
2
"'" + @escaped.command.gsub(/'/,"\'") + "'"
-
end
-
-
1
def name
-
@name || @escaped.name
-
end
-
end
-
-
1
class CommandChain < CommandLine
-
1
def initialize
-
4
@commands = []
-
4
yield self if block_given?
-
end
-
-
1
attr_reader :commands
-
-
1
def add(cmd)
-
8
yield cmd if block_given?
-
8
@commands << cmd
-
end
-
-
1
def name
-
@name || @commands.last.name
-
end
-
end
-
-
1
class WrappingChain < CommandChain
-
1
def command
-
6
@commands.map{|cmd| cmd.command}.join(" -- ")
-
end
-
end
-
-
1
class PrereqChain < CommandChain
-
1
def command
-
3
@commands.map{|cmd| cmd.command}.join(" && ")
-
end
-
end
-
-
1
class PipelineChain < CommandChain
-
1
def command
-
3
@commands.map{|cmd| cmd.command}.join(" | ")
-
end
-
end
-
end
-
1
require 'mattock/task'
-
1
require 'mattock/command-line'
-
-
1
module Mattock
-
1
class CommandTask < Task
-
1
setting(:task_name, :run)
-
1
setting(:command)
-
1
setting(:verify_command, nil)
-
-
1
def verify_command
-
4
if @verify_command.respond_to?(:call)
-
@verify_command = @verify_command.call
-
end
-
4
@verify_command
-
end
-
-
1
def decorated(cmd)
-
cmd
-
end
-
-
1
def action
-
1
decorated(command).must_succeed!
-
end
-
-
1
def needed?
-
1
unless verify_command.nil?
-
1
!decorated(verify_command).succeeds?
-
else
-
super
-
end
-
end
-
end
-
end
-
1
module Mattock
-
1
module Configurable
-
1
RequiredField = Object.new
-
1
class << RequiredField
-
1
def to_s
-
3
"<unset>"
-
end
-
-
1
def inspect
-
3
to_s
-
end
-
end
-
1
RequiredField.freeze
-
-
1
module ClassMethods
-
-
1
def default_values
-
99
@default_values ||= {}
-
end
-
-
1
def set_defaults_on(instance)
-
31
if Configurable > superclass
-
16
superclass.set_defaults_on(instance)
-
end
-
31
default_values.each_pair do |name,value|
-
42
instance.__send__("#{name}=", value)
-
42
if Configurable === value
-
5
value.class.set_defaults_on(value)
-
end
-
end
-
end
-
-
1
def missing_required_fields_on(instance)
-
27
missing = []
-
27
if Configurable > superclass
-
14
missing = superclass.missing_required_fields_on(instance)
-
end
-
27
default_values.each_pair do |name,value|
-
35
set_value = instance.__send__(name)
-
35
if value == RequiredField and set_value == RequiredField
-
3
missing << name
-
3
next
-
end
-
32
if Configurable === set_value
-
4
missing += set_value.class.missing_required_fields_on(set_value).map do |field|
-
2
[name, field].join(".")
-
end
-
end
-
end
-
27
return missing
-
end
-
-
1
def copy_settings(from, to)
-
3
if Configurable > superclass
-
2
superclass.copy_settings(from, to)
-
end
-
3
default_values.keys.each do |field|
-
7
begin
-
7
to.__send__("#{field}=", from.__send__(field))
-
rescue NoMethodError
-
#shrug it off
-
end
-
end
-
end
-
-
1
def nested(hash=nil)
-
2
obj = Class.new(Struct).new
-
2
obj.settings(hash || {})
-
2
return obj
-
end
-
-
1
def nil_fields(*names)
-
2
names.each do |name|
-
3
setting(name, nil)
-
end
-
end
-
1
alias nil_field nil_fields
-
-
1
def required_fields(*names)
-
2
names.each do |name|
-
2
setting(name)
-
end
-
end
-
1
alias required_field required_fields
-
-
# @macro [attack] configurable_property
-
# @method $1
-
# @return [$2] The default value of $1
-
# @method $1=
-
1
def setting(name, default_value = RequiredField)
-
19
name = name.to_sym
-
19
attr_accessor(name)
-
19
if default_values.has_key?(name) and default_values[name] != default_value
-
warn "Changing default value of #{self.name}##{name} from #{default_values[name].inspect} to #{default_value.inspect}"
-
end
-
19
default_values[name] = default_value
-
end
-
-
1
def settings(hash)
-
1
hash.each_pair do |name, value|
-
2
setting(name, value)
-
end
-
1
return self
-
end
-
-
1
def included(mod)
-
7
mod.extend ClassMethods
-
end
-
end
-
1
extend ClassMethods
-
-
1
def copy_settings_to(other)
-
1
self.class.copy_settings(self, other)
-
1
self
-
end
-
-
1
def setup_defaults
-
10
self.class.set_defaults_on(self)
-
10
self
-
end
-
-
1
def check_required
-
9
missing = self.class.missing_required_fields_on(self)
-
9
unless missing.empty?
-
5
raise "Required field#{missing.length > 1 ? "s" : ""} #{missing.map{|field| field.to_s.inspect}.join(", ")} unset on #{self.inspect}"
-
end
-
7
self
-
end
-
-
1
def unset?(value)
-
value == RequiredField
-
end
-
-
1
def setting(name, default_value = nil)
-
3
self.class.setting(name, default_value)
-
3
instance_variable_set("@#{name}", default_value)
-
end
-
-
1
def settings(hash)
-
2
hash.each_pair do |name, value|
-
3
setting(name, value)
-
end
-
2
return self
-
end
-
-
1
def required_fields(*names)
-
1
self.class.required_fields(*names)
-
1
self
-
end
-
1
alias required_field required_fields
-
-
1
def nil_fields(*names)
-
self.class.nil_fields(*names)
-
self
-
end
-
1
alias nil_field nil_fields
-
-
1
class Struct
-
1
include Configurable
-
end
-
end
-
end
-
1
require 'mattock/command-task'
-
1
module Mattock
-
1
class RemoteCommandTask < CommandTask
-
1
setting(:remote_server, nested(
-
:address => nil,
-
:user => nil
-
))
-
1
setting(:ssh_options, [])
-
1
nil_fields(:id_file, :free_arguments)
-
-
1
def decorated(command_on_remote)
-
2
fail "Need remote server for #{self.class.name}" unless remote_server.address
-
-
2
raise "Empty remote command" if command_on_remote.nil?
-
2
Mattock::WrappingChain.new do |cmd|
-
2
cmd.add Mattock::CommandLine.new("ssh") do |cmd|
-
2
cmd.options << "-u #{remote_server.user}" if remote_server.user
-
2
cmd.options << "-i #{id_file}" if id_file
-
2
unless ssh_options.empty?
-
ssh_options.each do |opt|
-
cmd.options "-o #{opt}"
-
end
-
end
-
2
cmd.options << remote_server.address
-
end
-
2
cmd.add Mattock::ShellEscaped.new(command_on_remote)
-
end
-
end
-
end
-
end
-
1
require 'mattock/cascading-definition'
-
1
require 'rake/task'
-
1
require 'rake/file_task'
-
-
1
module Mattock
-
# A configurable subclass of Rake::Task, such that you can use a
-
# configuration block to change how a common task behaves, while still
-
# overriding Rake API methods like Task#needed? and Task#timestamp
-
-
1
module TaskMixin
-
1
include CascadingDefinition
-
-
1
setting :task_name
-
1
setting :task_args
-
-
1
module ClassMethods
-
1
def default_taskname(name)
-
setting(:task_name, name)
-
end
-
end
-
-
1
def self.included(mod)
-
4
mod.class_eval{ extend ClassMethods }
-
2
super
-
end
-
-
1
def initialize(*args)
-
1
configs = args.take_while{|arg| Configurable === arg}
-
1
@extracted_task_args = args[configs.length..-1]
-
1
if @extracted_task_args.any?{|arg| Configurable === arg}
-
raise "Mattock::Task classes should be created with parent configs, then Rake task args"
-
end
-
1
super(*configs)
-
end
-
-
1
def resolve_configuration
-
1
if @extracted_task_args.empty?
-
1
self.task_args = [task_name]
-
else
-
self.task_args = @extracted_task_args
-
end
-
end
-
-
1
def action
-
end
-
-
# I continue to look for an alternative here.
-
1
def task_class
-
1
return @task_class if @task_class
-
1
@task_class = Class.new(self.class) do
-
1
define_method :initialize, Rake::Task.instance_method(:initialize)
-
end
-
end
-
-
1
def inspect
-
"#{self.class.name}: #{self.task_args.inspect}"
-
end
-
-
1
def define
-
1
task = task_class.define_task(*task_args) do
-
1
action
-
end
-
1
copy_settings_to(task)
-
end
-
end
-
-
1
class Task < Rake::Task
-
1
include TaskMixin
-
end
-
-
1
class FileTask < Rake::FileTask
-
1
include TaskMixin
-
end
-
end
-
1
require 'rake/tasklib'
-
1
require 'mattock/cascading-definition'
-
-
1
module Mattock
-
1
class TaskLib < Rake::TaskLib
-
1
include CascadingDefinition
-
-
1
attr_writer :namespace_name
-
1
def self.default_namespace(name)
-
1
setting(:namespace_name, name)
-
end
-
-
1
attr_reader :tasks
-
-
1
def initialize(*toolkits)
-
5
@tasks = []
-
5
super
-
end
-
-
1
def task(*args)
-
10
a_task = super
-
10
@tasks << a_task
-
10
return a_task
-
end
-
-
1
def in_namespace(*tasknames)
-
10
if tasknames.empty?
-
5
if block_given?
-
5
if @namespace_name.nil?
-
yield
-
else
-
5
namespace @namespace_name do
-
5
yield
-
end
-
end
-
end
-
else
-
5
tasknames.map do |taskname|
-
5
[@namespace_name, taskname].compact.join(":")
-
end
-
end
-
end
-
-
1
def root_task
-
5
@namespace_name || :default
-
end
-
-
1
def default_namespace
-
nil
-
end
-
-
1
def [](taskname)
-
5
in_namespace(taskname).first
-
end
-
end
-
1
Tasklib = TaskLib #Because I can never remember
-
end
-
1
require 'tilt'
-
1
require 'valise'
-
-
1
module Mattock
-
1
module ValiseManager
-
1
def default_valise(*dirs)
-
1
Valise::Set.define do
-
1
dirs.each do |dir|
-
1
ro dir
-
end
-
end
-
end
-
-
1
def rel_dir(base_path = nil, up_to = nil)
-
base_path ||= /(.*):\d+/.match(caller[0])[1]
-
up_to ||= "lib"
-
-
abs_path = File::expand_path(base_path)
-
base_path = abs_path.split(File::Separator)
-
until base_path.empty?
-
unless base_path.last == up_to
-
base_path.pop
-
else
-
break
-
end
-
end
-
-
if base_path.empty?
-
raise "Relative root #{up_to} not found in #{abs_path}"
-
end
-
-
return base_path
-
end
-
1
module_function :rel_dir, :default_valise
-
end
-
-
1
module TemplateHost
-
1
def self.template_cache
-
1
@template_cache ||= Tilt::Cache.new
-
end
-
-
1
attr_accessor :valise
-
-
1
def render(path, locals=nil)
-
1
template = TemplateHost::template_cache.fetch(path) do
-
1
Tilt.new(path) do |tmpl|
-
1
valise.find(["templates"] + valise.unpath(tmpl.file)).contents
-
end
-
end
-
-
1
template.render(self, locals)
-
end
-
end
-
end
-
1
require 'mattock/command-line'
-
-
1
module Mattock
-
1
class MockCommandResult < CommandRunResult
-
1
def self.create(*args)
-
2
if args.length == 1
-
2
args = [args[0], {1 => ""}]
-
end
-
-
2
if String == args[1]
-
args[1] = {1 => args[1]}
-
end
-
-
2
return self.new(*args)
-
end
-
-
1
def initialize(code, streams)
-
2
@streams = streams
-
2
@exit_code = code
-
end
-
-
1
attr_reader :exit_code, :streams
-
-
1
alias exit_status exit_code
-
end
-
-
1
module CommandLineExampleGroup
-
1
def self.included(group)
-
1
group.class_eval do
-
1
let :pairs do
-
1
[]
-
end
-
-
1
before :each do
-
1
Mattock::CommandLine.should_receive(:execute) do |cmd|
-
2
pattern, res = pairs.shift
-
2
pattern =~ cmd
-
2
Mattock::MockCommandResult.create(*res)
-
end.any_number_of_times
-
end
-
-
1
after :each do
-
1
pairs.should have_all_been_called
-
end
-
end
-
end
-
-
1
def expect_command(cmd, *result)
-
2
pairs << [cmd, result]
-
end
-
-
1
module Matchers
-
1
extend RSpec::Matchers::DSL
-
-
1
define :have_all_been_called do
-
1
match do |list|
-
1
list.empty?
-
end
-
-
1
failure_message_for_should do |list|
-
"Expected all commands to be run, but: #{list.map{|item| item[0].source.inspect}.join(", ")} #{list.length > 1 ? "were" : "was"} not."
-
end
-
end
-
end
-
1
include Matchers
-
end
-
end
-
1
require 'rake'
-
-
1
module Mattock
-
1
module RakeExampleGroup
-
1
SavedEnvironmentVariables = %w{APPDATA HOME HOMEDRIVE HOMEPATH RAKE_COLUMNS RAKE_SYSTEM RAKEOPT USERPROFILE}
-
1
DeletedEnvironmentVariables = %w{RAKE_COLUMNS RAKE_SYSTEM RAKEOPT}
-
1
include Rake::DSL
-
#include FileUtils
-
-
1
class TaskManager
-
1
include Rake::TaskManager
-
end
-
-
1
def self.included(mod)
-
2
mod.class_eval do
-
2
let! :rake do
-
6
Rake.application = Rake::Application.new
-
6
Rake::TaskManager.record_task_metadata = true
-
6
RakeFileUtils.verbose_flag = false
-
6
Rake.application
-
end
-
-
2
before :each do
-
6
ARGV.clear
-
-
6
@original_ENV = {}
-
6
SavedEnvironmentVariables.each do |var|
-
48
@original_ENV[var] = ENV[var]
-
end
-
6
DeletedEnvironmentVariables.each do |var|
-
18
ENV.delete(var)
-
end
-
-
end
-
-
2
after :each do
-
6
SavedEnvironmentVariables.each do |var|
-
48
ENV[var] = @original_ENV[var]
-
end
-
-
6
if @original_ENV['APPDATA'].nil?
-
6
ENV.delete 'APPDATA'
-
end
-
end
-
-
2
before :each do
-
6
@tempdir = File.join "/tmp", "test_mattock_#{$$}"
-
-
6
@original_PWD = Dir.pwd
-
6
FileUtils.mkdir_p @tempdir
-
6
Dir.chdir @tempdir
-
end
-
-
2
after :each do
-
6
Dir.chdir @original_PWD
-
6
FileUtils.rm_rf @tempdir
-
end
-
end
-
end
-
-
1
module Matchers
-
1
extend RSpec::Matchers::DSL
-
-
1
define :have_task do |name|
-
3
match do |rake|
-
3
!rake.lookup("rake:" + name.to_s).nil?
-
end
-
end
-
-
1
define :depend_on do |name|
-
2
match do |task|
-
2
task.prerequisites.include?(name)
-
end
-
end
-
end
-
-
1
include Matchers
-
end
-
end
-
1
require 'mattock/command-line'
-
-
1
module Mattock
-
1
class CommandLine
-
1
@@commands = []
-
1
class << self
-
1
alias original_execute execute
-
-
1
def execute(command)
-
5
result = original_execute(command)
-
5
@@commands << [command, result]
-
5
return result
-
end
-
-
1
attr_accessor :command_recording_path
-
-
1
def command_recording_path
-
2
@command_recording_path ||= ENV['MATTOCK_CMDREC']
-
end
-
-
1
def emit_recording
-
1
io = $stderr
-
1
if command_recording_path
-
1
io = File.open(command_recording_path, "w")
-
else
-
io.puts "Set MATTOCK_CMDREC to write to a path"
-
end
-
1
@@commands.each do |pair|
-
5
io.puts "[/#{pair[0]}/, #{[pair[1].exit_code, pair[1].streams].inspect}]"
-
end
-
end
-
end
-
end
-
end
-
-
1
at_exit do
-
1
Mattock::CommandLine.emit_recording
-
end