clamp 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +12 -4
  3. data/.travis.yml +4 -6
  4. data/CHANGES.md +7 -0
  5. data/Gemfile +8 -6
  6. data/Guardfile +3 -1
  7. data/Rakefile +8 -0
  8. data/clamp.gemspec +8 -6
  9. data/examples/admin +3 -2
  10. data/examples/defaulted +4 -3
  11. data/examples/flipflop +1 -0
  12. data/examples/fubar +1 -0
  13. data/examples/gitdown +2 -1
  14. data/examples/scoop +3 -2
  15. data/examples/speak +3 -2
  16. data/examples/subcommand_missing +1 -0
  17. data/examples/word +1 -0
  18. data/lib/clamp.rb +3 -1
  19. data/lib/clamp/attribute/declaration.rb +5 -0
  20. data/lib/clamp/attribute/definition.rb +15 -12
  21. data/lib/clamp/attribute/instance.rb +5 -3
  22. data/lib/clamp/command.rb +9 -1
  23. data/lib/clamp/errors.rb +7 -3
  24. data/lib/clamp/help.rb +8 -6
  25. data/lib/clamp/messages.rb +21 -14
  26. data/lib/clamp/option/declaration.rb +4 -0
  27. data/lib/clamp/option/definition.rb +9 -3
  28. data/lib/clamp/option/parsing.rb +37 -32
  29. data/lib/clamp/parameter/declaration.rb +4 -0
  30. data/lib/clamp/parameter/definition.rb +9 -3
  31. data/lib/clamp/parameter/parsing.rb +5 -1
  32. data/lib/clamp/subcommand/declaration.rb +15 -13
  33. data/lib/clamp/subcommand/definition.rb +2 -0
  34. data/lib/clamp/subcommand/execution.rb +11 -0
  35. data/lib/clamp/subcommand/parsing.rb +4 -0
  36. data/lib/clamp/truthy.rb +4 -2
  37. data/lib/clamp/version.rb +3 -1
  38. data/spec/clamp/command_group_spec.rb +27 -9
  39. data/spec/clamp/command_spec.rb +84 -49
  40. data/spec/clamp/messages_spec.rb +5 -4
  41. data/spec/clamp/option/definition_spec.rb +13 -11
  42. data/spec/clamp/option_module_spec.rb +3 -1
  43. data/spec/clamp/option_reordering_spec.rb +6 -4
  44. data/spec/clamp/parameter/definition_spec.rb +14 -12
  45. data/spec/spec_helper.rb +3 -3
  46. metadata +4 -4
@@ -1,8 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Clamp
2
4
 
5
+ # raised to indicate invalid option/parameter declaration
3
6
  class DeclarationError < StandardError
4
7
  end
5
8
 
9
+ # abstract command runtime error
6
10
  class RuntimeError < StandardError
7
11
 
8
12
  def initialize(message, command)
@@ -14,10 +18,10 @@ module Clamp
14
18
 
15
19
  end
16
20
 
17
- # raise to signal incorrect command usage
21
+ # raised to signal incorrect command usage
18
22
  class UsageError < RuntimeError; end
19
23
 
20
- # raise to request usage help
24
+ # raised to request usage help
21
25
  class HelpWanted < RuntimeError
22
26
 
23
27
  def initialize(command)
@@ -26,7 +30,7 @@ module Clamp
26
30
 
27
31
  end
28
32
 
29
- # raise to signal error during execution
33
+ # raised to signal error during execution
30
34
  class ExecutionError < RuntimeError
31
35
 
32
36
  def initialize(message, command, status = 1)
@@ -1,8 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "stringio"
2
4
  require "clamp/messages"
3
5
 
4
6
  module Clamp
5
7
 
8
+ # Command help generation.
9
+ #
6
10
  module Help
7
11
 
8
12
  def usage(usage)
@@ -41,16 +45,14 @@ module Clamp
41
45
  help = builder
42
46
  help.add_usage(invocation_path, usage_descriptions)
43
47
  help.add_description(description)
44
- if has_parameters?
45
- help.add_list(Clamp.message(:parameters_heading), parameters)
46
- end
47
- if has_subcommands?
48
- help.add_list(Clamp.message(:subcommands_heading), recognised_subcommands)
49
- end
48
+ help.add_list(Clamp.message(:parameters_heading), parameters) if has_parameters?
49
+ help.add_list(Clamp.message(:subcommands_heading), recognised_subcommands) if has_subcommands?
50
50
  help.add_list(Clamp.message(:options_heading), recognised_options)
51
51
  help.string
52
52
  end
53
53
 
54
+ # A builder for auto-generated help.
55
+ #
54
56
  class Builder
55
57
 
56
58
  def initialize
@@ -1,5 +1,9 @@
1
- module Clamp
1
+ # frozen_string_literal: true
2
2
 
3
+ module Clamp #:nodoc:
4
+
5
+ # Message lookup, to allow localization.
6
+ #
3
7
  module Messages
4
8
 
5
9
  def messages=(new_messages)
@@ -17,19 +21,22 @@ module Clamp
17
21
  private
18
22
 
19
23
  DEFAULTS = {
20
- :too_many_arguments => "too many arguments",
21
- :option_required => "option '%<option>s' is required",
22
- :option_or_env_required => "option '%<option>s' (or env %<env>s) is required",
23
- :option_argument_error => "option '%<switch>s': %<message>s",
24
- :parameter_argument_error => "parameter '%<param>s': %<message>s",
25
- :env_argument_error => "$%<env>s: %<message>s",
26
- :unrecognised_option => "Unrecognised option '%<switch>s'",
27
- :no_such_subcommand => "No such sub-command '%<name>s'",
28
- :no_value_provided => "no value provided",
29
- :usage_heading => "Usage",
30
- :parameters_heading => "Parameters",
31
- :subcommands_heading => "Subcommands",
32
- :options_heading => "Options"
24
+ too_many_arguments: "too many arguments",
25
+ option_required: "option '%<option>s' is required",
26
+ option_or_env_required: "option '%<option>s' (or env %<env>s) is required",
27
+ option_argument_error: "option '%<switch>s': %<message>s",
28
+ parameter_argument_error: "parameter '%<param>s': %<message>s",
29
+ env_argument_error: "$%<env>s: %<message>s",
30
+ unrecognised_option: "Unrecognised option '%<switch>s'",
31
+ no_such_subcommand: "No such sub-command '%<name>s'",
32
+ no_value_provided: "no value provided",
33
+ default: "default",
34
+ or: "or",
35
+ required: "required",
36
+ usage_heading: "Usage",
37
+ parameters_heading: "Parameters",
38
+ subcommands_heading: "Subcommands",
39
+ options_heading: "Options"
33
40
  }.freeze
34
41
 
35
42
  def messages
@@ -1,9 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "clamp/attribute/declaration"
2
4
  require "clamp/option/definition"
3
5
 
4
6
  module Clamp
5
7
  module Option
6
8
 
9
+ # Option declaration methods.
10
+ #
7
11
  module Declaration
8
12
 
9
13
  include Clamp::Attribute::Declaration
@@ -1,9 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "clamp/attribute/definition"
2
4
  require "clamp/truthy"
3
5
 
4
6
  module Clamp
5
7
  module Option
6
8
 
9
+ # Represents an option of a Clamp::Command class.
10
+ #
7
11
  class Definition < Attribute::Definition
8
12
 
9
13
  def initialize(switches, type, description, options = {})
@@ -76,14 +80,16 @@ module Clamp
76
80
  end
77
81
 
78
82
  def infer_attribute_name
79
- unless long_switch
80
- raise Clamp::DeclarationError, "You must specify either a long-switch or an :attribute_value"
81
- end
83
+ raise Clamp::DeclarationError, "You must specify either a long-switch or an :attribute_value" unless long_switch
82
84
  inferred_name = long_switch.sub(/^--(\[no-\])?/, "").tr("-", "_")
83
85
  inferred_name += "_list" if multivalued?
84
86
  inferred_name
85
87
  end
86
88
 
89
+ def required_indicator
90
+ Clamp.message(:required) if required?
91
+ end
92
+
87
93
  end
88
94
 
89
95
  end
@@ -1,11 +1,17 @@
1
- module Clamp
1
+ # frozen_string_literal: true
2
+
3
+ module Clamp #:nodoc:
2
4
 
3
5
  class << self
6
+
4
7
  attr_accessor :allow_options_after_parameters
8
+
5
9
  end
6
10
 
7
11
  module Option
8
12
 
13
+ # Option parsing methods.
14
+ #
9
15
  module Parsing
10
16
 
11
17
  protected
@@ -13,7 +19,6 @@ module Clamp
13
19
  def parse_options
14
20
  set_options_from_command_line
15
21
  default_options_from_environment
16
- verify_required_options_are_set
17
22
  end
18
23
 
19
24
  private
@@ -22,43 +27,43 @@ module Clamp
22
27
  argument_buffer = []
23
28
  argument_buffer_limit = self.class.parameter_buffer_limit
24
29
  until remaining_arguments.empty?
25
-
26
30
  unless remaining_arguments.first.start_with?("-")
27
- if argument_buffer.size < argument_buffer_limit
28
- argument_buffer << remaining_arguments.shift
29
- next
30
- else
31
- break
32
- end
31
+ break unless argument_buffer.size < argument_buffer_limit
32
+ argument_buffer << remaining_arguments.shift
33
+ next
33
34
  end
34
-
35
35
  switch = remaining_arguments.shift
36
36
  break if switch == "--"
37
+ handle_switch(switch)
38
+ end
39
+ remaining_arguments.unshift(*argument_buffer)
40
+ end
37
41
 
38
- case switch
39
- when /\A(-\w)(.+)\z/m # combined short options
40
- switch = Regexp.last_match(1)
41
- if find_option(switch).flag?
42
- remaining_arguments.unshift("-" + Regexp.last_match(2))
43
- else
44
- remaining_arguments.unshift(Regexp.last_match(2))
45
- end
46
- when /\A(--[^=]+)=(.*)\z/m
47
- switch = Regexp.last_match(1)
48
- remaining_arguments.unshift(Regexp.last_match(2))
49
- end
50
-
51
- option = find_option(switch)
52
- value = option.extract_value(switch, remaining_arguments)
42
+ def handle_switch(switch)
43
+ switch = split_trailing_switches(switch)
44
+ option = find_option(switch)
45
+ value = option.extract_value(switch, remaining_arguments)
46
+ begin
47
+ option.of(self).take(value)
48
+ rescue ArgumentError => e
49
+ signal_usage_error Clamp.message(:option_argument_error, switch: switch, message: e.message)
50
+ end
51
+ end
53
52
 
54
- begin
55
- option.of(self).take(value)
56
- rescue ArgumentError => e
57
- signal_usage_error Clamp.message(:option_argument_error, :switch => switch, :message => e.message)
53
+ def split_trailing_switches(switch)
54
+ case switch
55
+ when /\A(-\w)(.+)\z/m # combined short options
56
+ switch = Regexp.last_match(1)
57
+ if find_option(switch).flag?
58
+ remaining_arguments.unshift("-" + Regexp.last_match(2))
59
+ else
60
+ remaining_arguments.unshift(Regexp.last_match(2))
58
61
  end
59
-
62
+ when /\A(--[^=]+)=(.*)\z/m
63
+ switch = Regexp.last_match(1)
64
+ remaining_arguments.unshift(Regexp.last_match(2))
60
65
  end
61
- remaining_arguments.unshift(*argument_buffer)
66
+ switch
62
67
  end
63
68
 
64
69
  def default_options_from_environment
@@ -75,7 +80,7 @@ module Clamp
75
80
 
76
81
  def find_option(switch)
77
82
  self.class.find_option(switch) ||
78
- signal_usage_error(Clamp.message(:unrecognised_option, :switch => switch))
83
+ signal_usage_error(Clamp.message(:unrecognised_option, switch: switch))
79
84
  end
80
85
 
81
86
  end
@@ -1,9 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "clamp/attribute/declaration"
2
4
  require "clamp/parameter/definition"
3
5
 
4
6
  module Clamp
5
7
  module Parameter
6
8
 
9
+ # Parameter declaration methods.
10
+ #
7
11
  module Declaration
8
12
 
9
13
  include Clamp::Attribute::Declaration
@@ -1,8 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "clamp/attribute/definition"
2
4
 
3
5
  module Clamp
4
6
  module Parameter
5
7
 
8
+ # Represents an parameter of a Clamp::Command class.
9
+ #
6
10
  class Definition < Attribute::Definition
7
11
 
8
12
  def initialize(name, description, options = {})
@@ -40,13 +44,15 @@ module Clamp
40
44
 
41
45
  def infer_attribute_name
42
46
  inferred_name = name.downcase.tr("-", "_").sub(ELLIPSIS_SUFFIX, "").sub(OPTIONAL) { Regexp.last_match(1) }
43
- unless inferred_name =~ VALID_ATTRIBUTE_NAME
44
- raise "cannot infer attribute_name from #{name.inspect}"
45
- end
47
+ raise "cannot infer attribute_name from #{name.inspect}" unless inferred_name =~ VALID_ATTRIBUTE_NAME
46
48
  inferred_name += "_list" if multivalued?
47
49
  inferred_name
48
50
  end
49
51
 
52
+ def required_indicator
53
+ # implied by LHS
54
+ end
55
+
50
56
  end
51
57
 
52
58
  end
@@ -1,6 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Clamp
2
4
  module Parameter
3
5
 
6
+ # Parameter parsing methods.
7
+ #
4
8
  module Parsing
5
9
 
6
10
  protected
@@ -19,7 +23,7 @@ module Clamp
19
23
  parameter.of(self).take(value)
20
24
  end
21
25
  rescue ArgumentError => e
22
- signal_usage_error Clamp.message(:parameter_argument_error, :param => parameter.name, :message => e.message)
26
+ signal_usage_error Clamp.message(:parameter_argument_error, param: parameter.name, message: e.message)
23
27
  end
24
28
  end
25
29
  end
@@ -1,9 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "clamp/errors"
2
4
  require "clamp/subcommand/definition"
3
5
 
4
6
  module Clamp
5
7
  module Subcommand
6
8
 
9
+ # Subcommand declaration methods.
10
+ #
7
11
  module Declaration
8
12
 
9
13
  def recognised_subcommands
@@ -37,9 +41,7 @@ module Clamp
37
41
  end
38
42
 
39
43
  def default_subcommand=(name)
40
- if has_subcommands?
41
- raise Clamp::DeclarationError, "default_subcommand must be defined before subcommands"
42
- end
44
+ raise Clamp::DeclarationError, "default_subcommand must be defined before subcommands" if has_subcommands?
43
45
  @default_subcommand = name
44
46
  end
45
47
 
@@ -48,7 +50,7 @@ module Clamp
48
50
  @default_subcommand
49
51
  else
50
52
  $stderr.puts "WARNING: Clamp default_subcommand syntax has changed; check the README."
51
- $stderr.puts " (from #{caller.first})"
53
+ $stderr.puts " (from #{caller(1..1).first})"
52
54
  self.default_subcommand = args.first
53
55
  subcommand(*args, &block)
54
56
  end
@@ -59,19 +61,19 @@ module Clamp
59
61
  def declare_subcommand_parameters
60
62
  if @default_subcommand
61
63
  parameter "[SUBCOMMAND]", "subcommand",
62
- :attribute_name => :subcommand_name,
63
- :default => @default_subcommand,
64
- :inheritable => false
64
+ attribute_name: :subcommand_name,
65
+ default: @default_subcommand,
66
+ inheritable: false
65
67
  else
66
68
  parameter "SUBCOMMAND", "subcommand",
67
- :attribute_name => :subcommand_name,
68
- :required => false,
69
- :inheritable => false
69
+ attribute_name: :subcommand_name,
70
+ required: false,
71
+ inheritable: false
70
72
  end
71
- remove_method :default_subcommand_name
73
+ remove_method :default_subcommand_name if method_defined?(:default_subcommand_name)
72
74
  parameter "[ARG] ...", "subcommand arguments",
73
- :attribute_name => :subcommand_arguments,
74
- :inheritable => false
75
+ attribute_name: :subcommand_arguments,
76
+ inheritable: false
75
77
  end
76
78
 
77
79
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Clamp
2
4
  module Subcommand
3
5
 
@@ -1,6 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Clamp
2
4
  module Subcommand
3
5
 
6
+ # Support for subcommand execution.
7
+ #
8
+ # This module is mixed into command instances that have subcommands, overriding
9
+ # default behaviour in {Clamp::Command}.
10
+ #
4
11
  module Execution
5
12
 
6
13
  # override default Command behaviour
@@ -34,6 +41,10 @@ module Clamp
34
41
  subcommand_missing(name)
35
42
  end
36
43
 
44
+ def verify_required_options_are_set
45
+ # not required
46
+ end
47
+
37
48
  end
38
49
 
39
50
  end
@@ -1,8 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "clamp/subcommand/execution"
2
4
 
3
5
  module Clamp
4
6
  module Subcommand
5
7
 
8
+ # Subcommand parsing methods.
9
+ #
6
10
  module Parsing
7
11
 
8
12
  protected
@@ -1,6 +1,8 @@
1
- module Clamp
1
+ # frozen_string_literal: true
2
2
 
3
- TRUTHY_VALUES = %w(1 yes enable on true).freeze
3
+ module Clamp #:nodoc:
4
+
5
+ TRUTHY_VALUES = %w[1 yes enable on true].freeze
4
6
 
5
7
  def self.truthy?(arg)
6
8
  TRUTHY_VALUES.include?(arg.to_s.downcase)