clamp 1.2.0.beta1 → 1.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.editorconfig +9 -0
- data/.gitignore +1 -1
- data/.rspec +1 -0
- data/.rubocop.yml +26 -19
- data/.travis.yml +3 -6
- data/CHANGES.md +17 -1
- data/Gemfile +8 -6
- data/Guardfile +3 -1
- data/README.md +36 -43
- data/Rakefile +8 -0
- data/clamp.gemspec +8 -6
- data/examples/admin +3 -2
- data/examples/defaulted +4 -3
- data/examples/flipflop +1 -0
- data/examples/fubar +1 -0
- data/examples/gitdown +2 -1
- data/examples/scoop +3 -2
- data/examples/speak +3 -2
- data/examples/subcommand_missing +1 -0
- data/examples/word +1 -0
- data/lib/clamp.rb +3 -1
- data/lib/clamp/attribute/declaration.rb +5 -0
- data/lib/clamp/attribute/definition.rb +25 -11
- data/lib/clamp/attribute/instance.rb +25 -3
- data/lib/clamp/command.rb +9 -1
- data/lib/clamp/errors.rb +7 -3
- data/lib/clamp/help.rb +38 -17
- data/lib/clamp/messages.rb +25 -15
- data/lib/clamp/option/declaration.rb +5 -1
- data/lib/clamp/option/definition.rb +9 -3
- data/lib/clamp/option/parsing.rb +38 -43
- data/lib/clamp/parameter/declaration.rb +4 -0
- data/lib/clamp/parameter/definition.rb +9 -3
- data/lib/clamp/parameter/parsing.rb +5 -1
- data/lib/clamp/subcommand/declaration.rb +17 -15
- data/lib/clamp/subcommand/definition.rb +5 -6
- data/lib/clamp/subcommand/execution.rb +12 -1
- data/lib/clamp/subcommand/parsing.rb +4 -0
- data/lib/clamp/truthy.rb +4 -2
- data/lib/clamp/version.rb +3 -1
- data/spec/clamp/command_group_spec.rb +29 -11
- data/spec/clamp/command_spec.rb +130 -48
- data/spec/clamp/help_spec.rb +63 -0
- data/spec/clamp/messages_spec.rb +5 -4
- data/spec/clamp/option/definition_spec.rb +13 -11
- data/spec/clamp/option_module_spec.rb +3 -1
- data/spec/clamp/option_reordering_spec.rb +6 -4
- data/spec/clamp/parameter/definition_spec.rb +14 -12
- data/spec/spec_helper.rb +3 -3
- metadata +9 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c164c63137c57502ad15e11ab471b2607661868298e5330a8a4961dfea629537
|
4
|
+
data.tar.gz: 297e0abb1ead811d567eea7e668a91ba7c99654657a358016abb5cdbfd13168d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3773151ffa64b908d26c569877bb27704bf68efe658d9b6ebd48c009bcbc1a4d0a3fce2c4f16a02ed379e126dd436ef018ff1eac56334fcc8f467402fad6c995
|
7
|
+
data.tar.gz: 8104ea9b1d9a42669234cb0d82cc441efd7a6f94ed5cba542a502ae1cb2797d977530a07bbe649d98dab551e9078b434711e091e9d0b3716799522c04436d8c9
|
data/.editorconfig
ADDED
data/.gitignore
CHANGED
data/.rspec
CHANGED
data/.rubocop.yml
CHANGED
@@ -1,17 +1,40 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.1
|
3
|
+
|
1
4
|
Eval:
|
2
5
|
Exclude:
|
3
6
|
- "Rakefile"
|
4
7
|
|
8
|
+
Layout/EmptyLinesAroundBlockBody:
|
9
|
+
Enabled: false
|
10
|
+
|
11
|
+
Layout/EmptyLinesAroundClassBody:
|
12
|
+
EnforcedStyle: empty_lines
|
13
|
+
|
14
|
+
Layout/EmptyLinesAroundModuleBody:
|
15
|
+
Enabled: false
|
16
|
+
|
5
17
|
Metrics/AbcSize:
|
6
18
|
Enabled: false
|
7
19
|
|
20
|
+
Metrics/BlockLength:
|
21
|
+
Exclude:
|
22
|
+
- "spec/**/*"
|
23
|
+
|
8
24
|
Metrics/LineLength:
|
9
25
|
Max: 120
|
10
26
|
|
11
27
|
Metrics/MethodLength:
|
12
28
|
Max: 30
|
13
29
|
|
14
|
-
|
30
|
+
Naming/AccessorMethodName:
|
31
|
+
Enabled: false
|
32
|
+
|
33
|
+
Naming/FileName:
|
34
|
+
Exclude:
|
35
|
+
- "bin/*"
|
36
|
+
|
37
|
+
Naming/PredicateName:
|
15
38
|
Enabled: false
|
16
39
|
|
17
40
|
Style/ClassAndModuleChildren:
|
@@ -22,35 +45,19 @@ Style/ClassAndModuleChildren:
|
|
22
45
|
Style/Documentation:
|
23
46
|
Exclude:
|
24
47
|
- "lib/**/version.rb"
|
48
|
+
- "examples/*"
|
25
49
|
- "spec/**/*"
|
26
50
|
|
27
|
-
Style/EmptyLinesAroundBlockBody:
|
28
|
-
Enabled: false
|
29
|
-
|
30
|
-
Style/EmptyLinesAroundClassBody:
|
31
|
-
EnforcedStyle: empty_lines
|
32
|
-
|
33
|
-
Style/EmptyLinesAroundModuleBody:
|
34
|
-
Enabled: false
|
35
|
-
|
36
51
|
Style/Encoding:
|
37
|
-
EnforcedStyle: when_needed
|
38
52
|
Enabled: true
|
39
53
|
|
40
|
-
Style/FileName:
|
41
|
-
Exclude:
|
42
|
-
- "bin/*"
|
43
|
-
|
44
|
-
Style/HashSyntax:
|
45
|
-
EnforcedStyle: hash_rockets
|
46
|
-
|
47
54
|
Style/Lambda:
|
48
55
|
Enabled: false
|
49
56
|
|
50
57
|
Style/NumericLiterals:
|
51
58
|
Enabled: false
|
52
59
|
|
53
|
-
Style/
|
60
|
+
Style/StderrPuts:
|
54
61
|
Enabled: false
|
55
62
|
|
56
63
|
Style/StringLiterals:
|
data/.travis.yml
CHANGED
data/CHANGES.md
CHANGED
@@ -1,6 +1,22 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
##
|
3
|
+
## 1.3.2 (2020-08-20)
|
4
|
+
|
5
|
+
* Fix Ruby warnings.
|
6
|
+
|
7
|
+
## 1.3.1 (2019-07-11)
|
8
|
+
|
9
|
+
* Choose a sensible column width in generated help, based on content.
|
10
|
+
* Fix issue#99: extraneous parameter names in subcommand help.
|
11
|
+
|
12
|
+
## 1.3.0 (2018-06-17)
|
13
|
+
|
14
|
+
* Add `.execute` DSL method.
|
15
|
+
* Append '(required)' to the description of required options.
|
16
|
+
* Fix issue#75: don't generate `default_XXX` method unless a default is specified.
|
17
|
+
* Fix issue#90: allow required options to be provided after subcommands.
|
18
|
+
|
19
|
+
## 1.2.0 (2018-02-12)
|
4
20
|
|
5
21
|
* Add option to `Clamp.allow_options_after_parameters`.
|
6
22
|
|
data/Gemfile
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
source "http://rubygems.org"
|
2
4
|
|
3
5
|
gemspec
|
4
6
|
|
5
7
|
group :development do
|
6
|
-
gem "guard-rspec", "~> 4.
|
7
|
-
gem "
|
8
|
-
gem "
|
9
|
-
gem "
|
8
|
+
gem "guard-rspec", "~> 4.7", require: false
|
9
|
+
gem "highline"
|
10
|
+
gem "listen", "~> 3.0"
|
11
|
+
gem "rake", "~> 12.3"
|
12
|
+
gem "rubocop", "~> 0.57.2", "<= 0.58", require: false
|
10
13
|
end
|
11
14
|
|
12
15
|
group :test do
|
13
|
-
gem "rspec", "~> 3.
|
14
|
-
gem "rr", "~> 1.2.0"
|
16
|
+
gem "rspec", "~> 3.7"
|
15
17
|
end
|
data/Guardfile
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# A sample Guardfile
|
2
4
|
# More info at https://github.com/guard/guard#readme
|
3
5
|
|
@@ -24,7 +26,7 @@
|
|
24
26
|
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
25
27
|
# * 'just' rspec: 'rspec'
|
26
28
|
|
27
|
-
guard :rspec, :
|
29
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
28
30
|
require "guard/rspec/dsl"
|
29
31
|
dsl = Guard::RSpec::Dsl.new(self)
|
30
32
|
|
data/README.md
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
Clamp
|
2
|
-
=====
|
1
|
+
# Clamp
|
3
2
|
|
4
3
|
[![Gem Version](https://badge.fury.io/rb/clamp.png)](http://badge.fury.io/rb/clamp)
|
5
4
|
[![Build Status](https://secure.travis-ci.org/mdub/clamp.png?branch=master)](http://travis-ci.org/mdub/clamp)
|
@@ -8,8 +7,7 @@ Clamp
|
|
8
7
|
|
9
8
|
It handles boring stuff like parsing the command-line, and generating help, so you can get on with making your command actually do stuff.
|
10
9
|
|
11
|
-
Not another one!
|
12
|
-
----------------
|
10
|
+
## Not another one!
|
13
11
|
|
14
12
|
Yeah, sorry. There are a bunch of existing command-line parsing libraries out there, and Clamp draws inspiration from a variety of sources, including [Thor], [optparse], and [Clip]. In the end, though, I wanted a slightly rounder wheel. (Although, Clamp has a _lot_ in common with Ara T. Howard's [main.rb]. Had I been aware of that project at the time, I might not have written Clamp.)
|
15
13
|
|
@@ -18,8 +16,7 @@ Yeah, sorry. There are a bunch of existing command-line parsing libraries out t
|
|
18
16
|
[Clip]: http://clip.rubyforge.org/
|
19
17
|
[main.rb]: https://github.com/ahoward/main
|
20
18
|
|
21
|
-
Quick Start
|
22
|
-
-----------
|
19
|
+
## Quick Start
|
23
20
|
|
24
21
|
A typical Clamp script looks like this:
|
25
22
|
|
@@ -29,11 +26,11 @@ require 'clamp'
|
|
29
26
|
Clamp do
|
30
27
|
|
31
28
|
option "--loud", :flag, "say it loud"
|
32
|
-
option ["-n", "--iterations"], "N", "say it N times", :
|
29
|
+
option ["-n", "--iterations"], "N", "say it N times", default: 1 do |s|
|
33
30
|
Integer(s)
|
34
31
|
end
|
35
32
|
|
36
|
-
parameter "WORDS ...", "the thing to say", :
|
33
|
+
parameter "WORDS ...", "the thing to say", attribute_name: :words
|
37
34
|
|
38
35
|
def execute
|
39
36
|
the_truth = words.join(" ")
|
@@ -54,11 +51,11 @@ require 'clamp'
|
|
54
51
|
class SpeakCommand < Clamp::Command
|
55
52
|
|
56
53
|
option "--loud", :flag, "say it loud"
|
57
|
-
option ["-n", "--iterations"], "N", "say it N times", :
|
54
|
+
option ["-n", "--iterations"], "N", "say it N times", default: 1 do |s|
|
58
55
|
Integer(s)
|
59
56
|
end
|
60
57
|
|
61
|
-
parameter "WORDS ...", "the thing to say", :
|
58
|
+
parameter "WORDS ...", "the thing to say", attribute_name: :words
|
62
59
|
|
63
60
|
def execute
|
64
61
|
the_truth = words.join(" ")
|
@@ -79,8 +76,7 @@ There are more examples demonstrating various features of Clamp [on Github][exam
|
|
79
76
|
|
80
77
|
[examples]: https://github.com/mdub/clamp/tree/master/examples
|
81
78
|
|
82
|
-
Declaring options
|
83
|
-
-----------------
|
79
|
+
## Declaring options
|
84
80
|
|
85
81
|
Options are declared using the `option` method. The three required arguments are:
|
86
82
|
|
@@ -105,7 +101,7 @@ end
|
|
105
101
|
If you don't like the inferred attribute name, you can override it:
|
106
102
|
|
107
103
|
```ruby
|
108
|
-
option "--type", "TYPE", "type of widget", :
|
104
|
+
option "--type", "TYPE", "type of widget", attribute_name: :widget_type
|
109
105
|
# to avoid clobbering Object#type
|
110
106
|
```
|
111
107
|
|
@@ -140,7 +136,7 @@ Clamp will handle both "`--force`" and "`--no-force`" options, setting the value
|
|
140
136
|
Although "required option" is an oxymoron, Clamp lets you mark an option as required, and will verify that a value is provided:
|
141
137
|
|
142
138
|
```ruby
|
143
|
-
option "--password", "PASSWORD", "the secret password", :
|
139
|
+
option "--password", "PASSWORD", "the secret password", required: true
|
144
140
|
```
|
145
141
|
|
146
142
|
Note that it makes no sense to mark a `:flag` option, or one with a `:default`, as `:required`.
|
@@ -150,7 +146,7 @@ Note that it makes no sense to mark a `:flag` option, or one with a `:default`,
|
|
150
146
|
Declaring an option "`:multivalued`" allows it to be specified multiple times on the command line.
|
151
147
|
|
152
148
|
```ruby
|
153
|
-
option "--format", "FORMAT", "output format", :
|
149
|
+
option "--format", "FORMAT", "output format", multivalued: true
|
154
150
|
```
|
155
151
|
|
156
152
|
The underlying attribute becomes an Array, and the suffix "`_list`" is appended to the default attribute name. In this case, an attribute called "`format_list`" would be generated (unless you override the default by specifying an `:attribute_name`).
|
@@ -160,12 +156,12 @@ The underlying attribute becomes an Array, and the suffix "`_list`" is appended
|
|
160
156
|
Declaring an option "`:hidden`" will cause it to be hidden from `--help` output.
|
161
157
|
|
162
158
|
```ruby
|
163
|
-
option "--some-option", "VALUE", "Just a little option", :
|
159
|
+
option "--some-option", "VALUE", "Just a little option", hidden: true
|
164
160
|
```
|
165
161
|
|
166
162
|
### Version option
|
167
163
|
|
168
|
-
A common idiom is to have an option `--version` that outputs the command version and doesn't run any subcommands. This can be
|
164
|
+
A common idiom is to have an option `--version` that outputs the command version and doesn't run any subcommands. This can be achieved by:
|
169
165
|
|
170
166
|
```ruby
|
171
167
|
option "--version", :flag, "Show version" do
|
@@ -174,8 +170,7 @@ option "--version", :flag, "Show version" do
|
|
174
170
|
end
|
175
171
|
```
|
176
172
|
|
177
|
-
Declaring parameters
|
178
|
-
--------------------
|
173
|
+
## Declaring parameters
|
179
174
|
|
180
175
|
Positional parameters can be declared using `parameter`, specifying
|
181
176
|
|
@@ -203,13 +198,12 @@ parameter "[TARGET_DIR]", "target directory"
|
|
203
198
|
Three dots at the end of a parameter name makes it "greedy" - it will consume all remaining command-line arguments. For example:
|
204
199
|
|
205
200
|
```ruby
|
206
|
-
parameter "FILE ...", "input files", :
|
201
|
+
parameter "FILE ...", "input files", attribute_name: :files
|
207
202
|
```
|
208
203
|
|
209
204
|
Like multivalued options, greedy parameters are backed by an Array attribute (named with a "`_list`" suffix, by default).
|
210
205
|
|
211
|
-
Parsing and validation of options and parameters
|
212
|
-
------------------------------------------------
|
206
|
+
## Parsing and validation of options and parameters
|
213
207
|
|
214
208
|
When you `#run` a command, it will first attempt to `#parse` command-line arguments, and map them onto the declared options and parameters, before invoking your `#execute` method.
|
215
209
|
|
@@ -265,15 +259,15 @@ end
|
|
265
259
|
Default values can be specified for options, and optional parameters:
|
266
260
|
|
267
261
|
```ruby
|
268
|
-
option "--flavour", "FLAVOUR", "ice-cream flavour", :
|
262
|
+
option "--flavour", "FLAVOUR", "ice-cream flavour", default: "chocolate"
|
269
263
|
|
270
|
-
parameter "[HOST]", "server host", :
|
264
|
+
parameter "[HOST]", "server host", default: "localhost"
|
271
265
|
```
|
272
266
|
|
273
267
|
For more advanced cases, you can also specify default values by defining a method called "`default_#{attribute_name}`":
|
274
268
|
|
275
269
|
```ruby
|
276
|
-
option "--http-port", "PORT", "web-server port", :
|
270
|
+
option "--http-port", "PORT", "web-server port", default: 9000
|
277
271
|
|
278
272
|
option "--admin-port", "PORT", "admin port"
|
279
273
|
|
@@ -287,29 +281,30 @@ end
|
|
287
281
|
Options (and optional parameters) can also be associated with environment variables:
|
288
282
|
|
289
283
|
```ruby
|
290
|
-
option "--port", "PORT", "the port to listen on", :
|
284
|
+
option "--port", "PORT", "the port to listen on", environment_variable: "MYAPP_PORT" do |val|
|
291
285
|
val.to_i
|
292
286
|
end
|
293
287
|
|
294
|
-
parameter "[HOST]", "server address", :
|
288
|
+
parameter "[HOST]", "server address", environment_variable: "MYAPP_HOST"
|
295
289
|
```
|
296
290
|
|
297
291
|
Clamp will check the specified envariables in the absence of values supplied on the command line, before looking for a default value.
|
298
292
|
|
299
293
|
### Allowing options after parameters
|
300
294
|
|
301
|
-
|
295
|
+
By default, Clamp only recognises options _before_ positional parameters.
|
296
|
+
|
297
|
+
Some other option-parsing libraries - notably [GNU `getopt(3)`](https://www.gnu.org/software/libc/manual/html_node/Using-Getopt.html) - allow option and parameter arguments to appear in any order on the command-line, e.g.
|
302
298
|
|
303
299
|
foobar --foo=bar something --fnord=snuffle another-thing
|
304
300
|
|
305
|
-
|
301
|
+
If you want Clamp to allow options and parameters to be "interspersed" in this way, set:
|
306
302
|
|
307
303
|
```ruby
|
308
304
|
Clamp.allow_options_after_parameters = true
|
309
305
|
```
|
310
306
|
|
311
|
-
Declaring Subcommands
|
312
|
-
---------------------
|
307
|
+
## Declaring Subcommands
|
313
308
|
|
314
309
|
Subcommand support helps you wrap a number of related commands into a single script (ala tools like "`git`"). Clamp will inspect the first command-line argument (after options are parsed), and delegate to the named subcommand.
|
315
310
|
|
@@ -400,8 +395,7 @@ Clamp do
|
|
400
395
|
end
|
401
396
|
```
|
402
397
|
|
403
|
-
Getting help
|
404
|
-
------------
|
398
|
+
## Getting help
|
405
399
|
|
406
400
|
All Clamp commands support a "`--help`" option, which outputs brief usage documentation, based on those seemingly useless extra parameters that you had to pass to `option` and `parameter`.
|
407
401
|
|
@@ -419,27 +413,27 @@ Options:
|
|
419
413
|
-h, --help print help
|
420
414
|
```
|
421
415
|
|
422
|
-
Localization
|
423
|
-
------------
|
416
|
+
## Localization
|
424
417
|
|
425
418
|
Clamp comes with support for overriding strings with custom translations. You can use localization library of your choice and override the strings at startup.
|
426
419
|
|
427
420
|
Example usage:
|
421
|
+
|
428
422
|
```ruby
|
429
423
|
require 'gettext'
|
430
424
|
|
431
425
|
Clamp.messages = {
|
432
|
-
:
|
433
|
-
:
|
434
|
-
:
|
435
|
-
:
|
426
|
+
too_many_arguments: _("too many arguments"),
|
427
|
+
option_required: _("option '%<option>s' is required"),
|
428
|
+
option_or_env_required: _("option '%<option>s' (or env %<env>s) is required"),
|
429
|
+
option_argument_error: _("option '%<switch>s': %<message>s")
|
436
430
|
# ...
|
437
431
|
}
|
438
432
|
```
|
433
|
+
|
439
434
|
See [messages.rb](https://github.com/mdub/clamp/blob/master/lib/clamp/messages.rb) for full list of available messages.
|
440
435
|
|
441
|
-
License
|
442
|
-
-------
|
436
|
+
## License
|
443
437
|
|
444
438
|
Copyright (C) 2011 [Mike Williams](mailto:mdub@dogbiscuit.org)
|
445
439
|
|
@@ -460,7 +454,6 @@ THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
460
454
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
461
455
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
462
456
|
|
463
|
-
Contributing to Clamp
|
464
|
-
---------------------
|
457
|
+
## Contributing to Clamp
|
465
458
|
|
466
459
|
Source-code for Clamp is [on Github](https://github.com/mdub/clamp).
|
data/Rakefile
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "bundler"
|
2
4
|
|
3
5
|
Bundler::GemHelper.install_tasks
|
@@ -10,3 +12,9 @@ RSpec::Core::RakeTask.new do |t|
|
|
10
12
|
t.pattern = "spec/**/*_spec.rb"
|
11
13
|
t.rspec_opts = ["--colour", "--format", "documentation"]
|
12
14
|
end
|
15
|
+
|
16
|
+
require "rubocop/rake_task"
|
17
|
+
|
18
|
+
RuboCop::RakeTask.new
|
19
|
+
|
20
|
+
task "default" => "rubocop"
|
data/clamp.gemspec
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.push File.expand_path("lib", __dir__)
|
2
4
|
require "clamp/version"
|
3
5
|
|
4
6
|
Gem::Specification.new do |s|
|
@@ -8,15 +10,15 @@ Gem::Specification.new do |s|
|
|
8
10
|
s.platform = Gem::Platform::RUBY
|
9
11
|
s.authors = ["Mike Williams"]
|
10
12
|
s.email = "mdub@dogbiscuit.org"
|
11
|
-
s.homepage = "
|
13
|
+
s.homepage = "https://github.com/mdub/clamp"
|
12
14
|
|
13
15
|
s.license = "MIT"
|
14
16
|
|
15
17
|
s.summary = "a minimal framework for command-line utilities"
|
16
|
-
s.description =
|
17
|
-
Clamp provides an object-model for command-line utilities.
|
18
|
-
It handles parsing of command-line options, and generation of usage help.
|
19
|
-
|
18
|
+
s.description = <<-TEXT.gsub(/^\s+/, "")
|
19
|
+
Clamp provides an object-model for command-line utilities.
|
20
|
+
It handles parsing of command-line options, and generation of usage help.
|
21
|
+
TEXT
|
20
22
|
|
21
23
|
s.files = `git ls-files`.split("\n")
|
22
24
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|