Comments (15)
The block passed to #option
(and variants) is currently executed when the corresponding option is found, but I am not sure that it can be repurposed (in a backwards-compatible way) to verify and transform the value. It can break existing usage that relies on the option value not to change.
I believe that there are three common actions that Cri could/should support:
-
run a block (useful for e.g.
--help
,--version
; currently supported) -
validate (useful for e.g. verifying that a port number is an integer in the 0ā65535 range; not currently explicitly supported)
-
transform (useful for e.g. transforming input into an integer; not currently explicitly supported)
Some use cases:
- Specify any number
- Specify a number with requirements (e.g.
--port
>0 and <65535) - Specify a string with requirements (e.g.
--seed
length ā„ 20)
I am unsure in which order validation and transformation should happen. Validate-then-transform, or transform-then-validate?
Perhaps a transformer that can raise an exception, followed by additional validation, could work:
option :p, :port, 'Specify port to listen on',
transform: ->(n) { Integer(n) },
validate: ->(n) { (1..65535).include?(n) }
option :c, :'config-file', 'Specify configuration file',
transform: ->(fn) { ConfigFile.read(fn) }
validate: &:valid?
Any callable would be supported (not just procs), so this would also work:
ā¦
transform: method(:Integer)
or perhaps even
class IntegerTransformer
def initialize(min: nil, max: nil)
@min = min
@max = max
end
def validate(n)
Integer(n).tap do |parsed_n|
(min ? parsed_n >= min : true) &&
(min ? parsed_n >= min : true)
end
end
end
ā¦
transform: IntegerTransformer.new
ā¦ though at this point it might be worth looking into already-existing validation solutions out there (dry-validation comes to mind, but it might be overkill.)
As for argument validation, here are some ideas:
-
Fixed number:
num_arguments 1
-
Range:
num_arguments 1..3
-
Callable:
num_arguments -> (n) { n > 3 || n % 5 == 1 }
i.e. essentially anything that implements #===
. WDYT?
from cri.
FYI, I have deprecated #optional
and #required
as their names are far too confusing.
from cri.
@eguzki Iāve implemented transform:
support in #68. Can you take a look and check whether it makes sense, and fits your use case?
(Option validation is not explicitly implemented yet, although transform:
could be reused for that purpose for now. Argument validation is pending too.)
from cri.
The option value transformation is implemented and released in Cri 2.11. Can you take a look and confirm that it fits your needs?
from cri.
After, quick review, it seems this change allow us to "inject" configuration objects into options
object. So every command can get configuration object instead of getting config file and create themselves configuration object.
I have not checked yet actually using this new feature. Will do it ASAP.
from cri.
I think this a good progress š
Still missing the issue about validation and positional argument validation ;)
from cri.
After, quick review, it seems this change allow us to "inject" configuration objects into options object. So every command can get configuration object instead of getting config file and create themselves configuration object.
I believe the transformers should remain stateless; the configuration object is something you could construct afterwards, e.g.
option :p, :port, 'set port', argument: :required, transform: method(:Integer)
# options is a hash, e.g. { port: 12345 }
config = Config.new(options)
As for argument validation: itād be nice to be able to name them, e.g. arguments[:filename]
rather than arguments[0]
.
Some ideas for the API:
-
Example:
arguments [:host, :port]
Two positional arguments.
-
Example:
argument :host argument :port
Two positional arguments. This API might not make it clear enough that the list of arguments is ordered, however.
-
Example:
argument 0 => :host argument 1 => :port
Two positional arguments. Perhaps a bit verbose.
-
Example:
argument host: 0 argument port: 1
Two positional arguments. Perhaps a bit verbose and/or confusing.
-
Example:
argument host: 0 argument port: 1 argument filenames: 2..-1
Several positional arguments, with
:filenames
being a list of arguments rather than a single one.Note that the range
2..-1
does not actually make sense in Ruby (itās an empty range), but thereās no easy way to express an open-ended range in Ruby 2.5. Itās possible to use2..Float::INFINITY
, but thatās icky. This will change in Ruby 2.6, which will allow ranges like2..
which represents an open-ended range from 2 on. -
Example:
argument :host, pos: 0 argument :port, pos: 1 argument :filenames, from: 2
Alternative to the above, with a cleaner syntax for open-ended ranges.
More thoughts:
- Are there other requirements for argument validation? Other nice-to-haves?
parameter
orargument
?
from cri.
The same transformation would be used for arguments, e.g.
argument :ports, from: 0, transform: method(:Integer)
ā¦ to specify a list of ports and convert them to integers.
from cri.
I believe the transformers should remain stateless; the configuration object is something you could construct afterwards, e.g.
Makes sense. This is something very specific for our needs and will think about that. thnx.
WRT positional arguments, I would expect simple validation of existence. Any further support for transformation or keyword based access API is nice to have.
The most flexible way seems your last example
argument :ports, from: 0, transform: method(:Integer)
Allows extension for validation or running a block. I would call it argument
for consistency's sake.
from cri.
As the MVP, Iād like to implement something a bit simpler:
param :port, transform: method(:Integer)
-
I renamed
argument
toparam
. An argument is a value, e.g.8080
, while a parameter is a name, e.g.port
. I like to make that distinction. -
transform:
(same as before) -
No more
from
/pos
ā at least not for the MVP. For now, donāt support a variable number of arguments, and assume that the arguments are given in exactly the order ofparam
calls.
Additionally, if no params are specified, Cri will assume that there is no argument validation needed. The no_params
directive will tell Cri to do validation and check that there are indeed no arguments passed.
Later on:
- Implement
from:
andpos:
(thoughat:
might be a better name thanpos:
) - Implement
default:
from cri.
Very basic parameter checking is now implemented in master
. It includes (for now) only parameters without support for transform
, from
/at
, or default
ā just the name.
For example:
param :source
param :target
% ./sync . hydra:/srv/data
Syncingā¦
%
% ./sync hydra:/srv/data
sync: incorrect number of arguments given: expected 2, but got 1
%
from cri.
The released version 2.12.0 has the basic param
support. Further improvements are in the pipeline.
from cri.
Cri v2.13.0 has support for explicitly specifying zero parameters using no_params
, e.g.
command = Cri::Command.define do
name 'reset'
usage 'reset'
summary 'resets the site'
description 'ā¦'
no_params
run do |opts, args, cmd|
puts "Resettingā¦"
end
end
% my-tool reset x
reset: incorrect number of arguments given: expected 0, but got 1
% my-tool reset
Resettingā¦
%
from cri.
The just-released Cri v2.15.0 has parameter transformation support, e.g.
param :id, transform: ->(a) { a.upcase }
When given abc123
as argument, arguments[:id]
will be ABC123
.
This can also be used for validation (raise an exception in the transform block).
from cri.
Iāve created #74 (varargs) and #75 (params with default values) to track non-implemented features. Both feature requests have low priority at the moment, but if you have use cases where they could be useful, do let me know.
I believe that, apart from these, thereās nothing left that needs to be implemented, so Iāll close this ticket.
Thanks for your valuable feedback! I think it has made Cri a considerably better product, and Iāve also benefited from the new features that Iāve added to it.
from cri.
Related Issues (20)
- Feature: Arbitrary arguments HOT 3
- Is the argument: option required when using multiple: true HOT 3
- uninitialized constant Cri::Error HOT 2
- Options propagation on subcommand tree HOT 4
- iterating over arguments fails since 2.12 HOT 2
- colored breaks awesome_print HOT 3
- Release 2.15.4 breaks semver HOT 10
- Commands without `summary` does not even show command name HOT 2
- Forbidden optional parameters set to ` nil` when not present in command line HOT 15
- Version 2.15.7 causes runtime error HOT 1
- Options from one command appear in other commands
- Consider making it clearer that --verbose is an option for the help subcommand itself HOT 4
- 2.15.8 breaks nanoc HOT 6
- Handler for command line parameters and option errors
- Platform colour detection uses outdated technique on Windows HOT 3
- Warning with ruby 2.7 HOT 3
- [Feature Request]: Change symbols with dashes/hyphens to underscores
- Add negatable long-form options HOT 1
- Enhance #param to allow :optional and a description
- make a new release? HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
š Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ā¤ļø Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from cri.