excavator 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Rakefile +10 -0
- data/bin/excavator +3 -0
- data/excavator.gemspec +25 -0
- data/lib/excavator.rb +80 -0
- data/lib/excavator/command.rb +79 -0
- data/lib/excavator/dsl.rb +33 -0
- data/lib/excavator/environment.rb +24 -0
- data/lib/excavator/namespace.rb +56 -0
- data/lib/excavator/param.rb +25 -0
- data/lib/excavator/param_parser.rb +135 -0
- data/lib/excavator/runner.rb +107 -0
- data/lib/excavator/table_view.rb +90 -0
- data/lib/excavator/version.rb +3 -0
- data/test/fixtures/commands/test.rb +18 -0
- data/test/test_helper.rb +32 -0
- data/test/unit/command_test.rb +75 -0
- data/test/unit/dsl_test.rb +49 -0
- data/test/unit/environment_test.rb +64 -0
- data/test/unit/namespace_test.rb +61 -0
- data/test/unit/param_parser_test.rb +128 -0
- data/test/unit/runner_test.rb +107 -0
- data/test/unit/table_view_test.rb +62 -0
- metadata +118 -0
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'excavator'
|
2
|
+
module Excavator
|
3
|
+
class Runner
|
4
|
+
|
5
|
+
# Runner specific vars
|
6
|
+
attr_accessor :command_paths
|
7
|
+
attr_accessor :commands
|
8
|
+
attr_reader :current_namespace
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@namespace = Namespace.new(:default)
|
12
|
+
@current_namespace = @namespace
|
13
|
+
end
|
14
|
+
|
15
|
+
def cwd
|
16
|
+
Excavator.cwd
|
17
|
+
end
|
18
|
+
|
19
|
+
def namespaces
|
20
|
+
@namespaces
|
21
|
+
end
|
22
|
+
|
23
|
+
def namespace
|
24
|
+
@namespace
|
25
|
+
end
|
26
|
+
|
27
|
+
def current_namespace
|
28
|
+
@current_namespace
|
29
|
+
end
|
30
|
+
|
31
|
+
def in_namespace(name)
|
32
|
+
ns = @current_namespace.namespace(name)
|
33
|
+
ns = @current_namespace << Excavator.namespace_class.new(name) unless ns
|
34
|
+
@current_namespace = ns
|
35
|
+
yield
|
36
|
+
@current_namespace = ns.parent
|
37
|
+
end
|
38
|
+
|
39
|
+
def clear_commands!
|
40
|
+
self.commands = {}
|
41
|
+
end
|
42
|
+
|
43
|
+
def run(*args)
|
44
|
+
args.flatten!
|
45
|
+
|
46
|
+
name = args.delete_at(0)
|
47
|
+
load_commands
|
48
|
+
|
49
|
+
if (name.nil? && args.size == 0) || display_help?(name)
|
50
|
+
display_help
|
51
|
+
return
|
52
|
+
end
|
53
|
+
|
54
|
+
command = find_command name
|
55
|
+
command.execute *args
|
56
|
+
end
|
57
|
+
|
58
|
+
def find_command(cmd)
|
59
|
+
*namespaces, command_name = cmd.to_s.split(':').collect {|c| c.to_sym }
|
60
|
+
cur_namespace = current_namespace
|
61
|
+
namespaces.each do |n|
|
62
|
+
cur_namespace = cur_namespace.namespace(n)
|
63
|
+
end
|
64
|
+
|
65
|
+
cur_namespace.command(command_name)
|
66
|
+
end
|
67
|
+
|
68
|
+
def display_help?(command_name)
|
69
|
+
["-h", "-?", "--help", "help"].include?(command_name)
|
70
|
+
end
|
71
|
+
|
72
|
+
def display_help
|
73
|
+
table_view = Excavator::TableView.new do |t|
|
74
|
+
t.title "#{File.basename($0)} commands:\n"
|
75
|
+
t.header "Command"
|
76
|
+
t.header "Description"
|
77
|
+
t.divider "\t"
|
78
|
+
|
79
|
+
namespace.commands_and_descriptions.sort.each do |command|
|
80
|
+
t.record *command
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
puts table_view
|
85
|
+
end
|
86
|
+
|
87
|
+
def load_commands
|
88
|
+
command_paths.each do |path|
|
89
|
+
Dir["#{path.to_s}/**/*.rb"].each { |file| load file }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def last_command
|
94
|
+
@last_command ||= Excavator.command_class.new(
|
95
|
+
self, :namespace => current_namespace
|
96
|
+
)
|
97
|
+
end
|
98
|
+
|
99
|
+
def clear_last_command!
|
100
|
+
@last_command = nil
|
101
|
+
end
|
102
|
+
|
103
|
+
def command_paths
|
104
|
+
@command_paths = Excavator.command_paths || [cwd.join("commands")]
|
105
|
+
end
|
106
|
+
end # Runner
|
107
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'excavator'
|
2
|
+
module Excavator
|
3
|
+
class TableView
|
4
|
+
class InvalidDataForHeaders < StandardError; end
|
5
|
+
|
6
|
+
DEFAULT_DIVIDER = " | "
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
@options = options
|
10
|
+
@_title = ""
|
11
|
+
@_headers = []
|
12
|
+
@_records = []
|
13
|
+
@col_widths = Hash.new { |hash, key| hash[key] = 0 }
|
14
|
+
@divider = options[:divider] || DEFAULT_DIVIDER
|
15
|
+
|
16
|
+
yield self if block_given?
|
17
|
+
end
|
18
|
+
|
19
|
+
def title(t = nil)
|
20
|
+
if t.nil?
|
21
|
+
@_title = t
|
22
|
+
else
|
23
|
+
@_title = t
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def header(*headers)
|
28
|
+
headers.flatten!
|
29
|
+
|
30
|
+
headers.each do |h|
|
31
|
+
index = @_headers.size
|
32
|
+
@_headers << h
|
33
|
+
@col_widths[h] = h.to_s.size
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def divider(str)
|
38
|
+
@divider = str
|
39
|
+
end
|
40
|
+
|
41
|
+
def record(*args)
|
42
|
+
args.flatten!
|
43
|
+
if args.size != @_headers.size
|
44
|
+
raise InvalidDataForHeaders.new(<<-MSG)
|
45
|
+
Number of columns for record (#{args.size}) does not match number
|
46
|
+
of headers (#{@_headers.size})
|
47
|
+
MSG
|
48
|
+
end
|
49
|
+
update_column_widths Hash[*@_headers.zip(args).flatten]
|
50
|
+
@_records << args
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_s
|
54
|
+
out = ""
|
55
|
+
out << @_title << "\n" if !@_title.nil? && @_title != ""
|
56
|
+
|
57
|
+
print_headers out
|
58
|
+
|
59
|
+
@_records.each_with_index do |record|
|
60
|
+
record.each_with_index do |val, i|
|
61
|
+
out << column_formats[i] % val
|
62
|
+
out << @divider if i != (record.size - 1)
|
63
|
+
end
|
64
|
+
out << "\n"
|
65
|
+
end
|
66
|
+
out << "\n"
|
67
|
+
|
68
|
+
out
|
69
|
+
end
|
70
|
+
|
71
|
+
def update_column_widths(hash)
|
72
|
+
hash.each do |key, val|
|
73
|
+
@col_widths[key] = val.size if val && val.size > @col_widths[key]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def print_headers(out)
|
78
|
+
@_headers.each_with_index do |h, i|
|
79
|
+
out << column_formats[i] % h
|
80
|
+
out << @divider if i != (@_headers.size - 1)
|
81
|
+
end
|
82
|
+
out << "\n"
|
83
|
+
end
|
84
|
+
|
85
|
+
# Internal
|
86
|
+
def column_formats
|
87
|
+
@col_widths.collect { |h, width| "%-#{width}s" }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
command :test do
|
2
|
+
puts "test command"
|
3
|
+
end
|
4
|
+
|
5
|
+
desc "A command with an argument"
|
6
|
+
param :region, :default => "west"
|
7
|
+
command :command_with_arg do
|
8
|
+
puts params[:region]
|
9
|
+
end
|
10
|
+
|
11
|
+
namespace :first do
|
12
|
+
namespace :second do
|
13
|
+
command :third do
|
14
|
+
puts "third"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
basedir = Pathname.new(File.dirname(__FILE__)).join("..").expand_path
|
3
|
+
$LOAD_PATH.unshift(basedir.join("lib").to_s)
|
4
|
+
require 'excavator'
|
5
|
+
|
6
|
+
gem 'minitest'
|
7
|
+
require 'minitest/spec'
|
8
|
+
require 'minitest/autorun'
|
9
|
+
require 'minitest/mock'
|
10
|
+
|
11
|
+
require 'ruby-debug'
|
12
|
+
Debugger.start
|
13
|
+
|
14
|
+
module TestHelpers
|
15
|
+
def basedir
|
16
|
+
Pathname.new(__FILE__).join("..", "..").expand_path
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# I like #context more than #describe
|
21
|
+
alias :context :describe
|
22
|
+
|
23
|
+
class MiniTest::Spec
|
24
|
+
include TestHelpers
|
25
|
+
class << self
|
26
|
+
# Oh Rails, you have a strangle hold on my conventions ...
|
27
|
+
alias :test :it
|
28
|
+
alias :setup :before
|
29
|
+
alias :teardown :after
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
context "Command" do
|
3
|
+
include Excavator
|
4
|
+
|
5
|
+
setup do
|
6
|
+
Excavator.reset!
|
7
|
+
@runner = Excavator.runner
|
8
|
+
end
|
9
|
+
|
10
|
+
test "execute a command" do
|
11
|
+
cmd = Command.new(@runner, :name => "test", :block => proc { puts "test"})
|
12
|
+
out, error = capture_io do
|
13
|
+
cmd.execute
|
14
|
+
end
|
15
|
+
assert_match /test/, out
|
16
|
+
end
|
17
|
+
|
18
|
+
test "command arguments available in #params" do
|
19
|
+
cmd = Command.new(
|
20
|
+
@runner,
|
21
|
+
:name => "test",
|
22
|
+
:param_definitions => [Param.new(:name)],
|
23
|
+
:block => proc { puts params[:name] }
|
24
|
+
)
|
25
|
+
out, error = capture_io do
|
26
|
+
cmd.execute("--name", "hello, world!")
|
27
|
+
end
|
28
|
+
assert_match /hello, world!/, out
|
29
|
+
end
|
30
|
+
|
31
|
+
test "passing in two named parameters" do
|
32
|
+
cmd = Command.new(
|
33
|
+
@runner,
|
34
|
+
:name => "test",
|
35
|
+
:param_definitions => [Param.new(:arg1), Param.new(:arg2)],
|
36
|
+
:block => proc { puts "#{params[:arg1]}#{params[:arg2]}" }
|
37
|
+
)
|
38
|
+
out, error = capture_io do
|
39
|
+
cmd.execute("--arg1", "1", "--arg2", "2")
|
40
|
+
end
|
41
|
+
assert_match /12/, out
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
test "#execute_with_params runs a command's block" do
|
46
|
+
cmd = Command.new(Excavator.runner).tap do |c|
|
47
|
+
c.name = "test"
|
48
|
+
c.add_param(Param.new(:name))
|
49
|
+
c.add_param(Param.new(:server))
|
50
|
+
c.block = Proc.new do
|
51
|
+
[params[:name], params[:server]]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
results = cmd.execute_with_params :name => "paydro", :server => "web"
|
56
|
+
assert_equal ["paydro", "web"], results
|
57
|
+
end
|
58
|
+
|
59
|
+
test "#execute_with_params throws error when missing param" do
|
60
|
+
cmd = Command.new(Excavator.runner).tap do |c|
|
61
|
+
c.name = "test"
|
62
|
+
c.add_param(Param.new(:name))
|
63
|
+
c.add_param(Param.new(:server))
|
64
|
+
c.block = Proc.new do
|
65
|
+
[params[:name], params[:server]]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
error = assert_raises MissingParamsError do
|
70
|
+
cmd.execute_with_params :name => "paydro"
|
71
|
+
end
|
72
|
+
assert error.params.include?(:server)
|
73
|
+
end
|
74
|
+
end # Command
|
75
|
+
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
context "DSL command creation and execution" do
|
4
|
+
include Excavator::DSL
|
5
|
+
|
6
|
+
setup do
|
7
|
+
Excavator.reset!
|
8
|
+
@runner = Excavator.runner
|
9
|
+
end
|
10
|
+
|
11
|
+
test "create a command" do
|
12
|
+
refute @runner.find_command("test")
|
13
|
+
cmd = command(:test) { puts "test" }
|
14
|
+
assert cmd = @runner.find_command("test")
|
15
|
+
end
|
16
|
+
|
17
|
+
test "creating command clears out #last_command for a new command" do
|
18
|
+
cmd1 = command(:first) {}
|
19
|
+
refute_equal cmd1.object_id, @runner.last_command.object_id
|
20
|
+
end
|
21
|
+
|
22
|
+
test "adding a description to a command" do
|
23
|
+
desc "test desc"
|
24
|
+
command(:test) {}
|
25
|
+
assert_equal "test desc", @runner.find_command("test").desc
|
26
|
+
end
|
27
|
+
|
28
|
+
test "create a namespaced command" do
|
29
|
+
cmd = nil
|
30
|
+
namespace :outer do
|
31
|
+
cmd = command(:test) { }
|
32
|
+
end
|
33
|
+
|
34
|
+
assert found_cmd = @runner.find_command("outer:test")
|
35
|
+
assert_equal cmd.object_id, found_cmd.object_id
|
36
|
+
end
|
37
|
+
|
38
|
+
test "command has access to it's namespace" do
|
39
|
+
cmd = nil
|
40
|
+
b = nil
|
41
|
+
namespace :a do
|
42
|
+
namespace :b do
|
43
|
+
cmd = command(:inside) {}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
assert_equal :inside, cmd.name
|
47
|
+
assert_equal :b, cmd.namespace.name
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
context "Environment" do
|
4
|
+
include Excavator
|
5
|
+
include Excavator::DSL
|
6
|
+
|
7
|
+
setup do
|
8
|
+
Excavator.reset!
|
9
|
+
Excavator.command_paths = [
|
10
|
+
basedir.join("test", "fixtures", "commands")
|
11
|
+
]
|
12
|
+
end
|
13
|
+
|
14
|
+
test "execute another command" do
|
15
|
+
cmd = command(:first) { execute :second }
|
16
|
+
command(:second) { "called from second" }
|
17
|
+
assert_equal "called from second", cmd.execute
|
18
|
+
end
|
19
|
+
|
20
|
+
test "execute command with params" do
|
21
|
+
cmd = command(:run_another_cmd) do
|
22
|
+
execute :my_name, :name => 'paydro'
|
23
|
+
end
|
24
|
+
|
25
|
+
param :name
|
26
|
+
command(:my_name) { params[:name] }
|
27
|
+
|
28
|
+
assert_equal "paydro", cmd.execute
|
29
|
+
end
|
30
|
+
|
31
|
+
end # Environment
|
32
|
+
|
33
|
+
context "Environment#modify" do
|
34
|
+
include Excavator
|
35
|
+
|
36
|
+
module OtherCommands
|
37
|
+
def hello
|
38
|
+
"world"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
setup do
|
43
|
+
env_class = Class.new(Excavator::Environment)
|
44
|
+
Excavator.environment_class = env_class
|
45
|
+
@class = env_class
|
46
|
+
end
|
47
|
+
|
48
|
+
teardown do
|
49
|
+
Excavator.environment_class = nil
|
50
|
+
end
|
51
|
+
|
52
|
+
test "modify environment with modules" do
|
53
|
+
@class.modify OtherCommands
|
54
|
+
assert_equal "world", @class.new.hello
|
55
|
+
end
|
56
|
+
|
57
|
+
test "modify environment with a block" do
|
58
|
+
@class.modify do
|
59
|
+
include OtherCommands
|
60
|
+
end
|
61
|
+
|
62
|
+
assert_equal "world", @class.new.hello
|
63
|
+
end
|
64
|
+
end
|