excavator 0.0.1 → 0.0.2
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/LICENSE +7 -0
- data/README.md +280 -0
- data/lib/excavator.rb +71 -19
- data/lib/excavator/command.rb +59 -9
- data/lib/excavator/dsl.rb +97 -3
- data/lib/excavator/environment.rb +48 -1
- data/lib/excavator/namespace.rb +130 -1
- data/lib/excavator/param.rb +21 -1
- data/lib/excavator/param_parser.rb +88 -10
- data/lib/excavator/runner.rb +87 -33
- data/lib/excavator/table_view.rb +2 -0
- data/lib/excavator/version.rb +3 -1
- data/test/test_helper.rb +2 -0
- data/test/unit/command_test.rb +21 -0
- data/test/unit/namespace_test.rb +13 -6
- data/test/unit/param_parser_test.rb +1 -1
- metadata +11 -11
- data/bin/excavator +0 -3
data/lib/excavator/dsl.rb
CHANGED
@@ -1,21 +1,117 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
1
3
|
module Excavator
|
2
|
-
|
4
|
+
|
5
|
+
# Public: DSL is the primary interface for creating commands. When required,
|
6
|
+
# the global scope is extended with helpers to create namespaces, parameters,
|
7
|
+
# and commands.
|
8
|
+
#
|
9
|
+
# Examples
|
10
|
+
#
|
11
|
+
# namespace :servers do
|
12
|
+
# desc "Create a new server"
|
13
|
+
# param :name, :desc => "Name of the server"
|
14
|
+
# param :region, :desc => "Region the server is created in"
|
15
|
+
# command :create do
|
16
|
+
# # ...
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
#
|
3
20
|
module DSL
|
21
|
+
|
22
|
+
# Public: Create a namespace to group commands. Namespaces can be nested.
|
23
|
+
# Namespace names are prefixed to the command name.
|
24
|
+
#
|
25
|
+
# This will create a new namespace if it doesn't exist. All commands defined
|
26
|
+
# within the passed in block are added to the namespace.
|
27
|
+
#
|
28
|
+
# name - A String or Symbol. This is used as a part of the command name.
|
29
|
+
# block -
|
30
|
+
#
|
31
|
+
# Examples
|
32
|
+
#
|
33
|
+
# # Creates the command "servers:create"
|
34
|
+
# namespace :servers do
|
35
|
+
# command :create do
|
36
|
+
# # ...
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# # Nesting namespaces
|
41
|
+
# namespace :public do
|
42
|
+
# namespace :instances do
|
43
|
+
# # ...
|
44
|
+
# end
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# Returns nothing.
|
4
48
|
def namespace(name)
|
5
49
|
Excavator.runner.in_namespace(name) do
|
6
50
|
yield
|
7
51
|
end
|
8
52
|
end
|
9
53
|
|
54
|
+
# Public: Adds a description to the next command declared.
|
55
|
+
#
|
56
|
+
# description - A String describing the command.
|
57
|
+
#
|
58
|
+
# Examples
|
59
|
+
#
|
60
|
+
# desc "prints hello"
|
61
|
+
# command :print_hello { ... }
|
62
|
+
#
|
63
|
+
# Returns nothing.
|
10
64
|
def desc(description)
|
11
65
|
Excavator.runner.last_command.desc = description
|
12
66
|
end
|
13
67
|
|
68
|
+
# Public: Add a parameter to the next command declared. This passes all
|
69
|
+
# parameters directly into Param#initialize.
|
70
|
+
#
|
71
|
+
# See the Param and ParamParser class for more details.
|
72
|
+
#
|
73
|
+
# name - A String or Symbol of the parameter name.
|
74
|
+
# options - A Hash of Parameter options (default: {}).
|
75
|
+
# :desc - A String describing the parameter.
|
76
|
+
# :default - A default value for the parameter.
|
77
|
+
# :short - A String (normally one character) to use as the
|
78
|
+
# short switch for the parameter. For instance,
|
79
|
+
# "param :test, :short => 'c'" will allow
|
80
|
+
# the parameter to be used as "-c" on the command
|
81
|
+
# line.
|
82
|
+
# :optional - A Boolean specifying whether the paramter is
|
83
|
+
# optional.
|
84
|
+
#
|
85
|
+
# Examples
|
86
|
+
#
|
87
|
+
# # A required parameter with the long switch "--name"
|
88
|
+
# param :name
|
89
|
+
#
|
90
|
+
# # An optional parameter.
|
91
|
+
# param :name, :optional => true
|
92
|
+
#
|
93
|
+
# # Set a description.
|
94
|
+
# param :name, :desc => "A name"
|
95
|
+
#
|
96
|
+
# Returns nothing.
|
14
97
|
def param(name, options = {})
|
15
98
|
param = Excavator.param_class.new(name, options)
|
16
99
|
Excavator.runner.last_command.add_param(param)
|
17
100
|
end
|
18
101
|
|
102
|
+
# Public: Creates a command to add to Excavator.runner.
|
103
|
+
#
|
104
|
+
# name - A String or Symbole for the command.
|
105
|
+
# block - A block of code to execute when the command is called. This block
|
106
|
+
# is executed within an instance of Excavator::Environment.
|
107
|
+
#
|
108
|
+
# Examples
|
109
|
+
#
|
110
|
+
# command :hello do
|
111
|
+
# puts "hello"
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
# Returns a Command.
|
19
115
|
def command(name, &block)
|
20
116
|
cmd = Excavator.runner.last_command
|
21
117
|
cmd.name = name
|
@@ -29,5 +125,3 @@ module Excavator
|
|
29
125
|
end # Excavator
|
30
126
|
|
31
127
|
self.extend Excavator::DSL
|
32
|
-
|
33
|
-
|
@@ -1,6 +1,33 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
1
3
|
module Excavator
|
4
|
+
|
5
|
+
# Public: Environment instances are created for a Command's block to execute
|
6
|
+
# in. It is designed for modification and manipulation. Adding other methods
|
7
|
+
# into this class will allow all Commands to reference them.
|
8
|
+
#
|
9
|
+
# Examples
|
10
|
+
#
|
11
|
+
# module Helpers
|
12
|
+
# def pretty_logger(msg)
|
13
|
+
# puts "*~*~*~*"
|
14
|
+
# puts msg
|
15
|
+
# puts "*~*~*~*"
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# # Add Helpers to Environment
|
20
|
+
# Excavator::Environment.modify Helpers
|
21
|
+
#
|
22
|
+
# param :msg
|
23
|
+
# command :print do
|
24
|
+
# # #pretty_logger is now available
|
25
|
+
# pretty_logger params[:msg]
|
26
|
+
# end
|
27
|
+
#
|
2
28
|
class Environment
|
3
29
|
|
30
|
+
# Public: A convenience method to include other modules in this class.
|
4
31
|
def self.modify(*mods, &block)
|
5
32
|
mods.each { |m| include m }
|
6
33
|
instance_eval &block if block
|
@@ -15,7 +42,27 @@ module Excavator
|
|
15
42
|
@unparsed_params = options[:unparsed_params]
|
16
43
|
end
|
17
44
|
|
18
|
-
# Execute another command
|
45
|
+
# Public: Execute another command. This is a convenience method to execute
|
46
|
+
# other commands defined in Excavator. #execute will return the value
|
47
|
+
# of the executed command (useful for building reusable blocks of code).
|
48
|
+
#
|
49
|
+
# command - A String full name of a command. This should include namespaces.
|
50
|
+
# params - A Hash of Param name to values to pass to this command.
|
51
|
+
#
|
52
|
+
# Examples
|
53
|
+
#
|
54
|
+
# namespace :test do
|
55
|
+
# command :prepare do
|
56
|
+
# puts params[:foo]
|
57
|
+
# # => "bar"
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# command :run do
|
61
|
+
# execute "test:prepare", {:foo => "bar"}
|
62
|
+
# end
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# Returns value from executed command.
|
19
66
|
def execute(command, params = {})
|
20
67
|
command = runner.find_command(command)
|
21
68
|
command.execute(params)
|
data/lib/excavator/namespace.rb
CHANGED
@@ -1,6 +1,32 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
1
3
|
module Excavator
|
4
|
+
|
5
|
+
# Internal: Namespace is a container for other Namespaces and Commands.
|
6
|
+
#
|
7
|
+
# Examples
|
8
|
+
#
|
9
|
+
# ns = Namespace.new
|
10
|
+
#
|
11
|
+
# # Add a command
|
12
|
+
# ns << Command.new(:log)
|
13
|
+
#
|
14
|
+
# # Add a namespace
|
15
|
+
# ns << Namespace.new(:database)
|
16
|
+
#
|
17
|
+
# # Lookup a command
|
18
|
+
# ns.command(:log)
|
19
|
+
#
|
20
|
+
# # Lookup a namespace
|
21
|
+
# ns.namespace(:namespace)
|
22
|
+
#
|
2
23
|
class Namespace
|
24
|
+
|
25
|
+
# Public: A String/Symbol name for the namespace. This is used to build
|
26
|
+
# a Command's full name used on the command line.
|
3
27
|
attr_reader :name
|
28
|
+
|
29
|
+
# Internal: A pointer to the parent Namespace (if any).
|
4
30
|
attr_accessor :parent
|
5
31
|
|
6
32
|
def initialize(name = nil, options = {})
|
@@ -9,6 +35,27 @@ module Excavator
|
|
9
35
|
@commands = {}
|
10
36
|
end
|
11
37
|
|
38
|
+
# Public: Add a Namespace or Command instance to this namespace.
|
39
|
+
#
|
40
|
+
# When adding namespaces, it will automatically set #parent attribute
|
41
|
+
# on the new object.
|
42
|
+
#
|
43
|
+
# obj - A Command or Namespace to add to the namespace.
|
44
|
+
#
|
45
|
+
# Examples
|
46
|
+
#
|
47
|
+
# ns = Namespace.new(:test)
|
48
|
+
#
|
49
|
+
# # Add a namespace. The added namespace will have #parent set to 'ns'.
|
50
|
+
# recent = Namespace.new(:recent)
|
51
|
+
# ns << recent
|
52
|
+
# recent.parent
|
53
|
+
# # => ns
|
54
|
+
#
|
55
|
+
# # Add a command.
|
56
|
+
# ns << Command.new(:name)
|
57
|
+
#
|
58
|
+
# Returns itself (Namespace).
|
12
59
|
def <<(obj)
|
13
60
|
if self.class === obj
|
14
61
|
obj.parent = self
|
@@ -16,16 +63,74 @@ module Excavator
|
|
16
63
|
else
|
17
64
|
@commands[obj.name] = obj
|
18
65
|
end
|
66
|
+
|
67
|
+
self
|
19
68
|
end
|
20
69
|
|
70
|
+
# Public: Look up a command within this namespace. It does not look into
|
71
|
+
# child namespaces.
|
72
|
+
#
|
73
|
+
# name - A String or Symbol name of the command to look up.
|
74
|
+
#
|
75
|
+
# Examples
|
76
|
+
#
|
77
|
+
# ns = Namespace.new(:test)
|
78
|
+
# ns << Command.new(:run)
|
79
|
+
# ns.command("run")
|
80
|
+
# # => <Command :run>
|
81
|
+
#
|
82
|
+
# Returns a Command if the command exists in the namespace.
|
83
|
+
# Returns nil if the command does not exist.
|
21
84
|
def command(name)
|
22
85
|
@commands[name.to_sym]
|
23
86
|
end
|
24
87
|
|
88
|
+
# Public: Look up a child namespace within this namespace.
|
89
|
+
#
|
90
|
+
# name - A String or Symbol name of the namespace to look up.
|
91
|
+
#
|
92
|
+
# Examples
|
93
|
+
#
|
94
|
+
# ns = Namespace.new(:test)
|
95
|
+
# ns << Namespace.new(:recent)
|
96
|
+
# ns.namespace(:recent)
|
97
|
+
# # => <Namespace :recent>
|
98
|
+
#
|
99
|
+
# Returns a Namespace if it exists in the namespace.
|
100
|
+
# Returns nil if the namespace does not exist.
|
25
101
|
def namespace(name)
|
26
102
|
@namespaces[name.to_sym]
|
27
103
|
end
|
28
104
|
|
105
|
+
# Public: The full name of the namespace. This returns a string of the
|
106
|
+
# namespace name and it's ancestor's name joined by ":". A namespace with
|
107
|
+
# no parent will return an emptry String.
|
108
|
+
#
|
109
|
+
# command_name - An optional String or Symbol name of a command to append
|
110
|
+
# to the end of the namespace's full name.
|
111
|
+
#
|
112
|
+
# Examples
|
113
|
+
#
|
114
|
+
# ns_a = Namespace.new(:a)
|
115
|
+
# ns_b = Namespace.new(:b)
|
116
|
+
# ns_c = Namespace.new(:c)
|
117
|
+
#
|
118
|
+
# ns_a << ns_b
|
119
|
+
# ns_b << ns_c
|
120
|
+
#
|
121
|
+
# ns_a.full_name
|
122
|
+
# # => ""
|
123
|
+
#
|
124
|
+
# ns_b.full_name
|
125
|
+
# # => "a:b"
|
126
|
+
#
|
127
|
+
# ns_c.full_name
|
128
|
+
# # => "a:b:c"
|
129
|
+
#
|
130
|
+
# ns_c.full_name("zebra")
|
131
|
+
# # => "a:b:c:zebra"
|
132
|
+
#
|
133
|
+
# Returns a String.
|
29
134
|
def full_name(command_name = nil)
|
30
135
|
return "#{command_name}" if parent.nil?
|
31
136
|
|
@@ -37,11 +142,35 @@ module Excavator
|
|
37
142
|
parts.compact.join(":")
|
38
143
|
end
|
39
144
|
|
145
|
+
# Public: An array of full command names and their description. This will
|
146
|
+
# include all commands in child namespaces as well.
|
147
|
+
#
|
148
|
+
# Examples
|
149
|
+
#
|
150
|
+
# # Setup
|
151
|
+
# ns_a = Namespace.new(:a)
|
152
|
+
# ns_b = Namespace.new(:b)
|
153
|
+
# ns_c = Namespace.new(:c)
|
154
|
+
#
|
155
|
+
# # A > B > C
|
156
|
+
# ns_a << ns_b
|
157
|
+
# ns_b << ns_c
|
158
|
+
#
|
159
|
+
# ns_b << Command.new(:foo, :desc => "Foo Desc")
|
160
|
+
# ns_c << Command.new(:bar, :desc => "Bar Desc")
|
161
|
+
#
|
162
|
+
# ns_a.commands_and_descriptions
|
163
|
+
# # => [
|
164
|
+
# # ["a:b:foo", "Foo Desc"],
|
165
|
+
# # ["a:b:c:bar", "Bar Desc"
|
166
|
+
# # ]
|
167
|
+
#
|
168
|
+
# Returns an Array of two item Arrays.
|
40
169
|
def commands_and_descriptions
|
41
170
|
items = []
|
42
171
|
@commands.each do |cmd_name, cmd|
|
43
172
|
items << [
|
44
|
-
full_name
|
173
|
+
cmd.full_name,
|
45
174
|
cmd.desc
|
46
175
|
]
|
47
176
|
end
|
data/lib/excavator/param.rb
CHANGED
@@ -1,9 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
1
3
|
module Excavator
|
4
|
+
|
5
|
+
# Internal: Param is an object to describe the attributes of a parameter.
|
6
|
+
# There's never a need to instantiate these directly - instead use DSL#param.
|
2
7
|
class Param
|
8
|
+
|
9
|
+
# Public: The String/Symbol name of the Param.
|
3
10
|
attr_reader :name
|
11
|
+
|
12
|
+
# Public: An optional default value for the Param.
|
4
13
|
attr_reader :default
|
14
|
+
|
15
|
+
# Public: An optional String description for the Param.
|
5
16
|
attr_reader :desc
|
6
|
-
|
17
|
+
|
18
|
+
# Public: An optional String (normally one character) to use as the short
|
19
|
+
# switch for the Param.
|
20
|
+
attr_accessor :short
|
7
21
|
|
8
22
|
def initialize(name, options = {})
|
9
23
|
@name = name
|
@@ -13,10 +27,16 @@ module Excavator
|
|
13
27
|
@short = options[:short]
|
14
28
|
end
|
15
29
|
|
30
|
+
# Public: Returns whether or not the Param is required.
|
31
|
+
#
|
32
|
+
# Returns a Boolean.
|
16
33
|
def required?
|
17
34
|
!optional?
|
18
35
|
end
|
19
36
|
|
37
|
+
# Public: Returns whether or not the Param is optional.
|
38
|
+
#
|
39
|
+
# Returns a Boolean.
|
20
40
|
def optional?
|
21
41
|
@default || (@default.nil? && @optional)
|
22
42
|
end
|
@@ -1,9 +1,36 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
1
3
|
require 'optparse'
|
2
4
|
module Excavator
|
5
|
+
|
6
|
+
# Public: ParamParser is a strategy object to integrate Command, Param, and
|
7
|
+
# OptionParser to parse commandline arguments.
|
8
|
+
#
|
9
|
+
# ParamParser is the heart of Excavator. It takes Params and a Command and
|
10
|
+
# creates a command line parser that:
|
11
|
+
#
|
12
|
+
# * automatically assigns short and long switches for params
|
13
|
+
# (e.g. :name => --name or -n)
|
14
|
+
# * parsers arguments into named parameters
|
15
|
+
# * assigns default values to parameters if specified
|
16
|
+
# * creates a help message
|
17
|
+
# * verifies required params are passed - throws an error if they are not
|
18
|
+
#
|
19
|
+
# Examples
|
20
|
+
#
|
21
|
+
# parser = Excavator::ParamParser.new
|
22
|
+
# parser.build(
|
23
|
+
# :name => "command_name",
|
24
|
+
# :desc => "command description",
|
25
|
+
# :params => [ <Param>, <Param>, ... ]
|
26
|
+
# )
|
27
|
+
#
|
28
|
+
# parser.parse!(ARGV)
|
29
|
+
# # => { :param1 => "val", :param2 => "val2", ... }
|
30
|
+
#
|
3
31
|
class ParamParser
|
4
32
|
|
5
33
|
attr_accessor :name
|
6
|
-
attr_accessor :banner
|
7
34
|
|
8
35
|
def initialize
|
9
36
|
@parser = OptionParser.new
|
@@ -11,10 +38,29 @@ module Excavator
|
|
11
38
|
@params = []
|
12
39
|
end
|
13
40
|
|
41
|
+
# Public: Builds the parser up with the name, description and Params
|
42
|
+
# passed in.
|
43
|
+
#
|
44
|
+
# This builds an OptionParser object with a "-h" and "--help" option.
|
45
|
+
#
|
46
|
+
# options - A Hash of parameters needed to build the parser.
|
47
|
+
# :name - A String/Symbol name of the Command.
|
48
|
+
# :desc - A String description of the command.
|
49
|
+
# :params - An Array of Params.
|
50
|
+
#
|
51
|
+
# Examples
|
52
|
+
#
|
53
|
+
# ParamParser.new.build({
|
54
|
+
# :name => "test_command",
|
55
|
+
# :desc => "description",
|
56
|
+
# :params => [<Param>, <Param>]
|
57
|
+
# })
|
58
|
+
#
|
59
|
+
# Returns nothing.
|
14
60
|
def build(options = {})
|
15
|
-
@name = options[:name] if options[:name]
|
16
|
-
@params = options[:params] if options[:params]
|
17
|
-
@desc = options[:desc] if options[:desc]
|
61
|
+
@name = options[:name] #if options[:name]
|
62
|
+
@params = options[:params] #if options[:params]
|
63
|
+
@desc = options[:desc] #if options[:desc]
|
18
64
|
|
19
65
|
required_params = []
|
20
66
|
optional_params = []
|
@@ -26,7 +72,7 @@ module Excavator
|
|
26
72
|
opts = []
|
27
73
|
|
28
74
|
# Long option
|
29
|
-
opts << "--#{param.name}"
|
75
|
+
opts << "--#{param.name.to_s.gsub("_", "-")}"
|
30
76
|
|
31
77
|
# params require an argument (for now)
|
32
78
|
opts << "=#{param.name.to_s.upcase}"
|
@@ -69,6 +115,36 @@ module Excavator
|
|
69
115
|
end
|
70
116
|
end
|
71
117
|
|
118
|
+
# Public: Parses command line arguments. Any argument matching Params are
|
119
|
+
# removed from the argument list (destructive!) and returned in a hash.
|
120
|
+
#
|
121
|
+
# args - An Array of command line arguments. This is usually ARGV.
|
122
|
+
# If the last argument in the Array is a Hash, it is used as the
|
123
|
+
# default parameters. This is a convience method to pass arguments
|
124
|
+
# in a Hash form rather than an Array like ARGV.
|
125
|
+
#
|
126
|
+
# Examples
|
127
|
+
#
|
128
|
+
# parser = ParamParser.new
|
129
|
+
# parser.build({...})
|
130
|
+
#
|
131
|
+
# # Standard usage - assuming there is :name param.
|
132
|
+
# args = ["command", "--name", "hello"]
|
133
|
+
# parser.parse!(args)
|
134
|
+
# # => {:name => "hello"},
|
135
|
+
# # => args is now ["command"]
|
136
|
+
#
|
137
|
+
# # Same as above, but the hash is checked to see whether or not
|
138
|
+
# # the required params are met.
|
139
|
+
# parser.parse!([{:name => "hello"}])
|
140
|
+
# # => {:name => "hello"}
|
141
|
+
#
|
142
|
+
# # Assume :name is required
|
143
|
+
# parser.parse!(["command"])
|
144
|
+
# # => Raises MissingParamsError
|
145
|
+
#
|
146
|
+
# Returns a Hash of Symbol names to values.
|
147
|
+
# Raises Excavator::MissingParamsError if args is missing required params.
|
72
148
|
def parse!(args)
|
73
149
|
@parsed_params = args.last.is_a?(Hash) ? args.pop : {}
|
74
150
|
|
@@ -79,6 +155,13 @@ module Excavator
|
|
79
155
|
@parsed_params
|
80
156
|
end
|
81
157
|
|
158
|
+
# Public: Print out a help message for this parser.
|
159
|
+
def usage
|
160
|
+
@parser.to_s
|
161
|
+
end
|
162
|
+
|
163
|
+
protected
|
164
|
+
|
82
165
|
def detect_missing_params!
|
83
166
|
missing_params = []
|
84
167
|
@params.each do |p|
|
@@ -90,11 +173,6 @@ module Excavator
|
|
90
173
|
raise MissingParamsError.new(missing_params) if missing_params.size > 0
|
91
174
|
end
|
92
175
|
|
93
|
-
def usage
|
94
|
-
@parser.to_s
|
95
|
-
end
|
96
|
-
|
97
|
-
protected
|
98
176
|
|
99
177
|
def set_default_params
|
100
178
|
@params.each do |param|
|