excavator 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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|