lino 3.2.0.pre.10 → 4.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7cb69956b6fc3389deb0f7747c7ab5023225e95bee92ec99d1f7492d1a9020c4
4
- data.tar.gz: 4fb9eeea0d1be41bae1d8864cbf0893c0dcfa9f654e5730b69ffd0d396c2106c
3
+ metadata.gz: 2345fde1348f94927b9f8790c0a3d77a28b1bd0829eaa408249ef1b9ad06a9b1
4
+ data.tar.gz: 2ed278cbe9a30e2df451ccfa78bf592a2ceb6654bb70d1d37f4a3625aae37371
5
5
  SHA512:
6
- metadata.gz: aa736d59d32a7358972e4940035f4767c077e2d1af7edf6630c144cb07b9235788f086c22589d64bfd4f00f0e7ddf02dc01d5bf1976bac496b4c696a90a4a8ed
7
- data.tar.gz: aff892172f4752f99f0b4d7d68075aa60ba20a6e5069ee8adb59a32c0d0cc7b6c7784420d7a7910ba97f8022614523642d9d6ef75bf00bb9392abf2fb86c4b01
6
+ metadata.gz: 256de30d81d5bd07e9047878c3ed4691d2052997425c95d7a33bdba5cbf402cf3fcd5356945882fb5e8cbd85346e322a36c44c995ff32392c52435163314aac8
7
+ data.tar.gz: 4fdee42f644db2994a90d0de0465f8d04df02eac2be62f08d755b272dc75278f4894a7772f169935f8a7fc7e9c429466e86980491700e858cc014fe8f5ac9dce
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)
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'
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
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
@@ -336,9 +336,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
336
336
  version: '3.1'
337
337
  required_rubygems_version: !ruby/object:Gem::Requirement
338
338
  requirements:
339
- - - ">"
339
+ - - ">="
340
340
  - !ruby/object:Gem::Version
341
- version: 1.3.1
341
+ version: '0'
342
342
  requirements: []
343
343
  rubygems_version: 3.3.7
344
344
  signing_key: