clamp 0.0.7 → 0.0.9

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/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