sprinkles-opts 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +4 -7
- data/.gitignore +3 -0
- data/README.md +39 -17
- data/Rakefile +6 -6
- data/lib/sprinkles/opts/version.rb +1 -1
- data/lib/sprinkles/opts.rb +506 -322
- data/sorbet/config +2 -1
- data/sorbet/rbi/annotations/.gitattributes +1 -0
- data/sorbet/rbi/annotations/minitest.rbi +116 -0
- data/sorbet/rbi/annotations/rainbow.rbi +269 -0
- data/sorbet/rbi/gems/.gitattributes +1 -0
- data/sorbet/rbi/gems/minitest@6.0.2.rbi +1524 -0
- data/sorbet/rbi/gems/rainbow@3.1.1.rbi +403 -0
- data/sorbet/tapioca/config.yml +13 -0
- data/sorbet/tapioca/require.rb +4 -0
- data/sprinkles-opts.gemspec +19 -19
- metadata +31 -34
- data/Gemfile.lock +0 -49
- data/sorbet/rbi/gems/minitest.rbi +0 -420
- data/sorbet/rbi/gems/rake.rbi +0 -644
- data/sorbet/rbi/hidden-definitions/errors.txt +0 -1223
- data/sorbet/rbi/hidden-definitions/hidden.rbi +0 -2303
- data/sorbet/rbi/sorbet-typed/lib/minitest/all/minitest.rbi +0 -108
- data/sorbet/rbi/sorbet-typed/lib/rake/all/rake.rbi +0 -645
- data/sorbet/rbi/todo.rbi +0 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9e4b137fd5adbc7abaf8dcf83d4129fb99762dd1312373c596be6894814085da
|
|
4
|
+
data.tar.gz: f2980f5b6f3abf8906049759ed1f1edeec7f3986bd57278670b75abb0fe9a2b2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 37b02710c9b1e6bf6189aeae192e7a805aba61cc0d6518b3fdc60e369cd145f723a22c1510bf53ab7197b322a9ecb48c657102a86deeca426ec254c6c074fa7c
|
|
7
|
+
data.tar.gz: ac3191f10b60d631f2a4e1e2cfafbca62ff87c4e9a9b06e66664fc8253aecf6acf8cafa835c65d75d09031a6baceeb6901292e58c452a804092d7a175dcc3743
|
data/.github/workflows/ruby.yml
CHANGED
|
@@ -19,18 +19,15 @@ jobs:
|
|
|
19
19
|
runs-on: ubuntu-latest
|
|
20
20
|
strategy:
|
|
21
21
|
matrix:
|
|
22
|
-
ruby-version: ['
|
|
22
|
+
ruby-version: ['3.0', '3.1', '3.2', '3.3', '4.0']
|
|
23
23
|
|
|
24
24
|
steps:
|
|
25
|
-
- uses: actions/checkout@
|
|
25
|
+
- uses: actions/checkout@v6
|
|
26
26
|
- name: Set up Ruby
|
|
27
|
-
|
|
28
|
-
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
|
29
|
-
# uses: ruby/setup-ruby@v1
|
|
30
|
-
uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e
|
|
27
|
+
uses: ruby/setup-ruby@v1
|
|
31
28
|
with:
|
|
32
29
|
ruby-version: ${{ matrix.ruby-version }}
|
|
33
|
-
bundler-cache: true
|
|
30
|
+
bundler-cache: true
|
|
34
31
|
- name: Run tests
|
|
35
32
|
run: bundle exec rake
|
|
36
33
|
- name: Typecheck
|
data/.gitignore
CHANGED
data/README.md
CHANGED
|
@@ -5,16 +5,16 @@ argument parsing.
|
|
|
5
5
|
|
|
6
6
|
## Basic usage
|
|
7
7
|
|
|
8
|
-
Create a class that is a subclass of `Spinkles::Opts::GetOpt`. Define fields and their types with `const`, analogously to [how you would with `T::Struct`](), but those fields can have `short:` and `long:` options that map to the
|
|
8
|
+
Create a class that is a subclass of `Spinkles::Opts::GetOpt`. Define fields and their types with `const`, analogously to [how you would with `T::Struct`](https://sorbet.org/docs/tstruct), but those fields can have `short:` and `long:` options that map to the comman-line options used to provide them. You may also provide a value for `program_name` by overriding an abstract method, which will otherwise default to `$PROGRAM_NAME`:
|
|
9
9
|
|
|
10
10
|
```ruby
|
|
11
11
|
class MyOptions < Sprinkles::Opts::GetOpt
|
|
12
|
-
sig {override.returns(String)}
|
|
13
|
-
def self.program_name
|
|
12
|
+
sig { override.returns(String) }
|
|
13
|
+
def self.program_name = "my-program"
|
|
14
14
|
|
|
15
|
-
const :input, String, short:
|
|
16
|
-
const :num_iterations, Integer, short:
|
|
17
|
-
const :verbose, T::Boolean, short:
|
|
15
|
+
const :input, String, short: "i", long: "input"
|
|
16
|
+
const :num_iterations, Integer, short: "n", placeholder: "N"
|
|
17
|
+
const :verbose, T::Boolean, short: "v", long: "verbose", factory: -> { false }
|
|
18
18
|
end
|
|
19
19
|
```
|
|
20
20
|
|
|
@@ -22,7 +22,7 @@ You can then call `MyOptions.parse(ARGV)` in order to get a value of type `MyOpt
|
|
|
22
22
|
|
|
23
23
|
```ruby
|
|
24
24
|
opts = MyOptions.parse(%w{-i foo -n 8 --verbose})
|
|
25
|
-
assert_equal(
|
|
25
|
+
assert_equal("foo", opts.input)
|
|
26
26
|
assert_equal(8, opts.num_iterations)
|
|
27
27
|
assert_equal(true, opts.verbose)
|
|
28
28
|
```
|
|
@@ -33,16 +33,13 @@ Fields without a `short:` or `long:` parameter will be understood to be position
|
|
|
33
33
|
|
|
34
34
|
```ruby
|
|
35
35
|
class PosOptions < Sprinkles::Opts::GetOpt
|
|
36
|
-
sig {override.returns(String)}
|
|
37
|
-
def self.program_name; "positional-options"; end
|
|
38
|
-
|
|
39
36
|
const :source, String
|
|
40
37
|
const :destination, String
|
|
41
38
|
end
|
|
42
39
|
|
|
43
40
|
opts = PosOptions.parse(%w{this that})
|
|
44
|
-
assert_equal('this
|
|
45
|
-
assert_equal(that, opts.destination)
|
|
41
|
+
assert_equal("'this", opts.source)
|
|
42
|
+
assert_equal("that", opts.destination)
|
|
46
43
|
```
|
|
47
44
|
|
|
48
45
|
Parsing will fail and exit the program with a usage statement if either too many or too few positional parameters are provided.
|
|
@@ -55,11 +52,13 @@ opts = PosOptions.parse(%w{this})
|
|
|
55
52
|
# -h, --help Prints this help
|
|
56
53
|
```
|
|
57
54
|
|
|
55
|
+
In addition to only providing `const` (and therefore not allowing writer methods for the relevant fields) the resulting objects has been frozen and cannot be mutated at all, even by other methods. The intended use of a `GetOpt` subclass is as a pure data container; any other logic should be implemented as a wrapper around the container.
|
|
56
|
+
|
|
58
57
|
## Optional arguments
|
|
59
58
|
|
|
60
59
|
There are two ways of making arguments optional:
|
|
61
60
|
- A field whose type is marked as `T.nilable` will implicitly be initialized as `nil` if it is not provided.
|
|
62
|
-
- A field can have a `factory:` which should be a
|
|
61
|
+
- A field can have a `factory:` which should be a proc that will be called to initialize the field if the argument is not provided.
|
|
63
62
|
|
|
64
63
|
Fields that are not `T.nilable` and do not have a `factory:` must be provided when parsing arguments.
|
|
65
64
|
|
|
@@ -67,9 +66,6 @@ For _positional_ arguments, there's currently an extra restriction: all mandator
|
|
|
67
66
|
|
|
68
67
|
```ruby
|
|
69
68
|
class PosOptions < Sprinkles::Opts::GetOpt
|
|
70
|
-
sig {override.returns(String)}
|
|
71
|
-
def self.program_name; "positional-options"; end
|
|
72
|
-
|
|
73
69
|
const :a, String
|
|
74
70
|
const :b, T.nilable(String)
|
|
75
71
|
const :c, T.nilable(String)
|
|
@@ -117,6 +113,33 @@ PosArray.parse(%w{-a 5}) # a is [5]
|
|
|
117
113
|
PosArray.parse(%w{-a 22 -a 33}) # a is [22, 33]
|
|
118
114
|
```
|
|
119
115
|
|
|
116
|
+
## Other data types
|
|
117
|
+
|
|
118
|
+
Not all types are valid, and invalid types will cause a load-time exception. Basic Ruby types including `String`, `Symbol`, `Integer`, and `Float` are all valid field types, as well as a few gem-provided types like `Date` and `URI`. You can also provide a Sorbet `T::Enum` type and it will deserialize it from the provided value if possible.
|
|
119
|
+
|
|
120
|
+
Additionally, you can use a `T.any` of multiple types, and arguments will use successive attempts at parsing, falling back on the later types if the provided string cannot be parsed as an earlier type. One note is that it will attempt parsing in the order of the `T.any`, but some types will always parse correctly: for example, if you have a field whose type is `T.any(String, URI)`, then the field will never actually be set to a `URI` because it's impossible to provide an invalid `String` value.
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
class TrafficLight < T::Enum
|
|
124
|
+
enums do
|
|
125
|
+
Green = new("green")
|
|
126
|
+
Yellow = new("yellow")
|
|
127
|
+
Red = new("red")
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
class OtherValues < Sprinkles::Opts::GetOpt
|
|
132
|
+
const :num, Integer, short: "n"
|
|
133
|
+
const :date, Date, short: "d"
|
|
134
|
+
const :tl, TrafficLight, short: "t"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
OtherValues.parse(%w{-d 2015-07-01 -n 33 -t yellow})
|
|
138
|
+
# num is 22
|
|
139
|
+
# date is Date.new(2015, 07, 01)
|
|
140
|
+
# tl is TrafficLight::Yellow
|
|
141
|
+
```
|
|
142
|
+
|
|
120
143
|
## Help text and descriptions
|
|
121
144
|
|
|
122
145
|
The option names `-h` and `--help` are reserved, and when they are provided the program will print a usage panel and exit:
|
|
@@ -131,7 +154,6 @@ Usage: my-program --input=VALUE -nN
|
|
|
131
154
|
|
|
132
155
|
Individual fields can customize their default placeholder text away from the default `VALUE` using the `placeholder:` argument, and can provide more extensive descriptions using the `description:` argument.
|
|
133
156
|
|
|
134
|
-
|
|
135
157
|
## Why sprinkles?
|
|
136
158
|
|
|
137
159
|
Well, because it's a Sorbet topping. I have other unfinished ideas for how to leverage Sorbet to write certain abstractions, and my thought was that it might be nice to put them in a common namespace.
|
data/Rakefile
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "bundler/gem_tasks"
|
|
2
|
+
require "rake/testtask"
|
|
3
3
|
|
|
4
4
|
Rake::TestTask.new(:test) do |t|
|
|
5
|
-
t.libs <<
|
|
6
|
-
t.libs <<
|
|
7
|
-
t.test_files = FileList[
|
|
5
|
+
t.libs << "test"
|
|
6
|
+
t.libs << "lib"
|
|
7
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
task
|
|
10
|
+
task(default: :test)
|