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.
@@ -1,21 +1,117 @@
1
+ # -*- encoding: utf-8 -*-
2
+
1
3
  module Excavator
2
- # Needs to depend on the runner
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)
@@ -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(cmd_name),
173
+ cmd.full_name,
45
174
  cmd.desc
46
175
  ]
47
176
  end
@@ -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
- attr_accessor :short # Set by the ParamParser
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|