GithubHelp home page GithubHelp logo

clamp's People

Contributors

acook avatar alexwayfer avatar ares avatar danp avatar ekohl avatar evgeni avatar fajpunk avatar fearoffish avatar gitter-badger avatar jordansissel avatar mdub avatar mjio avatar mschulkind avatar ronen avatar schneiderl avatar smileart avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

clamp's Issues

Option / flag order

When parsing, it seems the order of options / flags and arguments matters.

Ex:
command --option arg

But if I type
command arg --option

It fails. ERROR: Too many arguments

Could this be resolved so the order of parameters doesn't matter? (Obviously order of arguments would matter, but not flags / options)

[RFE] option groups

What do you think of adding groups to options so when you display help you can divide them? Especially useful when you have more than 15 options I think.

Suggestion: 'execute do' instead of 'def execute'

Not sure if this was considered before and ruled out or not, but something that strikes me as a little odd in the syntax for creating an application with Clamp, is the fact that it has a def execute inside of a block. I don't recall seeing such an approach anywhere (but of course it may just mean that I have not seen enough gems...).

I was wondering if it would make sense to change or amend the syntax so that it will allow the use of execute do block, like so:

Clamp do
  subcommand "test", "Test it" do
    option "--loud", :flag, "say it loud"

    execute do
      ...
    end
  end
end

This feels a little more natural to me.

Allow options and flags after parameters

Both should work:
command delete --force filename.txt
command delete filename.txt --force

Also this should work:

command delete -- --filename.txt (filename is "--filename.txt")

..and I think it already does:

# option/parsing.rb:20
           break if switch == "--"

License missing from gemspec

RubyGems.org doesn't report a license for your gem. This is because it is not specified in the gemspec of your last release.

via e.g.

  spec.license = 'MIT'
  # or
  spec.licenses = ['MIT', 'GPL-2']

Including a license in your gemspec is an easy way for rubygems.org and other tools to check how your gem is licensed. As you can imagine, scanning your repository for a LICENSE file or parsing the README, and then attempting to identify the license or licenses is much more difficult and more error prone. So, even for projects that already specify a license, including a license in your gemspec is a good practice. See, for example, how rubygems.org uses the gemspec to display the rails gem license.

There is even a License Finder gem to help companies/individuals ensure all gems they use meet their licensing needs. This tool depends on license information being available in the gemspec. This is an important enough issue that even Bundler now generates gems with a default 'MIT' license.

I hope you'll consider specifying a license in your gemspec. If not, please just close the issue with a nice message. In either case, I'll follow up. Thanks for your time!

Appendix:

If you need help choosing a license (sorry, I haven't checked your readme or looked for a license file), GitHub has created a license picker tool. Code without a license specified defaults to 'All rights reserved'-- denying others all rights to use of the code.
Here's a list of the license names I've found and their frequencies

p.s. In case you're wondering how I found you and why I made this issue, it's because I'm collecting stats on gems (I was originally looking for download data) and decided to collect license metadata,too, and make issues for gemspecs not specifying a license as a public service :). See the previous link or my blog post about this project for more information.

Subcommand's aren't inherited

subcommands declared in a parent class don't appear in child classes:

#!  /usr/bin/env ruby
require 'clamp'

module Example
  class Base < Clamp::Command
    option "--version", :flag, "shows the version" do
      puts "VERSION"
      exit 0
    end

    def execute
      exit 0
    end

    subcommand "parent", "a command from the parent" do
    end
  end

  class Sub1 < Base
    parameter "ONE", "one thing"

    def execute
      puts one.inspect
    end
  end

  class Sub2 < Sub1
    def execute
      puts "from Sub2"
      super
    end
  end

  class Main < Base
    subcommand "one", "ones", Sub1
    subcommand "two", "twos", Sub2
  end
end

Example::Main.run

In the terminal:

$ cli --help
Subcommands:
one                           ones
two                           twos

I expected Base's subcommands to come along.

Showing "short usage" unless "--help" is provided

Ok, so this Clamp library is gorgeous. I love it, and it had me at "subcommand".

I am used to command line apps showing only a short "usage" pattern whenever they are executed without any argument, and only show the longer help when --help is provided.

With clamp, I see full help even without asking for it with --help.

Is there a way to change this behavior?

I am expecting to only see something like this:

$ ruby test.rb
Usage:
    test.rb [OPTIONS] SUBCOMMAND [ARG] ...

$ _

Support emdash?

Copy/pasting --foo into pidgin, adium, skype, etc, will turn the two-character double dash into a single emdash.

This makes folks get confused when flag parsing goes awry because clamp doesn't think emdash (—) (and other long dash glyphs?) are valid for flags.

Thoughts? I can try to prepare a patch for this.

Add :deprecated to options

I have flags I wish to deprecate, and I'd love it clamp could help with this.

Idea:

  • An option with :deprecated set will not appear in the usage output (ie; --help)
  • An option can be declared :deprecated => "The reason this is deprecated and some other notes" which can be displayed to users however the application developer decides.[1]
  • Add a new method deprecated_options intended for use in the Clamp::Command#execute method which allows an author to iterate over deprecated options that were given by the user

Rough sketch:

class Whoa < Clamp::Command
  option "--really-fast", :flag, "Go fast!", :deprecated => "Please use the --fast flag"
  option "--fast", :flag, "Go fast!"

  def execute
    deprecated_options.each do |flag, reason|
      puts "The #{flag} option is deprecated. #{reason}"
    end
  end
end

I am willing to provide the patches to achieve this, but I'd like to iterate on the design first before I write some code :)

Parameter values not passed down to nested subcommands

Given command subbed:

Clamp do

  parameter "THING", "the thing"

  subcommand "speak", "say things" do
    subcommand "loud", "from the rooftops" do
      def execute
        puts "#{thing.upcase}"
      end
    end
  end

end

I'd expect ./subbed blahblah speak loud to print "BLAHBLAH". Instead, it dies with:

$ ./subbed blahblah speak loud
./subbed:12:in `execute': undefined method `upcase' for nil:NilClass (NoMethodError)
    from /Users/mdub/.gem/ruby/2.3.1/gems/clamp-1.0.0/lib/clamp/command.rb:68:in `run'
    from /Users/mdub/.gem/ruby/2.3.1/gems/clamp-1.0.0/lib/clamp/subcommand/execution.rb:11:in `execute'
    from /Users/mdub/.gem/ruby/2.3.1/gems/clamp-1.0.0/lib/clamp/command.rb:68:in `run'
    from /Users/mdub/.gem/ruby/2.3.1/gems/clamp-1.0.0/lib/clamp/subcommand/execution.rb:11:in `execute'
    from /Users/mdub/.gem/ruby/2.3.1/gems/clamp-1.0.0/lib/clamp/command.rb:68:in `run'
    from /Users/mdub/.gem/ruby/2.3.1/gems/clamp-1.0.0/lib/clamp/command.rb:133:in `run'
    from /Users/mdub/.gem/ruby/2.3.1/gems/clamp-1.0.0/lib/clamp.rb:6:in `Clamp'
    from ./subbed:5:in `<main>'

"setup" method for global initialization?

Hi, thanks for the great gem.

In my current use, I have to do some initialization based on the global option values. I can define a setup method in my Clamp block, but then I need to call it at the start of each of my execute methods.

I wonder if you'd be interested in a PR that would DRY that up, automatically calling an optional setup?

It seems implementation would be easy, at command.rb#L61:

def run(arguments)
  parse(arguments)
  setup
  execute
end

def setup
  # base does nothing, sub-class can override 
end

That's all that I need for my current application, but one could also imagine going whole hog and providing setup, teardown, and around:

def run(arguments)
  parse(arguments)
  begin
    setup
    around do
      execute
    end
  ensure
    teardown
  end
end

def setup
  # base does nothing, sub-class can override 
end

def around
  yield # base does nothing interesting, sub-class can override
end

def teardown
  # base does nothing, sub-class can override
end

...but that might be overkill.

What do you think? I'm happy to submit a PR but don't want to put in the time for the specs etc if you're not up for it. Also could debate the names of the methods, the execution order of setup/teardown vs around, etc.

Thanks again

options / arguments ordering

I'm in the process of converting my Thor cli to Clamp. I am more of a fan of the way Clamp handles commands and arguments, but one major thing that bugs me is the fact that Clamp requires options to be specified before arguments.

Is there a reason why this was chosen?

I must say this reads more natural to me:

rspec my_test.rb --backtrace

than this does:

rspec --backtrace my_test.rb

To elaborate a bit more on my issue:

$ hashi test subject_name
using subject: subject_name
with commit: 
$ hashi test subject_name --commit "abcd..awes"
ERROR: too many arguments

See: 'hashi test --help'
$ hashi test --commit abcd..awes subject_name
using subject: subject_name
with commit: abcd..awes

my implementation:

class Hashi::CLI::Test < Abstract
  parameter '[SUBJECT]', 'Run test suite against subject'

  option ['--commit', '-c'], '4htg3..kj8rv',
    'Test only touched subjects in this commit (range)'

  def execute
    puts "using subject: #{subject}"
    puts "with commit: #{commit}"
  end
end

subcommand_missing only invoked if command has defined at least one subcommand

This code runs as expected:

#!/usr/bin/env ruby

require 'clamp'

Clamp do
  subcommand 'foo', 'bar'

  def subcommand_missing(name)
    abort "would have looked for subcommand #{name} here"
  end
end

If you comment out the subcommand, however, you just get:

ERROR: too many arguments

See: 'clamptest.rb --help'

This appears to be due to the has_subcommands? check in parse_subcommand, but I don't understand the code well enough yet to suggest a solution.

Options only work with longopts.

As soon as you use something like option "-d", it throws a trace, because the infer_attribute_name regex only matches for longopts.

To work around this one can manually specify an :attribute_name. Would be nice if that was either fixed or mentioned in the README.

May have found a bug, or at least an unexpected behaviour in parse order.

For reference, here is my latest code: https://github.com/bgupta/foreman-hammer/blob/master/hammer

I setup most options, in HammerCommand, and setup a couple methods of the --format option there.

 option "--format", "FORMAT", "Output format", :default => "json"

 def allowed_content_types
    return [ "json", "yaml", "text" ]
 end

 def format= s
   raise ArgumentError, "must be one of: #{allowed_content_types.join(', ')}" unless allowed_content_types.include?(s)
   @format = s
 end

This seems to work fine.

The problem comes when I try to override the allowed_content_types method in the HostShowCommand subclass.

def allowed_content_types
  return [ "json", "yaml" ]
end

I would expect that if I call the "hammer --format blah show host" command ("blah" being the literal string), it should throw an error listing json and yaml as the valid content types. In my case it is throwing the following error:

ERROR: option '--format': must be one of: json, yaml, text

See: 'hammer --help'

For some reason the options seem to be being parsed before the subcommands? I tried a number of different things, and asked a friend who knows ruby a lot better, and he felt that it probably wasn't my code, and might be a bug in clamp, and suggested that I follow up here.

Getting parameters and options dynamically from the execute method

I am using the Clamp in a tool where its parameters and options are dynamically created from a config file. This means that I only know during run time what are my params/options/flags.

When I get to writing the execute method I would like to have the ability to go over all params/options/flags... and send them to my program.

example:

Clamp do
  #Declarations of all parameters and options....

def execute
  options_and_their_values = self.get_options
  parameters... = self.get_params
 MyProgramClass.new(options_and_their_values)
end

end #end clamp

In this example I could send all params/options and their values without knowing what they are in run time.

I would like to hear what you think.

Optional Parameter Default Must Be Accessed Thru default_param ...

I am using and really loving clamp.
I am excited to use the new optional parameter default, but it seems to me when getting the value of an option param that has a default, the default is not given.
To get the behavior I expected I had to use: param || default_param.
Is this the intended workflow?
I think that calling param should give the default if one was not given.

Thank you for a wonderful gem.

Long options break justification

      # help.rb
      DETAIL_FORMAT = "    %-29s %s"

It would be nice if it calculated the justification based on the longest string. Now it looks messed up with long parameter names.

Is there a way to support global options?

e.g. - using git as an option:

git --namespace=blah branch delete branchname

also it seems that specific subcommand should ideally be able to support their own options as well.

Thanks,
Brian

Default subcommand with options/arguments

Right now it seems that the default subcommand is only supported when passing nothing to the clamp script. This generates an "odd" behavior to me.

For example I have these two subcommands (just example code, not really working):

class MainCommand < Clamp::Command
  #
  # Global options
  #
  option "-V", :flag, "print out version" do
    puts(VERSION)
    exit(0)
  end

  option [ "-v", "--verbose" ], :flag, "print out stuff", :default => false 

  #
  # Sub commands
  #
  self.default_subcommand = "sub1"

  subcommand "sub1", "subcommand 1", SubCommand1
  subcommand "sub2", "subcommand 2", SubCommand2
end

class SubCommand1 < MainCommand
  option "-o", "OPTION_ARG", "I am a required option", :required => true

  def execute
    puts "I am subcommand 1"
  end
end

class SubCommand2 < MainCommand
  option "-z", "OPTION_ARG", "I am an option"

  def execute
    puts "I am subcommand 2"
  end
end

What happens is that, when calling the command without any arguments, it correctly requires to specify the -o OPTION_ARG option since it defaults to SubCommand1; when adding the -o OPTION_ARG option without any subcommand clamp calls MainCommand and results in: ERROR: Unrecognised option '-o'.

This seems to me an odd behavior, I would expect clamp to always default to the default subcommand and only trigger specific options/subcommands in case you specify some.
A consistent behavior to me would be to always try the default subcommand and print the MainCommand help in case problems arise.

What do you think? Am I getting something wrong here? If you think I have a point I can pull request the changes needed.

Feature: environment variables as flag values

Howdy!

It's fairly common in some environements (like heroku, etc) to use environment variables to configure applications. I am happy to produce this feature as a patch, but am looking for feedback and thumbs-up before I spend time on it :)

Thoughts? General proposal below:

New feature: Flags can be set with environment variables

The problem

I see lots of programs that take flags but also support configuration via environment variables (rails, heroku, etc). In most cases, I find these programs using optparse, clamp, etc, for the flag handling, but then handling environment variables separately, and often, with code that is not shared across projects.

Automatic env handling:

option "--foo-bar", "FOO", "Some foo"

The above could be set with 'myapp --foo-bar value' like normal, but also FOO_BAR=value from the environment

Explicit env

option "--foo-bar", "FOO", "Some foo", :env => "FOO"

The above would allow FOO=value to set the value for --foo-bar.

--help improvements

Some hints should be given in the --help usage to indicate that environment variables can be used.

Conflicts

When given both env and flag values, the flag value should win.

How to define --version

It's quite common to have something like app --version output the version.

How to do this with clamp if you have subcommands in your main command?

How about other "global" options/flags such as ['-d', '--debug']?

Running plugins or "external subcommands"

Many CLI utils (git, brew, ..) are using a plugin system that works by trying to execute for example brew-do-stuff if you run brew do-stuff which is an unknown subcommand. The plugin subcommand receives rest of the arguments and gets the ENV from the parent app.

How would you do something similiar using clamp?

I was thinking about something like this:

  app_basename = File.basename(__FILE__)
  ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
    Dir[File.join(path, "#{app_basename}-*")].select{|f| File.executable?(f) && !File.directory?(f)}.each do |plugin_path|
      subcommand File.basename(plugin_path)[/^#{app_basename}-(.*)/, 1], "External command" do
        def execute
          exec "#{app_basename}-#{ARGV[0]}", *ARGV[1..-1]
        end
      end
    end
  end

It even almost works, except if you try to pass arguments to the external command, which will give you "too many arguments" or "unrecognized option" because clamp tries to parse them.

Invalid @type symbol conversion error

So, this is not a bug in clamp. That's just me being inattentive about the type param.

definition.rb:70:in `+': can't convert Symbol into String (TypeError)

The source was simply a stray :bool where a :flag should have been.

Anyway. Not big with Ruby, so would it make sense to adapt Clamp::Option::Definition#help_lhs nevertheless with:

    lhs += " " + type unless unless type.is_a? Symbol

in place of just flag? probing?

Strange behavior of attribute writers

Not sure if I'm crazy or if this is doing the wrong thing:

require 'clamp'

class WeirdCommand < Clamp::Command
  DEFAULT_FOO = 'fo0'

  parameter 'FOO', 'The foo', required: false

  def execute
    unless foo
      foo = DEFAULT_FOO         # Fails
      # @foo = DEFAULT_FOO      # Works
    end

    bar(foo)
  end

  def bar(f)
    puts %(The foo is "#{f}")
  end
end

RSpec.describe WeirdCommand do
  subject(:weird) do
    described_class.new('weird', {})
      .tap { |c| c.parse(args) }
  end
  let(:args) { [] }

  describe '#execute' do
    context 'without foo' do
      it 'bars the foo' do
        expect(subject).to receive(:bar).with(described_class::DEFAULT_FOO)
        subject.execute
      end
    end

    context 'with foo' do
      let(:foo_arg) { 'quux' }
      let(:args) { [foo_arg] }

      it 'bars the foo' do
        expect(subject).to receive(:bar).with(foo_arg)
        subject.execute
      end
    end
  end
end

Running spec:

$ rspec test_case.rb --format doc --color

WeirdCommand
  #execute
    without foo
      bars the foo
    with foo
      bars the foo (FAILED - 1)

Failures:

  1) WeirdCommand#execute with foo bars the foo
     Failure/Error: bar(foo)
     
       #<WeirdCommand:0x007fbd5391f178 @invocation_path="weird", @context={}, @remaining_arguments=[], @foo="quux"> received :bar with unexpected arguments
         expected: ("quux")
              got: (nil)
       Diff:
       @@ -1,2 +1,2 @@
       -["quux"]
       +[nil]
       
     # ./test_case.rb:13:in `execute'
     # ./test_case.rb:42:in `block (4 levels) in <top (required)>'

Finished in 0.02258 seconds (files took 0.12948 seconds to load)
2 examples, 1 failure

Failed examples:

rspec ./test_case.rb:40 # WeirdCommand#execute with foo bars the foo

The behavior of the command seems to be affected by a call to foo=() when that line should not even be reached, i.e., when foo is passed as a parameter (the unless block should not run, but foo is set to nil). Replace the call with a direct @foo = and the issue goes away.

I've been staring at this for a while and I don't see how it's possible. Please tell me what I'm doing wrong.

required:true breaks attribute writer functionality

This Works

#! /usr/bin/env ruby

require "clamp"

Clamp do
  option %w(--foo), 'FOO[:THING]',
         'A fooy thing',
         attribute_name: :fooilicious

  def fooilicious=(thing)
    @my_att = thing
  end

  def execute
    puts @my_att
  end
end

Output

$ ./required-setter-overwritten --foo bar
bar

This Fails

#! /usr/bin/env ruby

require "clamp"

Clamp do
  option %w(--foo), 'FOO[:THING]',
         'A fooy thing',
         attribute_name: :fooilicious,
+        required: true

  def fooilicious=(thing)
    @my_att = thing
  end

  def execute
    puts @my_att
  end
end

Output

$ ./required-setter-overwritten --foo bar
ERROR: option '--foo' is required

See: 'required-setter-overwritten --help'

Hidden subcommands

It would be nice to be able to hide subcommands from help.

Something like this:

if plugin_installed?("foo")
  subcommand "foo", "Do fooey things", FooCommand
else
  subcommand "foo", "Do fooey things", hidden:true do
    puts "Install plugin foo first by running: gem install foo-plugin"
  end
end

Or perhaps allow this:

Clamp do
  parameter "[SUBCMD]", "Sub command", hidden: true, required: false

  subcommand "foo", "Do fooey things", FooCommand

  def execute
    if known_plugins.include?(subcmd)
      abort "Install plugin #{subcmd} first by running gem install #{subcmd}-plugin"
    end
  end
end

OR:

Clamp do
  def subcommand_missing(name)
    abort "Install plugin #{name} first by running gem install #{name}-plugin"
  end
end

1.2.0 makes multivalued params always required

Setting required: false does not help:

require 'clamp'

Clamp do

  option ['--tag', '-t'], 'TAG', "Description", multivalued: true, required: false

  def execute
    puts tag_list.inspect
  end
end
$ ruby test_command.rb
ERROR: option '--tag' is required

See: 'test_command.rb --help'

Adding default: [] makes it fail with Specifying a :default value with :required doesn't make sense (ArgumentError).

"required" options

I treat what clamp calls an 'option' simply as a "named parameter" much as you would call a function with named parameters. I know this is is perhaps contentious, but hear me out :)

I would like to make the case that, while "option" is perhaps a poor choice of words, having a "required named parameter" (or 'option' in clamp's terms) is a very common thing.

Python's "optparse" says "the phrase “required option” is self-contradictory in English". Seems like clamp follows this definition as well.

But, if for a moment, you replace the word "option" with another word like "setting" or "flag" or "configuration" or "tunable" then the "required option is contradictory" dogma completely disappears while leaving the remaining functionality of clamp unscathed.

The primary differences between an "option" and a "parameter" in Clamp appear to be:

  • options are named, unordered, and always optional
  • parameters are unnamed, order-dependent, and sometimes optional.

In some applications, I have "required settings" that are quite numerous, and having to implement these as parameters (due to their 'requirable' property) is awkward once you get beyond more than 3 or 4 required settings. I would much rather use the named and unordered nature of Clamp's "option" feature while still making them required.

I would like to specify:

option "--foo", "FOO", "This is the foo", :required => true

Benefits here are:

  • that I can automatically have "--foo is required" appear in the usage output
  • without writing extra code, I can enforce that a given setting is present.

Thoughts? I am willing to provide patches to implement this, but I wanted to propose the solution before implementing to get feedback :)

Parameters implicit default value is always a new object

This is totally weird! Hah!

Example program:

require "clamp"
class Foo < Clamp::Command
  parameter "[ARGS] ...", "Hello", :attribute_name => :args
  def execute
    p args.object_id
    p args.object_id
    p args.object_id
  end
end

Foo.run(ARGV)

When run, this outputs 3 different object IDs for args. I traced this through the code, and it seems to be related to the implicit default parameter empty array.

If I define the parameter as:

parameter "[ARGS] ...", "Hello", :attribute_name => :args, :default => []

Then the object id is always the same.


Run 1 (no :default):

% ruby test.rb
70259856025040
70259856024820
70259856024660

Run 2 (with :default => [])

% ruby test.rb
70194689979260
70194689979260
70194689979260

This is easy to workaround. I found this bug while helping someone with an fpm bug. The bug is that, in fpm, we try to set some default parameters in certain cases, and did so with args << ... which doesn't work because args returns a new object every time, thus the attempt to set the default didn't work! Computers! :)

Related fpm code: https://github.com/jordansissel/fpm/blob/v1.3.3/lib/fpm/command.rb#L276

Mandatory options are not enforced

The following should abort with a meaningful error message :

require 'rubygems'
require 'clamp'

class RunCommand < Clamp::Command
  option "--test", "MANDATORY", "a mandatory thingie"

  def execute
    puts test.nil? ? "FAILED" : "OK"
  end
end

RunCommand.run

Instead, it displays "FAILED".
This was tested with clamp-0.2.1

Allow command aliases

It would be nice, especially for subcommands, to be able to specify an alias. For example, rails server has an alias of rails s, rails console has an alias of rails c, and so on.

support for exit codes

Hi,

Suggestion: It would be nice if the run method could call Kernel#exit with the executereturn value if not 0 or have another rescue clause just for doing exit(1) on error.

This would allow not doing explicit exit(1) in a command and be able to still use the command programmatically in another context than command line.

Option validation block can read wrong values of other options

Lets have two options

option %w[-t --template], 'TEMPLATE_VM', 'Template VM name.', 
       default: :katello_base do |template|
  find_template template
end

def default_template
  find_template :katello_base
end

option %w[-s --snapshot], 'SNAPSHOT', 
       'Template snapshot name.', required: true do |snapshot|
  template.snapshots.include?(snapshot) or
      raise ArgumentError, "'#{snapshot}' not found between: #{template.snapshots.join(', ')}"
  snapshot
end

When --snapshot s --template a_temp is called it this order then snapshot is validated against default value of template instead against passed value 'a_temp'.

Would it be possible to defer evaluation of validation blocks and trigger it when accessed from other validation block to get always the correct value?


BTW 👏 Clamp has very nice design.

Add setter for :multivalued => true option

It would make testing easier if :multivalued options could also define a setter of the form "#{:attribute_name}list=". The only write method defined currently for options with :multivalued => true is "append_to#{:attribute_name}_list" which only allow adding to the list.

I'd like to be able to do:

class MyClamp < Clamp::Command
   option "--my-option", "a multi-valued option", :multivalued => true
end

# In tests
c = MyClamp.new("")
c.my_option_list = %w{Values for testing}

Now I have to do:

c = MyClamp.new("")
%w{Values for testing}.each {|v| c.append_to_my_option_list(c) }

Is it possible to define parameters in module?

Thanks for your work on this project mdub! I really like it. Question: Is it possible to share parameters via modules. I've tried the following but the parameter doesn't appear in the help for my command nor does its attribute get set. What am I missing?

module SharedParameters
  extend Clamp::Parameter::Declaration
  parameter "NAME", "name of thing"
  def thing
     System.get(name).status 
  end
end

class StatusCommand
  include ShareParameters
  def execute
     puts thing
  end 
end

class MainCommand < Clamp::Command
  subcommand "status", "show status of thing", StatusCommand
end

Parameter ARGF like functionality

With current Clamp I often find it a bit complicated to handle input from either STDIN or a filename passed as parameter.

(Even more so if you try to mix in a default value for the filename (which is quite usual nowadays in many apps, if you have some Dockerfile, docker-compose.yml, Whateverfile in your current dir, it will be used by default if you omit the parameter).)

I believe Ruby's ARGF was made because it's such a common thing you have to deal with when making command line apps.

So, wouldn't it be super fantastic if you could do this:

Clamp do
  parameter '[FILE]', "File path", default: 'config.yml', argf: true
end

The value of file would then be either:

  1. File.read(file) if file
  2. $stdin.read unless $stdin.tty?
  3. File.read('config.yml')
  4. nil

(maybe it should signal_usage_error if param is given and there's something coming from stdin)

This would work a bit like the ruby ARGF:

# argf.rb
puts ARGF.read

$ ruby argf.rb file.txt
file.txt content

$ echo "hello" | ruby argf.rb
hello

# This I don't quite like about ARGF for my usecases because it's super difficult
# to separate the files:
$ ruby argf.rb file.txt file2.txt file3.txt
file.txt content
file2.txt content
file3.txt content

I have no idea if Ruby's ARGF can be used at all together with Clamp, probably not. Also it has some special abilities like the in place editing -i.

Another variation of the same type of scenario, but more easy to do with current clamp is:

Clamp do
  parameter '[FILE ...]', "File path"

  def default_file_list
     if $stdin.tty? #  perhaps something more elegant for checking is available?
       Array.new
     else
       $stdin.read.split(/\s+/)
     end
   end
end

Now you can do:

$ command x.txt y.txt z.txt
# or: (without using "xargs")
$ ls *.txt | command 

Not too difficult, but maybe this too could be handled by Clamp for you:

Clamp do
  parameter '[FILE ...]', "File path", default: 'file.txt', stdin: true
end

For the sake of noobs, could you please

I have not used any CLI tool like Clamp, Thor etc before. Forgive me if I sound really off the scale.

Its taken me three days to get my head around what and how Clamp works. The most part of it at trying to run the scripts in the terminal. I am still having problems reason I can't post any hints right now, but I will when I get going.

For the sake of newbies, could someone update the README with some directions on how to run your scripts in Clamp. There is not much said about it here. Thanks.

Included parameter/option defaults do not work as expected

Not necessarily a bug, but something of an annoyance.

This works as expected and documented:

class FooCommand < Clamp::Command

  parameter "[FOO]", "Foo"

  def default_foo
    "bar"
  end

  def execute
    puts foo
  end
end

FooCommand.run

But this does not:

module CommonParam
  def self.included(where)
    where.parameter "[FOO]", "Foo"
  end

  def default_foo
    "bar"
  end
end

class FooCommand < Clamp::Command

  include CommonParam

  def execute
    puts foo
  end
end

FooCommand.run

The default_foo will never be called.

I believe this is because the default_method is defined during declaration and not "on the spot".

A workaround like this can be used:

module CommonParam

  def self.included(where)
    where.prepend InstanceMethods
    where.parameter "[FOO]", "Foo"
  end

  module InstanceMethods
    def default_foo
      "bar"
    end
  end
end

class FooCommand < Clamp::Command

  include CommonParam

  def execute
    puts foo
  end
end

FooCommand.run

subcommands should be optional

I'm using clamp 0.1.8 and when i create a subcommand it stops the flag -v or --version that i added from working.

It's expecting a subcommand : ERROR: parameter 'SUBCOMMAND': no value provided

Cannot include attribute writers from a module

Expected Behavior

Can include an attribute writer, like I would be able to do in other ruby situations, and have clamp use that method

Actual Behavior

Clamp doesn't see that the method exists at all, and it's never called

Repro Steps

#! /usr/bin/env ruby

require "clamp"

module Foo
  def fooilicious=(thing)
    @my_att = thing
    @fooilicious = thing
  end
end

class Bar < Clamp::Command
  option %w(--foo), 'FOO[:THING]', 'A fooy thing',
  attribute_name: :fooilicious

  include Foo

  def execute
    puts "executing"
    puts @my_att
  end
end

Bar.run

Workaround

#! /usr/bin/env ruby

require "clamp"

- module Foo
-  def fooilicious=(thing)
-    @fooilicious = thing
-  end
-end

class Bar < Clamp::Command
  option %w(--foo), 'FOO[:THING]', 'A fooy thing',
  attribute_name: :fooilicious

-  include Foo
+ def fooilicious=(thing)
+   @fooilicious = thing
+ end

  def execute
    puts "executing"
    puts @fooilicious
  end
end

Bar.run

This workaround is not ideal because I have several commands that have similar behavior and I don't want to have command inheritance.

No (automatic) indicator of required/optional option help

Default behavior:

option '--path', 'PATH', "File path", required: false

$ cmd --help
  --path PATH  File path

With required: true it looks the same:

option '--path', 'PATH', "File path", required: true

$ cmd --help
  --path PATH  File path

You can use [] around it, but that makes it look like the option param is optional and with just --path it would act as a flag:

option '--path', '[PATH]', "File path"

$ cmd --help
  --path [PATH]  File path

You can combine [] and required: true:

option '--path', '[PATH]', "File path", required: true

$ cmd --help
  --path [PATH]  File path

You can also use <>:

option '--path', '<PATH>', "File path", required: false

$ cmd --help
  --path <PATH>  File path

Maybe it should be something like:

option '--path1', 'PATH', "File path", required: false
option '--path2', 'PATH', "File path", required: true

$ cmd --help
  [--path1 <PATH>]  File 1 path
   --path2 <PATH>   File 2 path

setting boolean/flag values

Use case: I have an option I default to enabled (on) that I want folks to be able to disable, but semantically defining a "--no-foo" flag seems awkward especially with the negatives I end up using in the help string.

If I define a flag:

option "--foo", :flag, "...", :default => true

I'd like to be able to accept --no-foo or even --foo=(no|false|off...)

Possible? Would you accept patches adding this if it's not available already?

option arg validation question.

I need to implement the following option, which allows one of three possible values (text, json, yaml):

option "--format", "FORMAT", "Output format"

How would I do the input validation against an array of valid strings? (And does the validation update the displayed usage text, or do I need to manually edit that text?)

I tried something like the following with no luck:

option "--format", "FORMAT", "Output format", :default => "json" do |s|
  ['json', 'yaml', 'text'].include? s
end

Oh one other thing, in subclasses I do need to override the default, and allowed FORMATs. Perhaps that might be interfering?

Thanks,
Brian

Release new version

There are some unreleased neat patches :-) Could you release minor version?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.