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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7e919d5a5f983e9de93adeccf16090c2ccf673d6
4
- data.tar.gz: fcc637ed598db752ac2411970391ff9440e96fcc
3
+ metadata.gz: a8365dad9e1c1555dfddea4d47c063438af94be5
4
+ data.tar.gz: b618f175a72f08180fb77315cc1a4ceafbbb134e
5
5
  SHA512:
6
- metadata.gz: bc3d0c1e9a7172ab3a0f8d09cc0583aec4f03ee8a85db033d2e0c413a03a7455ddc1c1cca622781620a28ab47148ebe9b3245d54ac533278bc0398e767dc558b
7
- data.tar.gz: 9551a0bbae48288c19b9ac5a2f1918753620d4deefd8efc1e47fa5206c9911e9ee80aa60223f017133e7ff545d8d61624ff1308fb17a9412d2c1724ee6623c9b
6
+ metadata.gz: 2bec2151d2344efc7fbcd37709646904d20e7456fa83365484aacfa318bb23207a8e3ae9e813457d26d791f56b8c890eb863ccb59a85724a53b542193af26384
7
+ data.tar.gz: 0be7bb668484292023495887a581e8e887e0d3f10f2fac89353f6e6a6d091a2f3e9a308902a3f8548c169dd138c0aec7a90e28fac950e36999f9524b1ffab886
@@ -1,3 +1,6 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.1
3
+
1
4
  Eval:
2
5
  Exclude:
3
6
  - "Rakefile"
@@ -14,6 +17,10 @@ Layout/EmptyLinesAroundModuleBody:
14
17
  Metrics/AbcSize:
15
18
  Enabled: false
16
19
 
20
+ Metrics/BlockLength:
21
+ Exclude:
22
+ - "spec/**/*"
23
+
17
24
  Metrics/LineLength:
18
25
  Max: 120
19
26
 
@@ -27,6 +34,9 @@ Naming/FileName:
27
34
  Exclude:
28
35
  - "bin/*"
29
36
 
37
+ Naming/PredicateName:
38
+ Enabled: false
39
+
30
40
  Style/ClassAndModuleChildren:
31
41
  EnforcedStyle: nested
32
42
  Exclude:
@@ -35,21 +45,19 @@ Style/ClassAndModuleChildren:
35
45
  Style/Documentation:
36
46
  Exclude:
37
47
  - "lib/**/version.rb"
48
+ - "examples/*"
38
49
  - "spec/**/*"
39
50
 
40
51
  Style/Encoding:
41
52
  Enabled: true
42
53
 
43
- Style/HashSyntax:
44
- EnforcedStyle: hash_rockets
45
-
46
54
  Style/Lambda:
47
55
  Enabled: false
48
56
 
49
57
  Style/NumericLiterals:
50
58
  Enabled: false
51
59
 
52
- Naming/PredicateName:
60
+ Style/StderrPuts:
53
61
  Enabled: false
54
62
 
55
63
  Style/StringLiterals:
@@ -1,8 +1,6 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.1.8
4
- - 2.2.4
5
- - 2.3.1
6
- before_install:
7
- - gem update --system
8
- - gem install bundler
3
+ - 2.1.9
4
+ - 2.2.10
5
+ - 2.3.7
6
+ - 2.5.1
data/CHANGES.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.3.0 (2018-06-17)
4
+
5
+ * Add `.execute` DSL method.
6
+ * Append '(required)' to the description of required options.
7
+ * Fix issue#75: don't generate `default_XXX` method unless a default is specified.
8
+ * Fix issue#90: allow required options to be provided after subcommands.
9
+
3
10
  ## 1.2.0 (2018-02-12)
4
11
 
5
12
  * Add option to `Clamp.allow_options_after_parameters`.
data/Gemfile CHANGED
@@ -1,15 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "http://rubygems.org"
2
4
 
3
5
  gemspec
4
6
 
5
7
  group :development do
6
- gem "guard-rspec", "~> 4.6.5", :require => false
7
- gem "listen", "~> 3.0.2"
8
- gem "rake", "~> 10.4"
9
- gem "rubocop", :require => false
8
+ gem "guard-rspec", "~> 4.7", require: false
9
+ gem "highline"
10
+ gem "listen", "~> 3.0"
11
+ gem "rake", "~> 12.3"
12
+ gem "rubocop", require: false
10
13
  end
11
14
 
12
15
  group :test do
13
- gem "rspec", "~> 3.5.0"
14
- gem "rr", "~> 1.2.0"
16
+ gem "rspec", "~> 3.7"
15
17
  end
data/Guardfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # A sample Guardfile
2
4
  # More info at https://github.com/guard/guard#readme
3
5
 
@@ -24,7 +26,7 @@
24
26
  # * zeus: 'zeus rspec' (requires the server to be started separately)
25
27
  # * 'just' rspec: 'rspec'
26
28
 
27
- guard :rspec, :cmd => "bundle exec rspec" do
29
+ guard :rspec, cmd: "bundle exec rspec" do
28
30
  require "guard/rspec/dsl"
29
31
  dsl = Guard::RSpec::Dsl.new(self)
30
32
 
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler"
2
4
 
3
5
  Bundler::GemHelper.install_tasks
@@ -10,3 +12,9 @@ RSpec::Core::RakeTask.new do |t|
10
12
  t.pattern = "spec/**/*_spec.rb"
11
13
  t.rspec_opts = ["--colour", "--format", "documentation"]
12
14
  end
15
+
16
+ require "rubocop/rake_task"
17
+
18
+ RuboCop::RakeTask.new
19
+
20
+ task "default" => "rubocop"
@@ -1,4 +1,6 @@
1
- $LOAD_PATH.push File.expand_path("../lib", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.push File.expand_path("lib", __dir__)
2
4
  require "clamp/version"
3
5
 
4
6
  Gem::Specification.new do |s|
@@ -8,15 +10,15 @@ Gem::Specification.new do |s|
8
10
  s.platform = Gem::Platform::RUBY
9
11
  s.authors = ["Mike Williams"]
10
12
  s.email = "mdub@dogbiscuit.org"
11
- s.homepage = "http://github.com/mdub/clamp"
13
+ s.homepage = "https://github.com/mdub/clamp"
12
14
 
13
15
  s.license = "MIT"
14
16
 
15
17
  s.summary = "a minimal framework for command-line utilities"
16
- s.description = <<EOF
17
- Clamp provides an object-model for command-line utilities.
18
- It handles parsing of command-line options, and generation of usage help.
19
- EOF
18
+ s.description = <<-TEXT.gsub(/^\s+/, "")
19
+ Clamp provides an object-model for command-line utilities.
20
+ It handles parsing of command-line options, and generation of usage help.
21
+ TEXT
20
22
 
21
23
  s.files = `git ls-files`.split("\n")
22
24
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -1,4 +1,5 @@
1
1
  #! /usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  # An example of options and parameters
4
5
 
@@ -6,12 +7,12 @@ require "clamp"
6
7
 
7
8
  Clamp do
8
9
 
9
- option "--timeout", "SECONDS", "connection timeout", :default => 5, :environment_variable => "MYAPP_TIMEOUT" do |x|
10
+ option "--timeout", "SECONDS", "connection timeout", default: 5, environment_variable: "MYAPP_TIMEOUT" do |x|
10
11
  Integer(x)
11
12
  end
12
13
 
13
14
  parameter "HOST", "server address"
14
- parameter "[PORT]", "server port", :default => 80, :environment_variable => "MYAPP_PORT"
15
+ parameter "[PORT]", "server port", default: 80, environment_variable: "MYAPP_PORT"
15
16
 
16
17
  def execute
17
18
  puts "trying to connect to #{host} on port #{port} (waiting up to #{timeout} seconds)"
@@ -1,4 +1,5 @@
1
1
  #! /usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  # An example of default values and methods
4
5
 
@@ -8,10 +9,10 @@ require "highline"
8
9
  Clamp do
9
10
 
10
11
  option ["-U", "--user"], "USER", "user name",
11
- :environment_variable => "THE_USER",
12
- :default => "bob"
12
+ environment_variable: "THE_USER",
13
+ default: "bob"
13
14
  option ["-P", "--password"], "PASSWORD", "password",
14
- :environment_variable => "THE_PASSWORD"
15
+ environment_variable: "THE_PASSWORD"
15
16
 
16
17
  def execute
17
18
  puts "User: #{user}, Password: #{password}"
@@ -1,4 +1,5 @@
1
1
  #! /usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  # An example of subcommands (including a default subcommand)
4
5
 
@@ -1,4 +1,5 @@
1
1
  #! /usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  # An example of nested subcommands
4
5
 
@@ -1,4 +1,5 @@
1
1
  #! /usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  # Demonstrate how subcommands can be declared as classes
4
5
 
@@ -25,7 +26,7 @@ module GitDown
25
26
  class CloneCommand < AbstractCommand
26
27
 
27
28
  parameter "REPOSITORY", "repository to clone"
28
- parameter "[DIR]", "working directory", :default => "."
29
+ parameter "[DIR]", "working directory", default: "."
29
30
 
30
31
  def execute
31
32
  say "cloning to #{dir}"
@@ -1,4 +1,5 @@
1
1
  #! /usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  # An example of multi-valued options
4
5
 
@@ -7,8 +8,8 @@ require "clamp"
7
8
  Clamp do
8
9
 
9
10
  option ["-f", "--flavour"], "FLAVOUR", "flavour",
10
- :multivalued => true, :default => ["chocolate"],
11
- :attribute_name => :flavours
11
+ multivalued: true, default: ["chocolate"],
12
+ attribute_name: :flavours
12
13
 
13
14
  def execute
14
15
  puts "one #{flavours.join(' and ')} ice-cream"
@@ -1,4 +1,5 @@
1
1
  #! /usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  # A simple Clamp command, with options and parameters
4
5
 
@@ -11,11 +12,11 @@ Clamp do
11
12
  )
12
13
 
13
14
  option "--loud", :flag, "say it loud"
14
- option ["-n", "--iterations"], "N", "say it N times", :default => 1 do |s|
15
+ option ["-n", "--iterations"], "N", "say it N times", default: 1 do |s|
15
16
  Integer(s)
16
17
  end
17
18
 
18
- parameter "WORDS ...", "the thing to say", :attribute_name => :words
19
+ parameter "WORDS ...", "the thing to say", attribute_name: :words
19
20
 
20
21
  def execute
21
22
  the_truth = words.join(" ")
@@ -1,4 +1,5 @@
1
1
  #! /usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  # Demonstrate subcommand_missing
4
5
 
@@ -1,4 +1,5 @@
1
1
  #! /usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  # Demonstrate "restful" style; parameters precede subcommands
4
5
 
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "clamp/version"
2
4
 
3
5
  require "clamp/command"
4
6
 
5
- def Clamp(&block)
7
+ def Clamp(&block) # rubocop:disable Naming/MethodName
6
8
  Class.new(Clamp::Command, &block).run
7
9
  end
@@ -1,6 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Clamp
2
4
  module Attribute
3
5
 
6
+ # Methods to generate attribute accessors.
7
+ #
4
8
  module Declaration
5
9
 
6
10
  protected
@@ -25,6 +29,7 @@ module Clamp
25
29
  end
26
30
 
27
31
  def define_default_for(attribute)
32
+ return false if attribute.default_value.nil?
28
33
  define_method(attribute.default_method) do
29
34
  attribute.default_value
30
35
  end
@@ -1,25 +1,28 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "clamp/attribute/instance"
2
4
 
3
5
  module Clamp
4
6
  module Attribute
5
7
 
8
+ # Represents an attribute of a Clamp::Command class.
9
+ #
6
10
  class Definition
7
11
 
8
12
  def initialize(options)
9
- if options.key?(:attribute_name)
10
- @attribute_name = options[:attribute_name].to_s
11
- end
13
+ @attribute_name = options[:attribute_name].to_s if options.key?(:attribute_name)
12
14
  @default_value = options[:default] if options.key?(:default)
13
- if options.key?(:environment_variable)
14
- @environment_variable = options[:environment_variable]
15
- end
15
+ @environment_variable = options[:environment_variable] if options.key?(:environment_variable)
16
16
  @hidden = options[:hidden] if options.key?(:hidden)
17
17
  end
18
18
 
19
19
  attr_reader :description, :environment_variable
20
20
 
21
21
  def help_rhs
22
- description + default_description
22
+ rhs = description
23
+ comments = required_indicator || default_description
24
+ rhs += " (#{comments})" if comments
25
+ rhs
23
26
  end
24
27
 
25
28
  def help
@@ -77,11 +80,11 @@ module Clamp
77
80
  def option_missing_message
78
81
  if environment_variable
79
82
  Clamp.message(:option_or_env_required,
80
- :option => switches.first,
81
- :env => environment_variable)
83
+ option: switches.first,
84
+ env: environment_variable)
82
85
  else
83
86
  Clamp.message(:option_required,
84
- :option => switches.first)
87
+ option: switches.first)
85
88
  end
86
89
  end
87
90
 
@@ -92,8 +95,8 @@ module Clamp
92
95
  ("$#{@environment_variable}" if defined?(@environment_variable)),
93
96
  (@default_value.inspect if defined?(@default_value))
94
97
  ].compact
95
- return "" if default_sources.empty?
96
- " (default: " + default_sources.join(", or ") + ")"
98
+ return nil if default_sources.empty?
99
+ "#{Clamp.message(:default)}: " + default_sources.join(", #{Clamp.message(:or)} ")
97
100
  end
98
101
 
99
102
  end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Clamp
2
4
  module Attribute
3
5
 
4
- # Represents an option/parameter of a Clamp::Command instance.
6
+ # Represents an attribute of a Clamp::Command instance.
5
7
  #
6
8
  class Instance
7
9
 
@@ -27,7 +29,7 @@ module Clamp
27
29
  end
28
30
 
29
31
  def default
30
- command.send(attribute.default_method)
32
+ command.send(attribute.default_method) if command.respond_to?(attribute.default_method, true)
31
33
  end
32
34
 
33
35
  # default implementation of read_method
@@ -73,7 +75,7 @@ module Clamp
73
75
  begin
74
76
  take(value)
75
77
  rescue ArgumentError => e
76
- signal_usage_error Clamp.message(:env_argument_error, :env => attribute.environment_variable, :message => e.message)
78
+ signal_usage_error Clamp.message(:env_argument_error, env: attribute.environment_variable, message: e.message)
77
79
  end
78
80
  end
79
81
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "clamp/messages"
2
4
  require "clamp/errors"
3
5
  require "clamp/help"
@@ -48,6 +50,7 @@ module Clamp
48
50
  parse_options
49
51
  parse_parameters
50
52
  parse_subcommand
53
+ verify_required_options_are_set
51
54
  handle_remaining_arguments
52
55
  end
53
56
 
@@ -81,7 +84,7 @@ module Clamp
81
84
  #
82
85
  # @ param [String] name subcommand_name
83
86
  def subcommand_missing(name)
84
- signal_usage_error(Clamp.message(:no_such_subcommand, :name => name))
87
+ signal_usage_error(Clamp.message(:no_such_subcommand, name: name))
85
88
  end
86
89
 
87
90
  include Clamp::Option::Parsing
@@ -122,6 +125,11 @@ module Clamp
122
125
  include Clamp::Subcommand::Declaration
123
126
  include Help
124
127
 
128
+ # An alternative to "def execute"
129
+ def execute(&block)
130
+ define_method(:execute, &block)
131
+ end
132
+
125
133
  # Create an instance of this command class, and run it.
126
134
  #
127
135
  # @param [String] invocation_path the path used to invoke the command