mdub / clamp Goto Github PK
View Code? Open in Web Editor NEWa Ruby command-line application framework
License: MIT License
a Ruby command-line application framework
License: MIT License
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)
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.
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.
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 == "--"
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.
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.
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] ...
$ _
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.
option ["-u", "--user"], "USER", "Foreman user"
...
puts user
"mycommand -u bgupta"
print correctly bgupta
"mycommand -ubgupta"
prints -ubgupta
Unix convention should allow this.
I have flags I wish to deprecate, and I'd love it clamp could help with this.
Idea:
:deprecated
set will not appear in the usage output (ie; --help
):deprecated => "The reason this is deprecated and some other notes"
which can be displayed to users however the application developer decides.[1]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 userRough 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 :)
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>'
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
Do we have something using which we can hide some options? so they do exist but when we do --help they should not print it.
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
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.
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.
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.
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.
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.
# 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.
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
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.
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:
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.
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
option "--foo-bar", "FOO", "Some foo", :env => "FOO"
The above would allow FOO=value
to set the value for --foo-bar.
Some hints should be given in the --help usage to indicate that environment variables can be used.
When given both env and flag values, the flag value should win.
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']
?
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.
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?
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.
#! /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
#! /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'
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
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)
.
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:
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:
Thoughts? I am willing to provide patches to implement this, but I wanted to propose the solution before implementing to get feedback :)
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
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
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.
Hi,
Suggestion: It would be nice if the run
method could call Kernel#exit
with the execute
return 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.
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.
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) }
I'm still looking for a clean way of including sub-commands from separate files (be it from modules or classes).
And NB. the help shows options twice when using sub-commands.
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
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:
File.read(file) if file
$stdin.read unless $stdin.tty?
File.read('config.yml')
(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
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.
Not necessarily a bug, but something of an annoyance.
class FooCommand < Clamp::Command
parameter "[FOO]", "Foo"
def default_foo
"bar"
end
def execute
puts foo
end
end
FooCommand.run
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".
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
It would be really great to build script which will automatically generate autocompletion files.
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
Can include an attribute writer, like I would be able to do in other ruby situations, and have clamp use that method
Clamp doesn't see that the method exists at all, and it's never called
#! /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
#! /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.
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
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?
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
There are some unreleased neat patches :-) Could you release minor version?
In exploration for #50, I was poking around and noticed that clamp uses rspec2 should
syntax. Any interest in upgrading to rspec3 expect()
syntax?
I am willing to do the work for this.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.