clamp 0.0.7 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -26,7 +26,7 @@ Clamp models a command as a Ruby class; a subclass of `Clamp::Command`. They lo
26
26
  Integer(s)
27
27
  end
28
28
 
29
- argument "WORDS ...", "the thing to say"
29
+ parameter "WORDS ...", "the thing to say"
30
30
 
31
31
  def execute
32
32
 
@@ -42,7 +42,7 @@ Clamp models a command as a Ruby class; a subclass of `Clamp::Command`. They lo
42
42
 
43
43
  end
44
44
 
45
- Class-level methods (like `option` and `argument`) are available to declare command-line options, and document usage.
45
+ Class-level methods (like `option` and `parameter`) are available to declare command-line options, and document usage.
46
46
 
47
47
  The command can be invoked by instantiating the class, and asking it to run:
48
48
 
@@ -111,15 +111,15 @@ If the block raises an ArgumentError, Clamp will catch it, and report that the o
111
111
  !!!plain
112
112
  ERROR: option '--port': invalid value for Integer: "blah"
113
113
 
114
- Declaring arguments
115
- -------------------
114
+ Declaring parameters
115
+ --------------------
116
116
 
117
- The `argument` method is used to declare command arguments:
117
+ The `parameter` method is used to declare positional command parameters:
118
118
 
119
- argument "FILE ...", "source files"
120
- argument "DIR", "target directory"
119
+ parameter "FILE ...", "source files"
120
+ parameter "DIR", "target directory"
121
121
 
122
- Use of `argument` is entirely for documentation purposes. Whether or not you declare and describe your expected arguments, the actual arguments that remain after option parsing will be available as `arguments` when your `#execute` method is called.
122
+ Use of `parameter` is entirely for documentation purposes. Whether or not you declare and describe your expected arguments, the actual arguments that remain after option parsing will be available as `arguments` when your `#execute` method is called.
123
123
 
124
124
  Sub-commands
125
125
  ------------
@@ -161,7 +161,7 @@ When a command has sub-commands, Clamp will attempt to delegate based on the fir
161
161
  Getting help
162
162
  ------------
163
163
 
164
- All Clamp commands support a "`--help`" option, which outputs brief usage documentation, based on those seemingly useless extra parameters that you had to pass to `option` and `argument`.
164
+ All Clamp commands support a "`--help`" option, which outputs brief usage documentation, based on those seemingly useless extra parameters that you had to pass to `option` and `parameter`.
165
165
 
166
166
  $ speak --help
167
167
  Usage:
data/examples/rename CHANGED
@@ -6,8 +6,8 @@ class Rename < Clamp::Command
6
6
 
7
7
  usage "[OPTIONS] TRANSFORM FILE ..."
8
8
 
9
- argument "TRANSFORM", "a Ruby expression"
10
- argument "FILE", "a file to rename"
9
+ parameter "TRANSFORM", "a Ruby expression"
10
+ parameter "FILE", "a file to rename"
11
11
 
12
12
  option ["-v", "--verbose"], :flag, "be verbose"
13
13
 
data/examples/speak CHANGED
@@ -9,7 +9,7 @@ class SpeakCommand < Clamp::Command
9
9
  Integer(s)
10
10
  end
11
11
 
12
- argument "WORDS ...", "the thing to say"
12
+ parameter "WORDS ...", "the thing to say"
13
13
 
14
14
  def execute
15
15
 
@@ -0,0 +1,38 @@
1
+ module Clamp
2
+
3
+ module AttributeDeclaration
4
+
5
+ protected
6
+
7
+ def define_accessors_for(attribute, &block)
8
+ define_reader_for(attribute)
9
+ define_writer_for(attribute, &block)
10
+ end
11
+
12
+ def define_reader_for(attribute)
13
+ reader_name = attribute.attribute_name
14
+ reader_name += "?" if attribute.respond_to?(:flag?) && attribute.flag?
15
+ ivar_name = "@#{attribute.attribute_name}"
16
+ define_method(reader_name) do
17
+ if instance_variable_defined?(ivar_name)
18
+ instance_variable_get(ivar_name)
19
+ elsif parent_command && parent_command.respond_to?(reader_name)
20
+ parent_command.send(reader_name)
21
+ elsif attribute.respond_to?(:default_value)
22
+ attribute.default_value
23
+ end
24
+ end
25
+ end
26
+
27
+ def define_writer_for(attribute, &block)
28
+ define_method("#{attribute.attribute_name}=") do |value|
29
+ if block
30
+ value = instance_exec(value, &block)
31
+ end
32
+ instance_variable_set("@#{attribute.attribute_name}", value)
33
+ end
34
+ end
35
+
36
+ end
37
+
38
+ end
data/lib/clamp/command.rb CHANGED
@@ -1,6 +1,9 @@
1
- require 'clamp/help_support'
2
- require 'clamp/option_support'
3
- require 'clamp/subcommand_support'
1
+ require 'clamp/command/declaration'
2
+ require 'clamp/errors'
3
+ require 'clamp/help'
4
+ require 'clamp/option/parsing'
5
+ require 'clamp/parameter/parsing'
6
+ require 'clamp/subcommand/execution'
4
7
 
5
8
  module Clamp
6
9
 
@@ -18,28 +21,9 @@ module Clamp
18
21
  attr_accessor :parent_command
19
22
 
20
23
  def parse(arguments)
21
- while arguments.first =~ /^-/
22
- case (switch = arguments.shift)
23
-
24
- when /\A--\z/
25
- break
26
-
27
- else
28
- option = find_option(switch)
29
- value = if option.flag?
30
- option.flag_value(switch)
31
- else
32
- arguments.shift
33
- end
34
- begin
35
- send("#{option.attribute_name}=", value)
36
- rescue ArgumentError => e
37
- signal_usage_error "option '#{switch}': #{e.message}"
38
- end
39
-
40
- end
41
- end
42
- @arguments = arguments
24
+ @arguments = arguments.dup
25
+ parse_options
26
+ parse_parameters
43
27
  end
44
28
 
45
29
  # default implementation
@@ -60,32 +44,12 @@ module Clamp
60
44
  self.class.help(name)
61
45
  end
62
46
 
47
+ include Option::Parsing
48
+ include Parameter::Parsing
49
+ include Subcommand::Execution
50
+
63
51
  private
64
52
 
65
- def execute_subcommand
66
- signal_usage_error "no subcommand specified" if arguments.empty?
67
- subcommand_name = arguments.shift
68
- subcommand_class = find_subcommand_class(subcommand_name)
69
- subcommand = subcommand_class.new("#{name} #{subcommand_name}", context)
70
- subcommand.parent_command = self
71
- subcommand.run(arguments)
72
- end
73
-
74
- def find_option(switch)
75
- self.class.find_option(switch) ||
76
- signal_usage_error("Unrecognised option '#{switch}'")
77
- end
78
-
79
- def find_subcommand(name)
80
- self.class.find_subcommand(name) ||
81
- signal_usage_error("No such sub-command '#{name}'")
82
- end
83
-
84
- def find_subcommand_class(name)
85
- subcommand = find_subcommand(name)
86
- subcommand.subcommand_class if subcommand
87
- end
88
-
89
53
  def signal_usage_error(message)
90
54
  e = UsageError.new(message, self)
91
55
  e.set_backtrace(caller)
@@ -98,9 +62,8 @@ module Clamp
98
62
 
99
63
  class << self
100
64
 
101
- include OptionSupport
102
- include SubcommandSupport
103
- include HelpSupport
65
+ include Command::Declaration
66
+ include Help
104
67
 
105
68
  def run(name = $0, args = ARGV, context = {})
106
69
  begin
@@ -119,27 +82,4 @@ module Clamp
119
82
 
120
83
  end
121
84
 
122
- class Error < StandardError
123
-
124
- def initialize(message, command)
125
- super(message)
126
- @command = command
127
- end
128
-
129
- attr_reader :command
130
-
131
- end
132
-
133
- # raise to signal incorrect command usage
134
- class UsageError < Error; end
135
-
136
- # raise to request usage help
137
- class HelpWanted < Error
138
-
139
- def initialize(command)
140
- super("I need help", command)
141
- end
142
-
143
- end
144
-
145
85
  end
@@ -0,0 +1,15 @@
1
+ require 'clamp/option/declaration'
2
+ require 'clamp/parameter/declaration'
3
+ require 'clamp/subcommand/declaration'
4
+
5
+ module Clamp
6
+ class Command
7
+
8
+ module Declaration
9
+ include Option::Declaration
10
+ include Parameter::Declaration
11
+ include Subcommand::Declaration
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,26 @@
1
+ module Clamp
2
+
3
+ class Error < StandardError
4
+
5
+ def initialize(message, command)
6
+ super(message)
7
+ @command = command
8
+ end
9
+
10
+ attr_reader :command
11
+
12
+ end
13
+
14
+ # raise to signal incorrect command usage
15
+ class UsageError < Error; end
16
+
17
+ # raise to request usage help
18
+ class HelpWanted < Error
19
+
20
+ def initialize(command)
21
+ super("I need help", command)
22
+ end
23
+
24
+ end
25
+
26
+ end
@@ -1,22 +1,6 @@
1
1
  module Clamp
2
2
 
3
- class Argument < Struct.new(:name, :description)
4
-
5
- def help
6
- [name, description]
7
- end
8
-
9
- end
10
-
11
- module HelpSupport
12
-
13
- def declared_arguments
14
- @declared_arguments ||= []
15
- end
16
-
17
- def argument(name, description)
18
- declared_arguments << Argument.new(name, description)
19
- end
3
+ module Help
20
4
 
21
5
  def usage(usage)
22
6
  @declared_usage_descriptions ||= []
@@ -26,7 +10,7 @@ module Clamp
26
10
  attr_reader :declared_usage_descriptions
27
11
 
28
12
  def derived_usage_description
29
- parts = declared_arguments.map { |a| a.name }
13
+ parts = parameters.map { |a| a.name }
30
14
  parts.unshift("SUBCOMMAND") if has_subcommands?
31
15
  parts.unshift("[OPTIONS]") if has_options?
32
16
  parts.join(" ")
@@ -43,10 +27,10 @@ module Clamp
43
27
  help.puts " #{command_name} #{usage}".rstrip
44
28
  end
45
29
  detail_format = " %-29s %s"
46
- unless declared_arguments.empty?
47
- help.puts "\nArguments:"
48
- declared_arguments.each do |argument|
49
- help.puts detail_format % argument.help
30
+ unless parameters.empty?
31
+ help.puts "\nParameters:"
32
+ parameters.each do |parameter|
33
+ help.puts detail_format % parameter.help
50
34
  end
51
35
  end
52
36
  unless recognised_subcommands.empty?
data/lib/clamp/option.rb CHANGED
@@ -2,9 +2,9 @@ module Clamp
2
2
 
3
3
  class Option
4
4
 
5
- def initialize(switches, argument_type, description, options = {})
5
+ def initialize(switches, type, description, options = {})
6
6
  @switches = Array(switches)
7
- @argument_type = argument_type
7
+ @type = type
8
8
  @description = description
9
9
  if options.has_key?(:attribute_name)
10
10
  @attribute_name = options[:attribute_name].to_s
@@ -14,7 +14,7 @@ module Clamp
14
14
  end
15
15
  end
16
16
 
17
- attr_reader :switches, :argument_type, :description, :default_value
17
+ attr_reader :switches, :type, :description, :default_value
18
18
 
19
19
  def attribute_name
20
20
  @attribute_name ||= long_switch.sub(/^--(\[no-\])?/, '').tr('-', '_')
@@ -29,21 +29,29 @@ module Clamp
29
29
  end
30
30
 
31
31
  def flag?
32
- @argument_type == :flag
32
+ @type == :flag
33
33
  end
34
34
 
35
35
  def flag_value(switch)
36
36
  !(switch =~ /^--no-(.*)/ && switches.member?("--\[no-\]#{$1}"))
37
37
  end
38
38
 
39
+ def extract_value(switch, arguments)
40
+ if flag?
41
+ flag_value(switch)
42
+ else
43
+ arguments.shift
44
+ end
45
+ end
46
+
39
47
  def help
40
48
  lhs = switches.join(", ")
41
- lhs += " " + argument_type unless flag?
49
+ lhs += " " + type unless flag?
42
50
  [lhs, description]
43
51
  end
44
52
 
45
53
  private
46
-
54
+
47
55
  def recognised_switches
48
56
  switches.map do |switch|
49
57
  if switch =~ /^--\[no-\](.*)/
@@ -0,0 +1,56 @@
1
+ require 'clamp/attribute_declaration'
2
+ require 'clamp/option'
3
+
4
+ module Clamp
5
+ class Option
6
+
7
+ module Declaration
8
+
9
+ include AttributeDeclaration
10
+
11
+ def option(switches, type, description, opts = {}, &block)
12
+ option = Clamp::Option.new(switches, type, description, opts)
13
+ my_declared_options << option
14
+ define_accessors_for(option, &block)
15
+ end
16
+
17
+ def has_options?
18
+ !declared_options.empty?
19
+ end
20
+
21
+ def declared_options
22
+ my_declared_options + inherited_declared_options
23
+ end
24
+
25
+ def recognised_options
26
+ declared_options + standard_options
27
+ end
28
+
29
+ def find_option(switch)
30
+ recognised_options.find { |o| o.handles?(switch) }
31
+ end
32
+
33
+ private
34
+
35
+ def my_declared_options
36
+ @my_declared_options ||= []
37
+ end
38
+
39
+ def inherited_declared_options
40
+ if superclass.respond_to?(:declared_options)
41
+ superclass.declared_options
42
+ else
43
+ []
44
+ end
45
+ end
46
+
47
+ HELP_OPTION = Clamp::Option.new("--help", :flag, "print help", :attribute_name => :help_requested)
48
+
49
+ def standard_options
50
+ [HELP_OPTION]
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,36 @@
1
+ module Clamp
2
+ class Option
3
+
4
+ module Parsing
5
+
6
+ protected
7
+
8
+ def parse_options
9
+ while arguments.first =~ /^-/
10
+
11
+ switch = arguments.shift
12
+ break if switch == "--"
13
+
14
+ option = find_option(switch)
15
+ value = option.extract_value(switch, arguments)
16
+
17
+ begin
18
+ send("#{option.attribute_name}=", value)
19
+ rescue ArgumentError => e
20
+ signal_usage_error "option '#{switch}': #{e.message}"
21
+ end
22
+
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def find_option(switch)
29
+ self.class.find_option(switch) ||
30
+ signal_usage_error("Unrecognised option '#{switch}'")
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+ end