samovar 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 58236fabb652bc2a55a95575f77222e1bc99ea0f18c545383c14fe4cf996db83
4
- data.tar.gz: 843270eb1bd1e2f310c7653314cd3964d539e610627509c032d8926e94249806
3
+ metadata.gz: dee6078a981a3ddc9c9511c8a5411159bd94c9c422000f28babc461d44f40fb5
4
+ data.tar.gz: 7478ed72f2668fe912cde8701d4ecc2ab497ebeb36f76484160a495622b4842c
5
5
  SHA512:
6
- metadata.gz: 574add44e120a76c74e54632e47df879f2555043b04520b8f1db0770b187b1ddef62fdc93801b2de7383ae3bc5148494f4207e284dc29fd0795d26be6390d947
7
- data.tar.gz: 149ba4f3864badc05ccd337b8a1b5dab140e6696c372cffc6b9f448f7f7998c3880164c04d029df628989480fed7d81a8cac1981778c786871511e20002e53ba
6
+ metadata.gz: 3a24806b771f6d3e32f841462ec585ba22e0778d840dd51345b452914fac06f31882c007db8740daf1a01e0d3076119bcecd926bd7e6b57847289d006ede52b4
7
+ data.tar.gz: ea01b240a4b01caac62d950abd4785c2f77efa5fd9cc9cc1fa4fd6f06ea08303842481c27c8ac276142ee1dc3aeab71cd60b4a3c0420817628cdcff4a7c4961a
data/README.md CHANGED
@@ -36,95 +36,141 @@ Or install it yourself as:
36
36
 
37
37
  ## Usage
38
38
 
39
- ### Basic Options
39
+ Generally speaking, you should create `Command` classes that represent specific functions in your program. The top level command might look something like this:
40
+
41
+ ```ruby
42
+ require 'samovar'
40
43
 
41
- require 'samovar'
44
+ class List < Samovar::Command
45
+ self.description = "List the current directory"
42
46
 
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
- option '--things <a,b,c>', "A list of things" do |value|
49
- value.split(/\s*,\s*/)
50
- end
51
- end
47
+ def call
48
+ system("ls -lah")
49
+ end
50
+ end
51
+
52
+ class Application < Samovar::Command
53
+ options do
54
+ option '--help', "Do you need help?"
52
55
  end
53
56
 
54
- application = Application.new(['-f', 'Algebraic!'])
55
- application.options[:frobulate] # 'Algebraic!'
56
-
57
- application = Application.new(['-x', '-y'])
58
- application.options[:axis] # :y
59
-
60
- application = Application.new(['-F'])
61
- application.options[:flag] # true
57
+ nested :command, {
58
+ 'list' => List
59
+ }, default: 'list'
62
60
 
63
- application = Application.new(['--things', 'x,y,z'])
64
- application.options[:things] # ['x', 'y', 'z']
61
+ def call
62
+ if @options[:help]
63
+ self.print_usage
64
+ else
65
+ @command.call
66
+ end
67
+ end
68
+ end
65
69
 
66
- ### Nested Commands
70
+ Application.call # Defaults to ARGV.
71
+ ```
67
72
 
68
- require 'samovar'
69
-
70
- class Create < Samovar::Command
71
- def invoke(parent)
72
- puts "Creating"
73
+ ### Basic Options
74
+
75
+ ```ruby
76
+ require 'samovar'
77
+
78
+ class Application < Samovar::Command
79
+ options do
80
+ option '-f/--frobulate <text>', "Frobulate the text"
81
+ option '-x | -y', "Specify either x or y axis.", key: :axis
82
+ option '-F/--yeah/--flag', "A boolean flag with several forms."
83
+ option '--things <a,b,c>', "A list of things" do |value|
84
+ value.split(/\s*,\s*/)
73
85
  end
74
86
  end
87
+ end
88
+
89
+ application = Application.new(['-f', 'Algebraic!'])
90
+ application.options[:frobulate] # 'Algebraic!'
91
+
92
+ application = Application.new(['-x', '-y'])
93
+ application.options[:axis] # :y
94
+
95
+ application = Application.new(['-F'])
96
+ application.options[:flag] # true
97
+
98
+ application = Application.new(['--things', 'x,y,z'])
99
+ application.options[:things] # ['x', 'y', 'z']
100
+ ```
101
+
102
+ ### Nested Commands
103
+
104
+ ```ruby
105
+ require 'samovar'
106
+
107
+ class Create < Samovar::Command
108
+ def invoke(parent)
109
+ puts "Creating"
110
+ end
111
+ end
112
+
113
+ class Application < Samovar::Command
114
+ nested '<command>',
115
+ 'create' => Create
75
116
 
76
- class Application < Samovar::Command
77
- nested '<command>',
78
- 'create' => Create
79
-
80
- def invoke(program_name: File.basename($0))
81
- if @command
82
- @command.invoke
83
- else
84
- print_usage(program_name)
85
- end
117
+ def invoke(program_name: File.basename($0))
118
+ if @command
119
+ @command.invoke
120
+ else
121
+ print_usage(program_name)
86
122
  end
87
123
  end
124
+ end
88
125
 
89
- Application.new(['create']).invoke
126
+ Application.new(['create']).invoke
127
+ ```
90
128
 
91
129
  ### ARGV Splits
92
130
 
93
- require 'samovar'
131
+ ```ruby
132
+ require 'samovar'
94
133
 
95
- class Application < Samovar::Command
96
- many :packages
97
- split :argv
98
- end
134
+ class Application < Samovar::Command
135
+ many :packages
136
+ split :argv
137
+ end
99
138
 
100
- application = Application.new(['foo', 'bar', 'baz', '--', 'apples', 'oranges', 'feijoas'])
101
- application.packages # ['foo', 'bar', 'baz']
102
- application.argv # ['apples', 'oranges', 'feijoas']
139
+ application = Application.new(['foo', 'bar', 'baz', '--', 'apples', 'oranges', 'feijoas'])
140
+ application.packages # ['foo', 'bar', 'baz']
141
+ application.argv # ['apples', 'oranges', 'feijoas']
142
+ ```
103
143
 
104
144
  ### Parsing Tokens
105
145
 
106
- require 'samovar'
146
+ ```ruby
147
+ require 'samovar'
148
+
149
+ class Application < Samovar::Command
150
+ self.description = "Mix together your favorite things."
107
151
 
108
- class Application < Samovar::Command
109
- self.description = "Mix together your favorite things."
110
-
111
- one :fruit, "Name one fruit"
112
- many :cakes, "Any cakes you like"
113
- end
152
+ one :fruit, "Name one fruit"
153
+ many :cakes, "Any cakes you like"
154
+ end
114
155
 
115
- application = Application.new(['apple', 'chocolate cake', 'fruit cake'])
116
- application.fruit # 'apple'
117
- application.cakes # ['chocolate cake', 'fruit cake']
156
+ application = Application.new(['apple', 'chocolate cake', 'fruit cake'])
157
+ application.fruit # 'apple'
158
+ application.cakes # ['chocolate cake', 'fruit cake']
159
+ ```
118
160
 
119
161
  ### Explicit Commands
120
162
 
121
163
  Given a custom `Samovar::Command` subclass, you can instantiate it with options:
122
164
 
123
- application = Application['--root', path]
165
+ ```ruby
166
+ application = Application['--root', path]
167
+ ```
124
168
 
125
169
  You can also duplicate an existing command instance with additions/changes:
126
170
 
127
- concurrent_application = application['--threads', 12]
171
+ ```ruby
172
+ concurrent_application = application['--threads', 12]
173
+ ```
128
174
 
129
175
  These forms can be useful when invoking one command from another, or in unit tests.
130
176
 
@@ -138,14 +184,6 @@ These forms can be useful when invoking one command from another, or in unit tes
138
184
 
139
185
  ### Future Work
140
186
 
141
- #### Line Wrapping
142
-
143
- 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).
144
-
145
- #### Type Coercion
146
-
147
- 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?
148
-
149
187
  #### Multi-value Options
150
188
 
151
189
  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.
@@ -154,11 +192,15 @@ Right now, options can take a single argument, e.g. `--count <int>`. Ideally, we
154
192
 
155
193
  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:
156
194
 
157
- command list --help
195
+ ```ruby
196
+ command list --help
197
+ ```
158
198
 
159
199
  One might reasonably expect this to parse but it isn't so easy to generalize this:
160
200
 
161
- command list -- --help
201
+ ```ruby
202
+ command list -- --help
203
+ ```
162
204
 
163
205
  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).
164
206
 
@@ -31,8 +31,14 @@ require_relative 'error'
31
31
 
32
32
  module Samovar
33
33
  class Command
34
+ def self.call(input = ARGV)
35
+ if command = self.parse(input)
36
+ command.call
37
+ end
38
+ end
39
+
34
40
  # The top level entry point for parsing ARGV.
35
- def self.parse(input = ARGV)
41
+ def self.parse(input)
36
42
  self.new(input)
37
43
  rescue Error => error
38
44
  error.command.print_usage(output: $stderr) do |formatter|
@@ -19,5 +19,5 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module Samovar
22
- VERSION = "2.0.1"
22
+ VERSION = "2.1.0"
23
23
  end
@@ -46,6 +46,16 @@ module Samovar::CommandSpec
46
46
  end
47
47
 
48
48
  RSpec.describe Samovar::Command do
49
+ it "should invoke call" do
50
+ expect(Top).to receive(:new).and_wrap_original do |original_method, *args, &block|
51
+ original_method.call(*args, &block).tap do |instance|
52
+ expect(instance).to receive(:call)
53
+ end
54
+ end
55
+
56
+ Top.call([])
57
+ end
58
+
49
59
  it "should use default value" do
50
60
  top = Top[]
51
61
  expect(top.options[:configuration]).to be == 'TEAPOT_CONFIGURATION'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: samovar
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams