samovar 1.1.0 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +113 -7
- data/lib/samovar/options.rb +13 -1
- data/lib/samovar/version.rb +1 -1
- data/spec/samovar/command_spec.rb +6 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df07fe3139c96d3f34b9e4d1ae5416e1f311eb2f
|
4
|
+
data.tar.gz: e2ef640c6126fa551258c29bafca19bdeb92ff0c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fbb6dea46c1245ae3e471f67ae62b75d5ce20937dba2c06fd82671e6ebad78f8fbe136816bbc7afc902f18d9aedbb98a9547a13792e167d5e791b6bfd36c12e9
|
7
|
+
data.tar.gz: 4ff9e33ab4b1cdc1969c547e8ba56733e9b406876409568d1ced9ee8b54d70ade58e456e66064f6055a8f6b0b818157308924de9060d83b1a807ed7d6d126f0e
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
![Teapot](teapot.png)
|
4
4
|
|
5
|
-
Samovar is a modern framework for building command-line tools and applications. It provides a declarative class-based DSL for building command-line parsers that include automatic documentation generation
|
5
|
+
Samovar is a modern framework for building command-line tools and applications. It provides a declarative class-based DSL for building command-line parsers that include automatic documentation generation. It helps you keep your functionality clean and isolated where possible.
|
6
6
|
|
7
7
|
[![Build Status](https://secure.travis-ci.org/ioquatix/samovar.svg)](http://travis-ci.org/ioquatix/samovar)
|
8
8
|
[![Code Climate](https://codeclimate.com/github/ioquatix/samovar.svg)](https://codeclimate.com/github/ioquatix/samovar)
|
@@ -14,6 +14,12 @@ I've been using [Trollop](https://github.com/ManageIQ/trollop) and while it's no
|
|
14
14
|
|
15
15
|
One of the other issues I had with existing frameworks is testability. Most frameworks expect to have some pretty heavy logic directly in the binary executable, or at least don't structure your code in a way which makes testing easy. Samovar structures your command processing logic into classes which can be easily tested in isolation, which means that you can mock up and [spec your command-line executables easily](https://github.com/ioquatix/teapot/blob/master/spec/teapot/command_spec.rb).
|
16
16
|
|
17
|
+
## Examples
|
18
|
+
|
19
|
+
- [Teapot](https://github.com/ioquatix/teapot/blob/master/lib/teapot/command.rb) is a build system and uses multiple top-level commands.
|
20
|
+
- [Utopia](https://github.com/ioquatix/utopia/blob/master/lib/utopia/command.rb) is a web application platform and uses nested commands.
|
21
|
+
- [LSync](https://github.com/ioquatix/lsync/blob/master/lib/lsync/command.rb) is a backup tool and sends commands across the network and has lots of options with default values.
|
22
|
+
|
17
23
|
## Installation
|
18
24
|
|
19
25
|
Add this line to your application's Gemfile:
|
@@ -30,11 +36,79 @@ Or install it yourself as:
|
|
30
36
|
|
31
37
|
## Usage
|
32
38
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
39
|
+
### Basic Options
|
40
|
+
|
41
|
+
require 'samovar'
|
42
|
+
|
43
|
+
class Application < Samovar::Command
|
44
|
+
options do
|
45
|
+
option '-f/--frobulate <text>', "Frobulate the text"
|
46
|
+
option '-x | -y', "Specify either x or y axis.", key: :axis
|
47
|
+
option '-F/--yeah/--flag', "A boolean flag with several forms."
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
application = Application.new(['-f', 'Algebraic!'])
|
52
|
+
application.options[:frobulate] # 'Algebraic!'
|
53
|
+
|
54
|
+
application = Application.new(['-x', '-y'])
|
55
|
+
application.options[:axis] # :y
|
56
|
+
|
57
|
+
application = Application.new(['-F'])
|
58
|
+
application.options[:flag] # true
|
59
|
+
|
60
|
+
### Nested Commands
|
61
|
+
|
62
|
+
require 'samovar'
|
63
|
+
|
64
|
+
class Create < Samovar::Command
|
65
|
+
def invoke(parent)
|
66
|
+
puts "Creating"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Application < Samovar::Command
|
71
|
+
nested '<command>',
|
72
|
+
'create' => Create
|
73
|
+
|
74
|
+
def invoke(program_name: File.basename($0))
|
75
|
+
if @command
|
76
|
+
@command.invoke
|
77
|
+
else
|
78
|
+
print_usage(program_name)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
Application.new(['create']).invoke
|
84
|
+
|
85
|
+
### ARGV Splits
|
86
|
+
|
87
|
+
require 'samovar'
|
88
|
+
|
89
|
+
class Application < Samovar::Command
|
90
|
+
many :packages
|
91
|
+
split :argv
|
92
|
+
end
|
93
|
+
|
94
|
+
application = Application.new(['foo', 'bar', 'baz', '--', 'apples', 'oranges', 'feijoas'])
|
95
|
+
application.packages # ['foo', 'bar', 'baz']
|
96
|
+
application.argv # ['apples', 'oranges', 'feijoas']
|
97
|
+
|
98
|
+
### Parsing Tokens
|
99
|
+
|
100
|
+
require 'samovar'
|
101
|
+
|
102
|
+
class Application < Samovar::Command
|
103
|
+
self.description = "Mix together your favorite things."
|
104
|
+
|
105
|
+
one :fruit, "Name one fruit"
|
106
|
+
many :cakes, "Any cakes you like"
|
107
|
+
end
|
108
|
+
|
109
|
+
application = Application.new(['apple', 'chocolate cake', 'fruit cake'])
|
110
|
+
application.fruit # 'apple'
|
111
|
+
application.cakes # ['chocolate cake', 'fruit cake']
|
38
112
|
|
39
113
|
## Contributing
|
40
114
|
|
@@ -46,7 +120,39 @@ Please feel free to submit other examples and I will link to them here.
|
|
46
120
|
|
47
121
|
### Future Work
|
48
122
|
|
49
|
-
|
123
|
+
#### Line Wrapping
|
124
|
+
|
125
|
+
Line wrapping is done by the terminal which is a bit ugly in some cases. There is a [half-implemented elegant solution](lib/samovar/output/line_wrapper.rb).
|
126
|
+
|
127
|
+
#### Type Coercion
|
128
|
+
|
129
|
+
It might make sense to enforce constraints at parse time.. or not. For example, if an option is given like `--count <int>` we should probably parse an integer?
|
130
|
+
|
131
|
+
#### Multi-value Options
|
132
|
+
|
133
|
+
Right now, options can take a single argument, e.g. `--count <int>`. Ideally, we support a specific sub-parser defined by the option, e.g. `--count <int...>` or `--tag <section> <tags...>`. These would map to specific parsers using `Samovar::One` and `Samovar::Many` internally.
|
134
|
+
|
135
|
+
#### Global Options
|
136
|
+
|
137
|
+
Options can only be parsed at the place they are explicitly mentioned, e.g. a command with sub-commands won't parse an option added to the end of the command:
|
138
|
+
|
139
|
+
command list --help
|
140
|
+
|
141
|
+
One might reasonably expect this to parse but it isn't so easy to generalize this:
|
142
|
+
|
143
|
+
command list -- --help
|
144
|
+
|
145
|
+
In this case, do we show help? Some effort is required to disambiguate this. Initially, it makes sense to keep things as simple as possible. But, it might make sense for some options to be declared in a global scope, which are extracted before parsing begins. I'm not sure if this is really a good idea. It might just be better to give good error output in this case (you specified an option but it was in the wrong place).
|
146
|
+
|
147
|
+
#### Shell Auto-completion
|
148
|
+
|
149
|
+
Because of the structure of the Samovar command parser, it should be possible to generate a list of all possible tokens at each point. Therefore, semantically correct tab completion should be possible.
|
150
|
+
|
151
|
+
As a secondary to this, it would be nice if `Samovar::One` and `Samovar::Many` could take a list of potential tokens so that auto-completion could give meaningful suggestions, and possibly improved validation.
|
152
|
+
|
153
|
+
#### Short/Long Help
|
154
|
+
|
155
|
+
It might be interesting to explore whether it's possible to have `-h` and `--help` do different things. This could include command specific help output, more detailed help output (similar to a man page), and other useful help related tasks.
|
50
156
|
|
51
157
|
## License
|
52
158
|
|
data/lib/samovar/options.rb
CHANGED
@@ -42,6 +42,8 @@ module Samovar
|
|
42
42
|
attr :description
|
43
43
|
attr :type
|
44
44
|
|
45
|
+
attr :default
|
46
|
+
|
45
47
|
attr :key
|
46
48
|
|
47
49
|
def parse(input)
|
@@ -77,11 +79,17 @@ module Samovar
|
|
77
79
|
def initialize(title = "Options", key: :options)
|
78
80
|
@title = title
|
79
81
|
@ordered = []
|
82
|
+
|
83
|
+
# We use this flag to option cache to improve parsing performance:
|
80
84
|
@keyed = {}
|
85
|
+
|
81
86
|
@key = key
|
87
|
+
|
88
|
+
@defaults = {}
|
82
89
|
end
|
83
90
|
|
84
91
|
attr :key
|
92
|
+
attr :defaults
|
85
93
|
|
86
94
|
def option(*args, **options)
|
87
95
|
self << Option.new(*args, **options)
|
@@ -96,10 +104,14 @@ module Samovar
|
|
96
104
|
@keyed[alternative] = option
|
97
105
|
end
|
98
106
|
end
|
107
|
+
|
108
|
+
if default = option.default
|
109
|
+
@defaults[option.key] = option.default
|
110
|
+
end
|
99
111
|
end
|
100
112
|
|
101
113
|
def parse(input)
|
102
|
-
values =
|
114
|
+
values = @defaults.dup
|
103
115
|
|
104
116
|
while option = @keyed[input.first]
|
105
117
|
if result = option.parse(input)
|
data/lib/samovar/version.rb
CHANGED
@@ -15,7 +15,7 @@ module Command
|
|
15
15
|
self.description = "A decentralised package manager and build tool."
|
16
16
|
|
17
17
|
options do
|
18
|
-
option '-c/--configuration <name>', "Specify a specific build configuration.", default:
|
18
|
+
option '-c/--configuration <name>', "Specify a specific build configuration.", default: 'TEAPOT_CONFIGURATION'
|
19
19
|
option '-i/--in/--root <path>', "Work in the given root directory."
|
20
20
|
option '--verbose | --quiet', "Verbosity of output for debugging.", key: :logging
|
21
21
|
option '-h/--help', "Print out help information."
|
@@ -28,6 +28,11 @@ module Command
|
|
28
28
|
end
|
29
29
|
|
30
30
|
describe Samovar::Command do
|
31
|
+
it "should use default value" do
|
32
|
+
top = Command::Top.parse([])
|
33
|
+
expect(top.options[:configuration]).to be == 'TEAPOT_CONFIGURATION'
|
34
|
+
end
|
35
|
+
|
31
36
|
it "should parse a simple command" do
|
32
37
|
top = Command::Top.parse(["-c", "path", "bottom", "foobar", "A", "B", "--", "args", "args"])
|
33
38
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: samovar
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-06-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mapping
|