lino 3.2.0.pre.10 → 4.0.0.pre.2

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: 7cb69956b6fc3389deb0f7747c7ab5023225e95bee92ec99d1f7492d1a9020c4
4
- data.tar.gz: 4fb9eeea0d1be41bae1d8864cbf0893c0dcfa9f654e5730b69ffd0d396c2106c
3
+ metadata.gz: 62e29445dc3209ccbd5fd21289316ee48e4f085cea6ac435377689f424bee7c1
4
+ data.tar.gz: 7eda92dd75dd89c9359e5b0d080e26a42f7ab86bc57c4a4364d2ff1dce1fb548
5
5
  SHA512:
6
- metadata.gz: aa736d59d32a7358972e4940035f4767c077e2d1af7edf6630c144cb07b9235788f086c22589d64bfd4f00f0e7ddf02dc01d5bf1976bac496b4c696a90a4a8ed
7
- data.tar.gz: aff892172f4752f99f0b4d7d68075aa60ba20a6e5069ee8adb59a32c0d0cc7b6c7784420d7a7910ba97f8022614523642d9d6ef75bf00bb9392abf2fb86c4b01
6
+ metadata.gz: 5d1e5afde0da35b878998e6d5861270376e4d524ea4f365cfa9d88075085d651b40a9b9326779488be888b9fcdbc52bd86b0111d1b0e17330d3c383fc5e47424
7
+ data.tar.gz: cb184d00c5aa2dd87c64760181457398a25eff11f3c3d8aa81bdbbab81239dd6a58f3fe1bef7e929d3528725cf505c335b8c280b7e5d508de24173afa37c0e36
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- lino (3.2.0.pre.10)
4
+ lino (4.0.0.pre.2)
5
5
  childprocess (~> 5.0.0)
6
6
  hamster (~> 3.0)
7
7
  open4 (~> 1.3)
@@ -32,9 +32,10 @@ GEM
32
32
  diff-lcs (1.5.1)
33
33
  docile (1.4.0)
34
34
  drb (2.2.1)
35
- excon (0.110.0)
36
- faraday (2.9.2)
35
+ excon (0.111.0)
36
+ faraday (2.10.0)
37
37
  faraday-net_http (>= 2.0, < 3.2)
38
+ logger
38
39
  faraday-net_http (3.1.0)
39
40
  net-http
40
41
  ffi (1.17.0)
@@ -67,6 +68,7 @@ GEM
67
68
  listen (3.9.0)
68
69
  rb-fsevent (~> 0.10, >= 0.10.3)
69
70
  rb-inotify (~> 0.9, >= 0.9.10)
71
+ logger (1.6.0)
70
72
  lumberjack (1.2.10)
71
73
  method_source (1.1.0)
72
74
  minitest (5.24.1)
@@ -83,7 +85,7 @@ GEM
83
85
  sawyer (~> 0.9)
84
86
  open4 (1.3.4)
85
87
  parallel (1.25.1)
86
- parser (3.3.3.0)
88
+ parser (3.3.4.0)
87
89
  ast (~> 2.4.1)
88
90
  racc
89
91
  pry (0.14.2)
@@ -105,11 +107,11 @@ GEM
105
107
  colored2 (~> 3.1)
106
108
  git (~> 1.13, >= 1.13.2)
107
109
  rake_factory (= 0.32.0.pre.2)
108
- rake_git_crypt (0.1.0.pre.29)
110
+ rake_git_crypt (0.1.0.pre.30)
109
111
  colored2 (~> 3.1)
110
112
  rake_factory (= 0.32.0.pre.2)
111
- ruby_git_crypt (= 0.1.0.pre.4)
112
- ruby_gpg2 (~> 0.6)
113
+ ruby_git_crypt (= 0.1.0.pre.6)
114
+ ruby_gpg2 (= 0.11.0.pre.5)
113
115
  rake_github (0.13.0)
114
116
  colored2 (~> 3.1)
115
117
  octokit (>= 4.16, < 9.0)
@@ -142,13 +144,13 @@ GEM
142
144
  diff-lcs (>= 1.2.0, < 2.0)
143
145
  rspec-support (~> 3.13.0)
144
146
  rspec-support (3.13.1)
145
- rubocop (1.64.1)
147
+ rubocop (1.65.0)
146
148
  json (~> 2.3)
147
149
  language_server-protocol (>= 3.17.0)
148
150
  parallel (~> 1.10)
149
151
  parser (>= 3.3.0.2)
150
152
  rainbow (>= 2.2.2, < 4.0)
151
- regexp_parser (>= 1.8, < 3.0)
153
+ regexp_parser (>= 2.4, < 3.0)
152
154
  rexml (>= 3.2.5, < 4.0)
153
155
  rubocop-ast (>= 1.31.1, < 2.0)
154
156
  ruby-progressbar (~> 1.7)
@@ -157,14 +159,14 @@ GEM
157
159
  parser (>= 3.3.1.0)
158
160
  rubocop-rake (0.6.0)
159
161
  rubocop (~> 1.0)
160
- rubocop-rspec (3.0.2)
162
+ rubocop-rspec (3.0.3)
161
163
  rubocop (~> 1.61)
162
164
  ruby-progressbar (1.13.0)
163
- ruby_git_crypt (0.1.0.pre.4)
165
+ ruby_git_crypt (0.1.0.pre.6)
164
166
  immutable-struct (~> 2.4)
165
- lino (~> 3.0)
166
- ruby_gpg2 (0.10.0)
167
- lino (~> 3.0)
167
+ lino (= 3.2.0.pre.10)
168
+ ruby_gpg2 (0.11.0.pre.5)
169
+ lino (= 3.2.0.pre.10)
168
170
  sawyer (0.9.2)
169
171
  addressable (>= 2.3.5)
170
172
  faraday (>= 0.17.3, < 3)
@@ -206,4 +208,4 @@ DEPENDENCIES
206
208
  simplecov
207
209
 
208
210
  BUNDLED WITH
209
- 2.4.17
211
+ 2.5.15
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Lino
2
2
 
3
- Command line execution utilities.
3
+ Command line building and execution utilities.
4
4
 
5
5
  ## Installation
6
6
 
@@ -25,12 +25,15 @@ Lino allows commands to be built and executed:
25
25
  ```ruby
26
26
  require 'lino'
27
27
 
28
- command_line = Lino::CommandLineBuilder.for_command('ruby')
28
+ command_line = Lino.builder_for_command('ruby')
29
29
  .with_flag('-v')
30
30
  .with_option('-e', 'puts "Hello"')
31
31
  .build
32
32
 
33
- puts command_line.to_s
33
+ puts command_line.array
34
+ # => ['ruby', '-v', '-e', 'puts "Hello"']
35
+
36
+ puts command_line.string
34
37
  # => ruby -v -e puts "Hello"
35
38
 
36
39
  command_line.execute
@@ -38,34 +41,66 @@ command_line.execute
38
41
  # Hello
39
42
  ```
40
43
 
41
- ### `Lino::CommandLineBuilder`
44
+ ### Building command lines
45
+
46
+ `Lino` supports building command lines via instances of the
47
+ `Lino::Builder::CommandLine` class. `Lino::Builder::CommandLine` allows a
48
+ number of different styles of commands to be built. The object built by
49
+ `Lino::Builder::CommandLine` is an instance of `Lino::Model::CommandLine`, which
50
+ represents the components and context of a command line and allows the
51
+ command line to be executed.
52
+
53
+ Aside from the object model, `Lino::Model::CommandLine` instances have two
54
+ representations, accessible via the `#string` and `#array` instance methods.
55
+
56
+ The string representation is useful when the command line is intended to be
57
+ executed by a shell, where quoting is important. However, it can present a
58
+ security risk if the components (option values, arguments, environment
59
+ variables) of the command line are user provided. For this reason, the array
60
+ representation is preferable and is the representation used by default whenever
61
+ `Lino` executes commands.
42
62
 
43
- The `CommandLineBuilder` allows a number of different styles of commands to be
44
- built.
63
+ #### Getting a command line builder
64
+
65
+ A `Lino::Builder::CommandLine` can be instantiated using:
66
+
67
+ ```ruby
68
+ Lino.builder_for_command('ls')
69
+ ```
70
+
71
+ or using the now deprecated:
72
+
73
+ ```ruby
74
+ Lino::CommandLineBuilder.for_command('ls')
75
+ ```
45
76
 
46
77
  #### Flags
47
78
 
48
79
  Flags can be added with `#with_flag`:
49
80
 
50
81
  ```ruby
51
- Lino::CommandLineBuilder.for_command('ls')
82
+ command_line = Lino.builder_for_command('ls')
52
83
  .with_flag('-l')
53
84
  .with_flag('-a')
54
85
  .build
55
- .to_s
56
86
 
57
- # => ls -l -a
87
+ command_line.array
88
+ # => ["ls", "-l", "-a"]
89
+ command_line.string
90
+ # => "ls -l -a"
58
91
  ```
59
92
 
60
93
  or `#with_flags`:
61
94
 
62
95
  ```ruby
63
- Lino::CommandLineBuilder.for_command('ls')
96
+ command_line = Lino.builder_for_command('ls')
64
97
  .with_flags(%w[-l -a])
65
98
  .build
66
- .to_s
67
99
 
68
- # => ls -l -a
100
+ command_line.array
101
+ # => ["ls", "-l", "-a"]
102
+ command_line.string
103
+ # => "ls -l -a"
69
104
  ```
70
105
 
71
106
  #### Options
@@ -73,33 +108,38 @@ Lino::CommandLineBuilder.for_command('ls')
73
108
  Options with values can be added with `#with_option`:
74
109
 
75
110
  ```ruby
76
- Lino::CommandLineBuilder.for_command('gpg')
111
+ command_line = Lino.builder_for_command('gpg')
77
112
  .with_option('--recipient', 'tobyclemson@gmail.com')
78
113
  .with_option('--sign', './doc.txt')
79
114
  .build
80
- .to_s
81
115
 
82
- # => gpg --recipient tobyclemson@gmail.com --sign ./doc.txt
116
+ command_line.array
117
+ # => ["gpg", "--recipient", "tobyclemson@gmail.com", "--sign", "./doc.txt"]
118
+ command_line.string
119
+ # => "gpg --recipient tobyclemson@gmail.com --sign ./doc.txt"
120
+
83
121
  ```
84
122
 
85
123
  or `#with_options`, either as a hash:
86
124
 
87
125
  ```ruby
88
- Lino::CommandLineBuilder.for_command('gpg')
126
+ command_line = Lino.builder_for_command('gpg')
89
127
  .with_options({
90
128
  '--recipient' => 'tobyclemson@gmail.com',
91
129
  '--sign' => './doc.txt'
92
130
  })
93
131
  .build
94
- .to_s
95
132
 
96
- # => gpg --recipient tobyclemson@gmail.com --sign ./doc.txt
133
+ command_line.array
134
+ # => ["gpg", "--recipient", "tobyclemson@gmail.com", "--sign", "./doc.txt"]
135
+ command_line.string
136
+ # => "gpg --recipient tobyclemson@gmail.com --sign ./doc.txt"
97
137
  ```
98
138
 
99
139
  or as an array:
100
140
 
101
141
  ```ruby
102
- Lino::CommandLineBuilder.for_command('gpg')
142
+ command_line = Lino.builder_for_command('gpg')
103
143
  .with_options(
104
144
  [
105
145
  { option: '--recipient', value: 'tobyclemson@gmail.com' },
@@ -107,20 +147,24 @@ Lino::CommandLineBuilder.for_command('gpg')
107
147
  ]
108
148
  )
109
149
  .build
110
- .to_s
111
150
 
112
- # => gpg --recipient tobyclemson@gmail.com --sign ./doc.txt
151
+ command_line.array
152
+ # => ["gpg", "--recipient", "tobyclemson@gmail.com", "--sign", "./doc.txt"]
153
+ command_line.string
154
+ # => "gpg --recipient tobyclemson@gmail.com --sign ./doc.txt"
113
155
  ```
114
156
 
115
157
  Some commands allow options to be repeated:
116
158
 
117
159
  ```ruby
118
- Lino::CommandLineBuilder.for_command('example.sh')
160
+ command_line = Lino.builder_for_command('example.sh')
119
161
  .with_repeated_option('--opt', ['file1.txt', nil, '', 'file2.txt'])
120
162
  .build
121
- .to_s
122
163
 
123
- # => example.sh --opt file1.txt --opt file2.txt
164
+ command_line.array
165
+ # => ["example.sh", "--opt", "file1.txt", "--opt", "file2.txt"]
166
+ command_line.string
167
+ # => "example.sh --opt file1.txt --opt file2.txt"
124
168
  ```
125
169
 
126
170
  > Note: `lino` ignores `nil` or empty option values in the resulting command
@@ -131,24 +175,28 @@ Lino::CommandLineBuilder.for_command('example.sh')
131
175
  Arguments can be added using `#with_argument`:
132
176
 
133
177
  ```ruby
134
- Lino::CommandLineBuilder.for_command('diff')
178
+ command_line = Lino.builder_for_command('diff')
135
179
  .with_argument('./file1.txt')
136
180
  .with_argument('./file2.txt')
137
181
  .build
138
- .to_s
139
182
 
140
- # => diff ./file1.txt ./file2.txt
183
+ command_line.array
184
+ # => ["diff", "./file1.txt", "./file2.txt"]
185
+ command_line.string
186
+ # => "diff ./file1.txt ./file2.txt"
141
187
  ```
142
188
 
143
189
  or `#with_arguments`, as an array:
144
190
 
145
191
  ```ruby
146
- Lino::CommandLineBuilder.for_command('diff')
192
+ command_line = Lino.builder_for_command('diff')
147
193
  .with_arguments(['./file1.txt', nil, '', './file2.txt'])
148
194
  .build
149
- .to_s
150
195
 
151
- # => diff ./file1.txt ./file2.txt
196
+ command_line.array
197
+ # => ["diff", "./file1.txt", "./file2.txt"]
198
+ command_line.string
199
+ # => "diff ./file1.txt ./file2.txt"
152
200
  ```
153
201
 
154
202
  > Note: `lino` ignores `nil` or empty argument values in the resulting command
@@ -156,30 +204,35 @@ Lino::CommandLineBuilder.for_command('diff')
156
204
 
157
205
  #### Option Separators
158
206
 
159
- By default, `lino` separates option values from the option by a space. This
160
- can be overridden globally using `#with_option_separator`:
207
+ By default, when rendering command lines as a string, `lino` separates option
208
+ values from the option by a space. This can be overridden globally using
209
+ `#with_option_separator`:
161
210
 
162
211
  ```ruby
163
- Lino::CommandLineBuilder.for_command('java')
212
+ command_line = Lino.builder_for_command('java')
164
213
  .with_option_separator(':')
165
214
  .with_option('-splash', './images/splash.jpg')
166
215
  .with_argument('./application.jar')
167
216
  .build
168
- .to_s
169
217
 
170
- # => java -splash:./images/splash.jpg ./application.jar
218
+ command_line.array
219
+ # => ["java", "-splash:./images/splash.jpg", "./application.jar"]
220
+ command_line.string
221
+ # => "java -splash:./images/splash.jpg ./application.jar"
171
222
  ```
172
223
 
173
- The option separator can be overridden on an option by option basis:
224
+ The option separator can also be overridden on an option by option basis:
174
225
 
175
226
  ```ruby
176
- Lino::CommandLineBuilder.for_command('java')
227
+ command_line = Lino.builder_for_command('java')
177
228
  .with_option('-splash', './images/splash.jpg', separator: ':')
178
229
  .with_argument('./application.jar')
179
230
  .build
180
- .to_s
181
231
 
182
- # => java -splash:./images/splash.jpg ./application.jar
232
+ command_line.array
233
+ # => ["java", "-splash:./images/splash.jpg", "./application.jar"]
234
+ command_line.string
235
+ # => "java -splash:./images/splash.jpg ./application.jar"
183
236
  ```
184
237
 
185
238
  > Note: `#with_options` supports separator overriding when the options are
@@ -193,29 +246,34 @@ Lino::CommandLineBuilder.for_command('java')
193
246
 
194
247
  #### Option Quoting
195
248
 
196
- By default, `lino` does not quote option values. This can be overridden
197
- globally using `#with_option_quoting`:
249
+ By default, when rendering command line strings, `lino` does not quote option
250
+ values. This can be overridden globally using `#with_option_quoting`:
198
251
 
199
252
  ```ruby
200
- Lino::CommandLineBuilder.for_command('gpg')
253
+ command_line = Lino.builder_for_command('gpg')
201
254
  .with_option_quoting('"')
202
255
  .with_option('--sign', 'some file.txt')
203
256
  .build
204
- .to_s
205
257
 
206
- # => gpg --sign "some file.txt"
258
+ command_line.string
259
+ # => "gpg --sign \"some file.txt\""
260
+ command_line.array
261
+ # => ["gpg", "--sign", "some file.txt"]
207
262
  ```
208
263
 
209
- The option quoting can be overridden on an option by option basis:
264
+ The option quoting can also be overridden on an option by option basis:
210
265
 
211
266
  ```ruby
212
- Lino::CommandLineBuilder.for_command('java')
267
+ command_line = Lino.builder_for_command('java')
213
268
  .with_option('-splash', './images/splash.jpg', quoting: '"')
214
269
  .with_argument('./application.jar')
215
270
  .build
216
- .to_s
271
+ .string
217
272
 
218
- # => java -splash "./images/splash.jpg" ./application.jar
273
+ command_line.string
274
+ # => "java -splash \"./images/splash.jpg\" ./application.jar"
275
+ command_line.array
276
+ # => ["java", "-splash", "./images/splash.jpg", "./application.jar"]
219
277
  ```
220
278
 
221
279
  > Note: `#with_options` supports quoting overriding when the options are
@@ -227,46 +285,55 @@ Lino::CommandLineBuilder.for_command('java')
227
285
  > Note: option specific quoting take precedence over the global option
228
286
  > quoting
229
287
 
288
+ > Note: option quoting has no impact on the array representation of a command
289
+ > line
290
+
230
291
  #### Subcommands
231
292
 
232
293
  Subcommands can be added using `#with_subcommand`:
233
294
 
234
295
  ```ruby
235
- Lino::CommandLineBuilder.for_command('git')
296
+ command_line = Lino.builder_for_command('git')
236
297
  .with_flag('--no-pager')
237
298
  .with_subcommand('log')
238
299
  .build
239
- .to_s
240
300
 
241
- # => git --no-pager log
301
+ command_line.array
302
+ # => ["git", "--no-pager", "log"]
303
+ command_line.string
304
+ # => "git --no-pager log"
242
305
  ```
243
306
 
244
307
  Multi-level subcommands can be added using multiple `#with_subcommand`
245
308
  invocations:
246
309
 
247
310
  ```ruby
248
- Lino::CommandLineBuilder.for_command('gcloud')
311
+ command_line = Lino.builder_for_command('gcloud')
249
312
  .with_subcommand('sql')
250
313
  .with_subcommand('instances')
251
314
  .with_subcommand('set-root-password')
252
315
  .with_subcommand('some-database')
253
316
  .build
254
- .to_s
255
317
 
256
- # => gcloud sql instances set-root-password some-database
318
+ command_line.array
319
+ # => ["gcloud", "sql", "instances", "set-root-password", "some-database"]
320
+ command_line.string
321
+ # => "gcloud sql instances set-root-password some-database"
257
322
  ```
258
323
 
259
324
  or using `#with_subcommands`:
260
325
 
261
326
  ```ruby
262
- Lino::CommandLineBuilder.for_command('gcloud')
327
+ command_line = Lino.builder_for_command('gcloud')
263
328
  .with_subcommands(
264
329
  %w[sql instances set-root-password some-database]
265
330
  )
266
331
  .build
267
- .to_s
268
-
269
- # => gcloud sql instances set-root-password some-database
332
+
333
+ command_line.array
334
+ # => ["gcloud", "sql", "instances", "set-root-password", "some-database"]
335
+ command_line.string
336
+ # => "gcloud sql instances set-root-password some-database"
270
337
  ```
271
338
 
272
339
  Subcommands also support options via `#with_flag`, `#with_flags`,
@@ -274,15 +341,17 @@ Subcommands also support options via `#with_flag`, `#with_flags`,
274
341
  via a block, for example:
275
342
 
276
343
  ```ruby
277
- Lino::CommandLineBuilder.for_command('git')
344
+ command_line = Lino.builder_for_command('git')
278
345
  .with_flag('--no-pager')
279
346
  .with_subcommand('log') do |sub|
280
347
  sub.with_option('--since', '2016-01-01')
281
348
  end
282
349
  .build
283
- .to_s
284
350
 
285
- # => git --no-pager log --since 2016-01-01
351
+ command_line.array
352
+ # => ["git", "--no-pager", "log", "--since", "2016-01-01"]
353
+ command_line.string
354
+ # => "git --no-pager log --since 2016-01-01"
286
355
  ```
287
356
 
288
357
  > Note: `#with_subcommands` also supports a block, which applies in the context
@@ -290,38 +359,46 @@ Lino::CommandLineBuilder.for_command('git')
290
359
 
291
360
  #### Environment Variables
292
361
 
293
- Command lines can be prefixed with environment variables using
362
+ Environment variables can be added to command lines using
294
363
  `#with_environment_variable`:
295
364
 
296
365
  ```ruby
297
- Lino::CommandLineBuilder.for_command('node')
366
+ command_line = Lino.builder_for_command('node')
298
367
  .with_environment_variable('PORT', '3030')
299
368
  .with_environment_variable('LOG_LEVEL', 'debug')
300
369
  .with_argument('./server.js')
301
370
  .build
302
- .to_s
303
-
304
- # => PORT=3030 LOG_LEVEL=debug node ./server.js
371
+
372
+ command_line.string
373
+ # => "PORT=\"3030\" LOG_LEVEL=\"debug\" node ./server.js"
374
+ command_line.array
375
+ # => ["node", "./server.js"]
376
+ command_line.env
377
+ # => {"PORT"=>"3030", "LOG_LEVEL"=>"debug"}
305
378
  ```
306
379
 
307
380
  or `#with_environment_variables`, either as a hash:
308
381
 
309
382
  ```ruby
310
- Lino::CommandLineBuilder.for_command('node')
383
+ command_line = Lino.builder_for_command('node')
311
384
  .with_environment_variables({
312
385
  'PORT' => '3030',
313
386
  'LOG_LEVEL' => 'debug'
314
387
  })
315
388
  .build
316
- .to_s
317
-
318
- # => PORT=3030 LOG_LEVEL=debug node ./server.js
389
+
390
+ command_line.string
391
+ # => "PORT=\"3030\" LOG_LEVEL=\"debug\" node ./server.js"
392
+ command_line.array
393
+ # => ["node", "./server.js"]
394
+ command_line.env
395
+ # => {"PORT"=>"3030", "LOG_LEVEL"=>"debug"}
319
396
  ```
320
397
 
321
398
  or as an array:
322
399
 
323
400
  ```ruby
324
- Lino::CommandLineBuilder.for_command('node')
401
+ command_line = Lino.builder_for_command('node')
325
402
  .with_environment_variables(
326
403
  [
327
404
  { name: 'PORT', value: '3030' },
@@ -329,9 +406,13 @@ Lino::CommandLineBuilder.for_command('node')
329
406
  ]
330
407
  )
331
408
  .build
332
- .to_s
333
-
334
- # => PORT=3030 LOG_LEVEL=debug node ./server.js
409
+
410
+ command_line.string
411
+ # => "PORT=\"3030\" LOG_LEVEL=\"debug\" node ./server.js"
412
+ command_line.array
413
+ # => ["node", "./server.js"]
414
+ command_line.env
415
+ # => {"PORT"=>"3030", "LOG_LEVEL"=>"debug"}
335
416
  ```
336
417
 
337
418
  #### Option Placement
@@ -342,13 +423,21 @@ subcommands and arguments.
342
423
  This is equivalent to calling `#with_options_after_command`:
343
424
 
344
425
  ```ruby
345
- Lino::CommandLineBuilder.for_command('gcloud')
426
+ command_line = Lino.builder_for_command('gcloud')
346
427
  .with_options_after_command
347
428
  .with_option('--password', 'super-secure')
348
429
  .with_subcommands(%w[sql instances set-root-password])
349
430
  .build
350
- .to_s
351
431
 
432
+ command_line.array
433
+ # =>
434
+ # ["gcloud",
435
+ # "--password",
436
+ # "super-secure",
437
+ # "sql",
438
+ # "instances",
439
+ # "set-root-password"]
440
+ command_line.string
352
441
  # => gcloud --password super-secure sql instances set-root-password
353
442
  ```
354
443
 
@@ -356,42 +445,61 @@ Alternatively, top-level options can be placed after all subcommands using
356
445
  `#with_options_after_subcommands`:
357
446
 
358
447
  ```ruby
359
- Lino::CommandLineBuilder.for_command('gcloud')
448
+ command_line = Lino.builder_for_command('gcloud')
360
449
  .with_options_after_subcommands
361
450
  .with_option('--password', 'super-secure')
362
451
  .with_subcommands(%w[sql instances set-root-password])
363
452
  .build
364
- .to_s
365
453
 
454
+ command_line.array
455
+ # =>
456
+ # ["gcloud",
457
+ # "sql",
458
+ # "instances",
459
+ # "set-root-password",
460
+ # "--password",
461
+ # "super-secure"]
462
+ command_line.string
366
463
  # => gcloud sql instances set-root-password --password super-secure
367
464
  ```
368
465
 
369
466
  or, after all arguments, using `#with_options_after_arguments`:
370
467
 
371
468
  ```ruby
372
- Lino::CommandLineBuilder.for_command('ls')
469
+ command_line = Lino.builder_for_command('ls')
373
470
  .with_options_after_arguments
374
471
  .with_flag('-l')
375
472
  .with_argument('/some/directory')
376
473
  .build
377
- .to_s
378
474
 
379
- # => ls /some/directory -l
475
+ command_line.array
476
+ # => ["ls", "/some/directory", "-l"]
477
+ command_line.string
478
+ # => "ls /some/directory -l"
380
479
  ```
381
480
 
382
481
  The option placement can be overridden on an option by option basis:
383
482
 
384
483
  ```ruby
385
- Lino::CommandLineBuilder.for_command('gcloud')
484
+ command_line = Lino.builder_for_command('gcloud')
386
485
  .with_options_after_subcommands
387
486
  .with_option('--log-level', 'debug', placement: :after_command)
388
- .with_option('--password', 'super-secure')
487
+ .with_option('--password', 'pass1')
389
488
  .with_subcommands(%w[sql instances set-root-password])
390
489
  .build
391
- .to_s
392
490
 
393
- # => gcloud --log-level debug sql instances set-root-password \
394
- # --password super-secure
491
+ command_line.array
492
+ # =>
493
+ # ["gcloud",
494
+ # "--log-level",
495
+ # "debug",
496
+ # "sql",
497
+ # "instances",
498
+ # "set-root-password",
499
+ # "--password",
500
+ # "pass1"]
501
+ command_line.string
502
+ # => "gcloud --log-level debug sql instances set-root-password --password pass1"
395
503
  ```
396
504
 
397
505
  The `:placement` keyword argument accepts placement values of `:after_command`,
@@ -429,20 +537,22 @@ end
429
537
  an instance of the appliable can be applied using `#with_appliable`:
430
538
 
431
539
  ```ruby
432
- Lino::CommandLineBuilder.for_command('gpg')
540
+ command_line = Lino.builder_for_command('gpg')
433
541
  .with_appliable(AppliableOption.new('--recipient', 'tobyclemson@gmail.com'))
434
542
  .with_flag('--sign')
435
543
  .with_argument('/some/file.txt')
436
544
  .build
437
- .to_s
438
545
 
439
- # => gpg --recipient tobyclemson@gmail.com --sign /some/file.txt
546
+ command_line.array
547
+ # => ["gpg", "--recipient", "tobyclemson@gmail.com", "--sign", "/some/file.txt"]
548
+ command_line.string
549
+ # => "gpg --recipient tobyclemson@gmail.com --sign /some/file.txt"
440
550
  ```
441
551
 
442
552
  or multiple with `#with_appliables`:
443
553
 
444
554
  ```ruby
445
- Lino::CommandLineBuilder.for_command('gpg')
555
+ command_line = Lino.builder_for_command('gpg')
446
556
  .with_appliables([
447
557
  AppliableOption.new('--recipient', 'user@example.com'),
448
558
  AppliableOption.new('--output', '/signed.txt')
@@ -450,57 +560,285 @@ Lino::CommandLineBuilder.for_command('gpg')
450
560
  .with_flag('--sign')
451
561
  .with_argument('/file.txt')
452
562
  .build
453
- .to_s
454
563
 
455
- # => gpg --recipient user@example.com --output /signed.txt --sign /file.txt
564
+ command_line.array
565
+ # =>
566
+ # ["gpg",
567
+ # "--recipient",
568
+ # "tobyclemson@gmail.com",
569
+ # "--output",
570
+ # "/signed.txt",
571
+ # "--sign",
572
+ # "/some/file.txt"]
573
+ command_line.string
574
+ # => "gpg --recipient user@example.com --output /signed.txt --sign /file.txt"
456
575
  ```
457
576
 
458
577
  > Note: an 'appliable' is any object that has an `#apply` method.
459
578
 
460
579
  > Note: `lino` ignores `nil` or empty appliables in the resulting command line.
461
580
 
462
- ### `Lino::CommandLine`
581
+ #### Working Directory
582
+
583
+ By default, when a command line is executed, the working directory of the parent
584
+ process is used. This can be overridden with `#with_working_directory`:
585
+
586
+ ```ruby
587
+ command_line = Lino.builder_for_command('ls')
588
+ .with_flag('-l')
589
+ .with_working_directory('/home/tobyclemson')
590
+ .build
591
+
592
+ command_line.working_directory
593
+ # => "/home/tobyclemson"
594
+ ```
595
+
596
+ All built in executors honour the provided working directory, setting it on
597
+ spawned processes.
463
598
 
464
- A `CommandLine` can be executed using the `#execute` method:
599
+ ### Executing command lines
600
+
601
+ `Lino::Model::CommandLine` instances can be executed after construction. They
602
+ utilise an executor to achieve this, which is any object that has an
603
+ `#execute(command_line, opts)` method. `Lino` provides default executors such
604
+ that a custom executor only needs to be provided in special cases.
605
+
606
+ #### `#execute`
607
+
608
+ A `Lino::Model::CommandLine` instance can be executed using the `#execute`
609
+ method:
465
610
 
466
611
  ```ruby
467
- command_line = Lino::CommandLineBuilder.for_command('ls')
612
+ command_line = Lino.builder_for_command('ls')
468
613
  .with_flag('-l')
469
614
  .with_flag('-a')
470
615
  .with_argument('/')
471
616
  .build
472
617
 
473
618
  command_line.execute
474
-
475
619
  # => <contents of / directory>
476
620
  ```
477
621
 
478
- By default, the standard input stream is empty and the process writes to the
479
- standard output and error streams.
622
+ #### Standard Streams
623
+
624
+ By default, all streams are inherited from the parent process.
480
625
 
481
626
  To populate standard input:
482
627
 
483
628
  ```ruby
484
- command_line.execute(stdin: 'something to be passed to standard input')
629
+ require 'stringio'
630
+
631
+ command_line.execute(
632
+ stdin: StringIO.new('something to be passed to standard input')
633
+ )
485
634
  ```
486
635
 
487
- The `stdin` option supports any object that responds to `each`, `read` or
488
- `to_s`.
636
+ The `stdin` option supports any object that responds to `read`.
489
637
 
490
638
  To provide custom streams for standard output or standard error:
491
639
 
492
640
  ```ruby
493
- require 'stringio'
641
+ require 'tempfile'
494
642
 
495
- stdout = StringIO.new
496
- stderr = StringIO.new
643
+ stdout = Tempfile.new
644
+ stderr = Tempfile.new
497
645
 
498
646
  command_line.execute(stdout: stdout, stderr: stderr)
647
+
648
+ stdout.rewind
649
+ stderr.rewind
499
650
 
500
- puts "[output: #{stdout.string}, error: #{stderr.string}]"
651
+ puts "[output: #{stdout.read}, error: #{stderr.read}]"
652
+ ```
653
+
654
+ The `stdout` and `stderr` options support any instance of `IO` or a subclass.
655
+
656
+ #### Executors
657
+
658
+ `Lino` includes three built-in executors:
659
+
660
+ * `Lino::Executors::Childprocess` which is based on the
661
+ [`childprocess` gem](https://github.com/enkessler/childprocess)
662
+ * `Lino::Executors::Open4` which is based on the
663
+ [`open4` gem](https://github.com/ahoward/open4)
664
+ * `Lino::Executors::Mock` which does not start real processes and is useful for
665
+ use in tests.
666
+
667
+ ##### Configuration
668
+
669
+ By default, an instance of `Lino::Executors::Childprocess` is used. This is
670
+ controlled by the default executor configured on `Lino`:
671
+
672
+ ```ruby
673
+ Lino.configuration.executor
674
+ # => #<Lino::Executors::Childprocess:0x0000000103007108>
675
+
676
+ executor = Lino::Executors::Mock.new
677
+
678
+ Lino.configure do |config|
679
+ config.executor = executor
680
+ end
681
+
682
+ Lino.configuration.executor
683
+ # =>
684
+ # #<Lino::Executors::Mock:0x0000000106d4d3c8
685
+ # @executions=[],
686
+ # @exit_code=0,
687
+ # @stderr_contents=nil,
688
+ # @stdout_contents=nil>
689
+
690
+ Lino.reset!
691
+
692
+ Lino.configuration.executor
693
+ # => #<Lino::Executors::Childprocess:0x00000001090fcb48>
694
+ ```
695
+
696
+ ##### Builder overrides
697
+
698
+ Any built command will inherit the executor set as default at build time.
699
+
700
+ To override the executor on the builder, use `#with_executor`:
701
+
702
+ ```ruby
703
+ executor = Lino::Executors::Mock.new
704
+ command_line = Lino.builder_for_command('ls')
705
+ .with_executor(executor)
706
+ .build
707
+
708
+ command_line.executor
709
+ # =>
710
+ # #<Lino::Executors::Mock:0x0000000108e7d890
711
+ # @executions=[],
712
+ # @exit_code=0,
713
+ # @stderr_contents=nil,
714
+ # @stdout_contents=nil>
715
+ ```
716
+
717
+ ##### Mock executor
718
+
719
+ The `Lino::Executors::Mock` captures executions without spawning any real
720
+ processes:
721
+
722
+ ```ruby
723
+ executor = Lino::Executors::Mock.new
724
+ command_line = Lino.builder_for_command('ls')
725
+ .with_executor(executor)
726
+ .build
727
+
728
+ command_line.execute
729
+
730
+ executor.executions.length
731
+ # => 1
732
+
733
+ execution = executor.executions.first
734
+ execution.command_line == command_line
735
+ # => true
736
+ execution.exit_code
737
+ # => 0
738
+ ```
739
+
740
+ The mock can be configured to write to any provided `stdout` or `stderr`:
741
+
742
+ ```ruby
743
+ require 'tempfile'
744
+
745
+ executor = Lino::Executors::Mock.new
746
+ executor.write_to_stdout('hello!')
747
+ executor.write_to_stderr('error!')
748
+
749
+ command_line = Lino.builder_for_command('ls')
750
+ .with_executor(executor)
751
+ .build
752
+
753
+ stdout = Tempfile.new
754
+ stderr = Tempfile.new
755
+
756
+ command_line.execute(stdout:, stderr:)
757
+
758
+ stdout.rewind
759
+ stderr.rewind
760
+
761
+ stdout.read == 'hello!'
762
+ # => true
763
+ stderr.read == 'error!'
764
+ # => true
765
+ ```
766
+
767
+ The mock also captures any provided `stdin`:
768
+
769
+ ```ruby
770
+ require 'stringio'
771
+
772
+ executor = Lino::Executors::Mock.new
773
+ command_line = Lino.builder_for_command('ls')
774
+ .with_executor(executor)
775
+ .build
776
+
777
+ stdin = StringIO.new("input\n")
778
+
779
+ command_line.execute(stdin:)
780
+
781
+ execution = executor.executions.first
782
+ execution.stdin_contents
783
+ # => "input\n"
501
784
  ```
502
785
 
503
- The `stdout` and `stderr` options support any object that responds to `<<`.
786
+ The mock can be configured to fail all executions:
787
+
788
+ ```ruby
789
+ executor = Lino::Executors::Mock.new
790
+ executor.fail_all_executions
791
+
792
+ command_line = Lino.builder_for_command('ls')
793
+ .with_executor(executor)
794
+ .build
795
+
796
+ command_line.execute
797
+ # ...in `execute': Failed while executing command line.
798
+ # (Lino::Errors::ExecutionError)
799
+
800
+ command_line.execute
801
+ # ...in `execute': Failed while executing command line.
802
+ # (Lino::Errors::ExecutionError)
803
+ ```
804
+
805
+ The exit code, which defaults to zero, can also be set explicitly, with anything
806
+ other than zero causing a `Lino::Errors::ExecutionError` to be raised:
807
+
808
+ ```ruby
809
+ executor = Lino::Executors::Mock.new
810
+ executor.exit_code = 128
811
+
812
+ command_line = Lino.builder_for_command('ls')
813
+ .with_executor(executor)
814
+ .build
815
+
816
+ begin
817
+ command_line.execute
818
+ rescue Lino::Errors::ExecutionError => e
819
+ e.exit_code
820
+ end
821
+ # => 128
822
+ ```
823
+
824
+ The mock is stateful and accumulates executions and configurations. To reset the
825
+ mock to its initial state:
826
+
827
+ ```ruby
828
+ executor = Lino::Executors::Mock.new
829
+ executor.exit_code = 128
830
+ executor.write_to_stdout('hello!')
831
+ executor.write_to_stderr('error!')
832
+
833
+ executor.reset
834
+
835
+ executor.exit_code
836
+ # => 0
837
+ executor.stdout_contents
838
+ # => nil
839
+ executor.stderr_contents
840
+ # => nil
841
+ ```
504
842
 
505
843
  ## Development
506
844
 
@@ -9,7 +9,7 @@ module Lino
9
9
  include Validation
10
10
 
11
11
  def with_appliable(appliable)
12
- return self if nil?(appliable)
12
+ return self if appliable.nil?
13
13
 
14
14
  appliable.apply(self)
15
15
  end
@@ -15,7 +15,7 @@ module Lino
15
15
  end
16
16
 
17
17
  def with_argument(argument)
18
- return self if nil?(argument)
18
+ return self if argument.nil?
19
19
  return self if empty?(argument.to_s)
20
20
 
21
21
  with(arguments: @arguments.add(Model::Argument.new(argument)))
@@ -23,7 +23,7 @@ module Lino
23
23
  quoting: nil,
24
24
  placement: nil
25
25
  )
26
- return self if nil?(value)
26
+ return self if value.nil?
27
27
 
28
28
  with(options: @options.add(
29
29
  {
@@ -69,7 +69,7 @@ module Lino
69
69
  end
70
70
 
71
71
  def with_flag(flag)
72
- return self if nil?(flag)
72
+ return self if flag.nil?
73
73
 
74
74
  with(options: @options.add(
75
75
  {
@@ -4,16 +4,12 @@ module Lino
4
4
  module Builders
5
5
  module Mixins
6
6
  module Validation
7
- def nil?(value)
8
- value.nil?
9
- end
10
-
11
7
  def empty?(value)
12
8
  value.respond_to?(:empty?) && value.empty?
13
9
  end
14
10
 
15
11
  def nil_or_empty?(value)
16
- nil?(value) || empty?(value)
12
+ value.nil? || empty?(value)
17
13
  end
18
14
  end
19
15
  end
@@ -3,6 +3,10 @@
3
3
  module Lino
4
4
  module Errors
5
5
  class ExecutionError < StandardError
6
+ attr_reader :command_line,
7
+ :exit_code,
8
+ :cause
9
+
6
10
  def initialize(
7
11
  command_line = nil,
8
12
  exit_code = nil,
@@ -32,7 +32,7 @@ module Lino
32
32
 
33
33
  def with_defaults(opts)
34
34
  {
35
- stdin: opts[:stdin] || '',
35
+ stdin: opts[:stdin] ? opts[:stdin].read : '',
36
36
  stdout: opts[:stdout] || $stdout,
37
37
  stderr: opts[:stderr] || $stderr
38
38
  }
data/lib/lino/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Lino
4
- VERSION = '3.2.0.pre.10'
4
+ VERSION = '4.0.0.pre.2'
5
5
  end
data/lib/lino.rb CHANGED
@@ -10,6 +10,10 @@ module Lino
10
10
  class << self
11
11
  attr_writer :configuration
12
12
 
13
+ def builder_for_command(command)
14
+ Lino::Builders::CommandLine.new(command:)
15
+ end
16
+
13
17
  def configuration
14
18
  @configuration ||= Configuration.new
15
19
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lino
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0.pre.10
4
+ version: 4.0.0.pre.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - InfraBlocks Maintainers
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-07-09 00:00:00.000000000 Z
11
+ date: 2024-07-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: childprocess