sod 0.13.0 → 0.14.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: 064c2b356590ff7c74a2f64a269fcadc1d7bfc46fc8c7867b8627a43d4719850
4
- data.tar.gz: 481aa201574305f457cd4613aedfde6ee6e6b07ac736395eabefeccf8ee618dd
3
+ metadata.gz: c85d0a0011997592a071b7238743f7bd3fbaa8f82c91ace66199412030c35185
4
+ data.tar.gz: cedce56c426c4e6ca1f02fea043ef6f83d76aada4430bd4d41df16579b1c47e9
5
5
  SHA512:
6
- metadata.gz: 628ba8d427d6899ba4e775001b80ed36179719b18517bd0a0aa7cd3f212745d26db1532c1dc0e7f4d14b4ea65133a453409d4c0bc0cfa7d86df6bbd5b9c907bf
7
- data.tar.gz: c8d810bdd5f57bedd874220bc5ca4b0b1d51c81d470383ecfa8222452069d37678645f11e87b5c4638cc1f69575912a6fa8c5be548a447604f2d8fe49efc60d8
6
+ metadata.gz: 11de476bc015d7563655dd99b34b532aa638bfe97384b7e7f00837903248ad8ac294b61c65eaa0cd22c509645817e9e49c49a01c057dbb8ef68226c692e761ba
7
+ data.tar.gz: 3a8a383dfd4eaf2df98c1f93b8ecd7a53c722e4f4d716f6ac396dca8a76b0c56fed023473195d76b622098952037a57d55826383c2122536acce1468795560cb
checksums.yaml.gz.sig CHANGED
Binary file
data/README.adoc CHANGED
@@ -292,23 +292,43 @@ With the above in mind, let's look at a few examples of what you can do when you
292
292
 
293
293
  ===== Booleans
294
294
 
295
- Boolean flags are long alases only, take _no arguments_, and use `[no-]` syntax after the double dashes. Here's a minimal implementation:
295
+ Boolean are long alases only, use `[no-]` syntax after the double dashes, and provide the boolean value for use within your action. Here's a minimal implementation:
296
296
 
297
297
  [source,ruby]
298
298
  ----
299
- class Demo < Sod::Action
299
+ class Action < Sod::Action
300
300
  on "--[no-]run"
301
301
 
302
- def call(value) = puts "Got: #{value}"
302
+ def call(boolean) = puts boolean
303
+ end
304
+
305
+ cli = Sod.new { on Action }
306
+
307
+ cli.call %w[--run] # "true"
308
+ cli.call %w[--no-run] # "false"
309
+ ----
310
+
311
+ Because a value is always provided when using a boolean flag, you can make it a required positional parameter via your method definition (i.e. `call(boolean)`). You don't need to worry about type safety because {option_parser_link} will pass in `true` or `false` as you can see from the output above.
312
+
313
+ ===== Flags
314
+
315
+ Flags are similar to _Booleans_ but take _no arguments_ and allow short or long aliases. When a flag is supplied, the action is _enabled_ which means you can execute custom functionality. Otherwise, when a flag isn't supplied (i.e. default), then the action is _disabled_ and nothing happens.
316
+
317
+ [source,ruby]
318
+ ----
319
+ class Action < Sod::Action
320
+ on %w[-m --max]
321
+
322
+ def call(*) = puts "Maximum enabled."
303
323
  end
304
324
 
305
- cli = Sod.new { on Demo }
325
+ cli = Sod.new { on Action }
306
326
 
307
- cli.call %w[--run] # "Got: true"
308
- cli.call %w[--no-run] # "Got: false"
327
+ cli.call %w[--max] # "Maximum enabled."
328
+ cli.call # Nothing happens.
309
329
  ----
310
330
 
311
- Because a value is always provided when using a boolean flag, you can make it a required positional parameter via your method definition (i.e. `call(value)`). You don't need to worry about type safety because {option_parser_link} will either pass in a `true` or `false` value as you can see from the output above.
331
+ Since `#call` expects an argument, you can use `call(+*+)` for the method signature to ignore all arguments since you don't need them.
312
332
 
313
333
  ===== Arguments
314
334
 
@@ -316,13 +336,13 @@ Arguments inform {option_parser_link} how to parse values as either _optional_ o
316
336
 
317
337
  [source,ruby]
318
338
  ----
319
- class Demo < Sod::Action
339
+ class Action < Sod::Action
320
340
  on %w[-e --echo], argument: "[TEXT]"
321
341
 
322
342
  def call(text = nil) = puts "Got: #{text}"
323
343
  end
324
344
 
325
- cli = Sod.new { on Demo }
345
+ cli = Sod.new { on Action }
326
346
 
327
347
  cli.call %w[-e] # "Got: "
328
348
  cli.call %w[--echo] # "Got: "
@@ -334,13 +354,13 @@ The method definition of `call(text = nil)` is important because if you call the
334
354
 
335
355
  [source,ruby]
336
356
  ----
337
- class Demo < Sod::Action
357
+ class Action < Sod::Action
338
358
  on %w[-e --echo], argument: "TEXT"
339
359
 
340
360
  def call(text) = puts "Got: #{text}"
341
361
  end
342
362
 
343
- cli = Sod.new { on Demo }
363
+ cli = Sod.new { on Action }
344
364
 
345
365
  cli.call %w[-e] # "🛑 Missing argument: -e"
346
366
  cli.call %w[--echo] # "🛑 Missing argument: --echo"
@@ -348,10 +368,10 @@ cli.call %w[-e hi] # "Got: hi"
348
368
  cli.call %w[--echo hi] # "Got: hi"
349
369
  ----
350
370
 
351
- There are only three major differences between the earlier _optional_ example and the above _required_ example:
371
+ There are three major differences between a _required_ and _optional_ argument:
352
372
 
353
373
  * The argument is required because it's not wrapped in brackets.
354
- * The method definition requires a `text` parameter.
374
+ * The method definition requires a parameter (i.e. `text` in the above example).
355
375
  * You get an error when not providing an argument.
356
376
 
357
377
  ===== Types
@@ -360,13 +380,13 @@ Types are optional but worth having when you need the safety check. Here's a min
360
380
 
361
381
  [source,ruby]
362
382
  ----
363
- class Demo < Sod::Action
383
+ class Action < Sod::Action
364
384
  on %w[-e --echo], argument: "NUMBER", type: Float
365
385
 
366
386
  def call(number) = puts "Got: #{number}"
367
387
  end
368
388
 
369
- cli = Sod.new { on Demo }
389
+ cli = Sod.new { on Action }
370
390
 
371
391
  cli.call %w[--echo 123] # "Got: 123.0"
372
392
  cli.call %w[--echo 1.5] # "Got: 1.5"
@@ -381,13 +401,13 @@ Allows give you the ability to define what is acceptable as input and need to ma
381
401
 
382
402
  [source,ruby]
383
403
  ----
384
- class Demo < Sod::Action
404
+ class Action < Sod::Action
385
405
  on %w[-e --echo], argument: "TEXT", allow: %w[hi hello]
386
406
 
387
407
  def call(text) = puts "Got: #{text}"
388
408
  end
389
409
 
390
- cli = Sod.new { on Demo }
410
+ cli = Sod.new { on Action }
391
411
 
392
412
  cli.call %w[--echo hi] # "Got: hi"
393
413
  cli.call %w[--echo hello] # "Got: hello"
@@ -398,56 +418,25 @@ Here you can see the first two examples pass while the last one fails because `"
398
418
 
399
419
  ===== Defaults
400
420
 
401
- Defaults are not something that {option_parser_link} supports out-of-the-box but are handy for documentation purposes and within your implementation as fallback values. Here's a minimal example:
421
+ Defaults are not supported by {option_parser_link} but are handy for documentation purposes and within your implementation as fallback values. Here's a minimal example:
402
422
 
403
423
  [source,ruby]
404
424
  ----
405
- class Demo < Sod::Action
425
+ class Action < Sod::Action
406
426
  on %w[-e --echo], argument: "[TEXT]", default: "fallback"
407
427
 
408
- def call(text = nil) = puts "Got: #{text || default}"
428
+ def call(text = default) = puts "Got: #{text}"
409
429
  end
410
430
 
411
- cli = Sod.new { on Demo }
431
+ cli = Sod.new { on Action }
412
432
 
413
433
  cli.call %w[--echo] # "Got: fallback"
414
434
  cli.call %w[--echo hi] # "Got: hi"
415
435
  ----
416
436
 
417
- Notice how the default is printed when no value is given but is overwritten when an actual value is supplied. This is the correct way to handle defaults but might not be what you are used to. If you're thinking that you'd rather write the implementation like this:
418
-
419
- [source,ruby]
420
- ----
421
- def call(text = default) = puts "Got: #{text}"
422
- ----
423
-
424
- ...you'd not be wrong. In fact, if you initialized and called the action, you'd get what you'd expect:
425
-
426
- [source,ruby]
427
- ----
428
- demo = Demo.new
429
-
430
- demo.call # "Got: fallback"
431
- demo.call "hi" # "Got: hi"
432
- ----
433
-
434
- The reason the above is a problem is because {option_parser_link} ignores _optional_ parameters and all keywords. Here's the fully modified example:
435
-
436
- [source,ruby]
437
- ----
438
- class Demo < Sod::Action
439
- on %w[-e --echo], argument: "[TEXT]", default: "fallback"
440
-
441
- def call(text = default) = puts "Got: #{text}"
442
- end
437
+ Notice how the default is printed when no value is given but is overwritten when an actual value is supplied.
443
438
 
444
- cli = Sod.new { on Demo }
445
-
446
- cli.call %w[--echo] # "Got: "
447
- cli.call %w[--echo hi] # "Got: hi"
448
- ----
449
-
450
- Notice how there is surprising behavior with the first result (i.e. an empty string). This is because when {option_parser_link} completely ignores the value of the _optional_ parameter. I've logged an link:https://github.com/ruby/optparse/issues/55[issue] if you want to know more. For now, be aware of this quirk as it can be confusing if you are not familiar with {option_parser_link}.
439
+ 💡 If you need to lazy compute a default value, then use the block syntax instead.
451
440
 
452
441
  ===== Examples
453
442
 
@@ -464,7 +453,7 @@ class Echo < Sod::Action
464
453
 
465
454
  default { "hello" }
466
455
 
467
- def call(text = nil) = puts(text || default)
456
+ def call(text = default) = puts text
468
457
  end
469
458
 
470
459
  cli = Sod.new :demo, banner: "Demo 0.0.0: A demonstration" do
@@ -737,7 +726,9 @@ class Demo < Sod::Command
737
726
  end
738
727
  ----
739
728
 
740
- One major difference between _reusable_ and _inline_ commands is that _reusable_ commands allow you implement a `#call` method. This method is optional, so if you don't need it, you don't have to implement it. However, if you do, this means you can process the input from your actions. This method is called _after_ the option parser has parsed all command line input for your actions which gives you a handy way to process all collected input via a single command. 💡 This is how the {rubysmith_link}, {gemsmith_link}, and {hanamismith_link} gems all build new Ruby projects for you based on the actions passed to them via the CLI.
729
+ One major difference between _reusable_ and _inline_ commands is that _reusable_ commands allow you implement a `#call` method. This method is optional, so if you don't need it, you don't have to implement it. However, if you do, this means you can process the input from your actions. This method is called _after_ the option parser has parsed all command line input for your actions which gives you a handy way to process all collected input via a single command.
730
+
731
+ 💡 This is how the {rubysmith_link}, {gemsmith_link}, and {hanamismith_link} gems all build new Ruby projects for you based on the actions passed to them via the CLI.
741
732
 
742
733
  ==== Initialization
743
734
 
@@ -775,7 +766,7 @@ context = Sod::Context.new defaults_path: "path/to/defaults.yml", version_label:
775
766
  context = Sod::Context[defaults_path: "path/to/defaults.yml", version_label: "Demo 0.0.0"]
776
767
  ----
777
768
 
778
- Once you have an instance, you can use it as follows:
769
+ Once you have an instance, you can use as follows:
779
770
 
780
771
  [source,ruby]
781
772
  ----
@@ -805,7 +796,7 @@ end
805
796
 
806
797
  Types are a way to extend default {option_parser_link} functionality. Here are a few types -- not provided by {option_parser_link} -- worth knowing about:
807
798
 
808
- **Pathname**
799
+ ===== Pathname
809
800
 
810
801
  Provided by this gem and must be manually required since it's disabled by default. Example:
811
802
 
@@ -821,7 +812,7 @@ end
821
812
 
822
813
  With the above, you'll always get a link:https://rubyapi.org/o/s?q=Pathname[Pathname] instance as input to your action.
823
814
 
824
- **Version**
815
+ ===== Version
825
816
 
826
817
  Provided via the {versionaire_link} gem which gives you a `Version` type when dealing with link:https://semver.org[semantic versions]. Here's how to leverage it:
827
818
 
@@ -835,7 +826,7 @@ class Demo < Sod::Action
835
826
  end
836
827
  ----
837
828
 
838
- **Custom**
829
+ ===== Custom
839
830
 
840
831
  Creating a custom type requires minimal effort and can be implemented in only a few files:
841
832
 
@@ -6,7 +6,7 @@ module Sod
6
6
  class Loader
7
7
  include Import[:client]
8
8
 
9
- using Refines::OptionParsers
9
+ using Refines::OptionParser
10
10
 
11
11
  def initialize(graph, **)
12
12
  super(**)
@@ -6,7 +6,7 @@ module Sod
6
6
  class Runner
7
7
  include Import[:client, :logger]
8
8
 
9
- using Refines::OptionParsers
9
+ using Refines::OptionParser
10
10
 
11
11
  HELP_PATTERN = /
12
12
  \A # Start of string.
@@ -48,6 +48,8 @@ module Sod
48
48
  usage(*arguments)
49
49
  else
50
50
  parser, node = registry.fetch lineage, client
51
+ alter_callback_for parser
52
+
51
53
  parser.order! arguments, command: node do |command|
52
54
  lineage.concat(" ", command).tap(&:strip!)
53
55
  visit arguments
@@ -55,6 +57,15 @@ module Sod
55
57
  end
56
58
  end
57
59
 
60
+ # :reek:FeatureEnvy
61
+ def alter_callback_for parser
62
+ parser.define_singleton_method :callback! do |function, max_arity, *positionals|
63
+ return function.call if function.arity == -1 && positionals.empty?
64
+
65
+ super(function, max_arity, *positionals)
66
+ end
67
+ end
68
+
58
69
  def usage(*arguments)
59
70
  commands = arguments.grep_v help_pattern
60
71
  commands = lineage.split if commands.empty?
@@ -5,10 +5,10 @@ require "optparse"
5
5
  module Sod
6
6
  module Refines
7
7
  # Provides additional enhancements to the option parser primitive.
8
- module OptionParsers
9
- refine OptionParser do
10
- def order!(argument = default_argv, into: nil, command: nil, &)
11
- super(argument, into:, &)
8
+ module OptionParser
9
+ refine ::OptionParser do
10
+ def order!(argument = default_argv, into: nil, command: nil, **, &)
11
+ super(argument, into:, **, &)
12
12
  command.call if command
13
13
  end
14
14
 
data/sod.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "sod"
5
- spec.version = "0.13.0"
5
+ spec.version = "0.14.0"
6
6
  spec.authors = ["Brooke Kuhlmann"]
7
7
  spec.email = ["brooke@alchemists.io"]
8
8
  spec.homepage = "https://alchemists.io/projects/sod"
@@ -26,7 +26,8 @@ Gem::Specification.new do |spec|
26
26
  spec.add_dependency "cogger", "~> 0.21"
27
27
  spec.add_dependency "containable", "~> 0.2"
28
28
  spec.add_dependency "infusible", "~> 3.8"
29
- spec.add_dependency "refinements", "~> 12.5"
29
+ spec.add_dependency "optparse", "~> 0.5"
30
+ spec.add_dependency "refinements", "~> 12.7"
30
31
  spec.add_dependency "tone", "~> 1.0"
31
32
  spec.add_dependency "zeitwerk", "~> 2.6"
32
33
 
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sod
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brooke Kuhlmann
@@ -35,7 +35,7 @@ cert_chain:
35
35
  3n5C8/6Zh9DYTkpcwPSuIfAga6wf4nXc9m6JAw8AuMLaiWN/r/2s4zJsUHYERJEu
36
36
  gZGm4JqtuSg8pYjPeIJxS960owq+SfuC+jxqmRA54BisFCv/0VOJi7tiJVY=
37
37
  -----END CERTIFICATE-----
38
- date: 2024-07-08 00:00:00.000000000 Z
38
+ date: 2024-08-11 00:00:00.000000000 Z
39
39
  dependencies:
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: cogger
@@ -79,20 +79,34 @@ dependencies:
79
79
  - - "~>"
80
80
  - !ruby/object:Gem::Version
81
81
  version: '3.8'
82
+ - !ruby/object:Gem::Dependency
83
+ name: optparse
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '0.5'
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '0.5'
82
96
  - !ruby/object:Gem::Dependency
83
97
  name: refinements
84
98
  requirement: !ruby/object:Gem::Requirement
85
99
  requirements:
86
100
  - - "~>"
87
101
  - !ruby/object:Gem::Version
88
- version: '12.5'
102
+ version: '12.7'
89
103
  type: :runtime
90
104
  prerelease: false
91
105
  version_requirements: !ruby/object:Gem::Requirement
92
106
  requirements:
93
107
  - - "~>"
94
108
  - !ruby/object:Gem::Version
95
- version: '12.5'
109
+ version: '12.7'
96
110
  - !ruby/object:Gem::Dependency
97
111
  name: tone
98
112
  requirement: !ruby/object:Gem::Requirement
@@ -153,7 +167,7 @@ files:
153
167
  - lib/sod/prefabs/commands/config.rb
154
168
  - lib/sod/presenters/action.rb
155
169
  - lib/sod/presenters/node.rb
156
- - lib/sod/refines/option_parsers.rb
170
+ - lib/sod/refines/option_parser.rb
157
171
  - lib/sod/shell.rb
158
172
  - lib/sod/types/pathname.rb
159
173
  - sod.gemspec
@@ -183,7 +197,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
183
197
  - !ruby/object:Gem::Version
184
198
  version: '0'
185
199
  requirements: []
186
- rubygems_version: 3.5.14
200
+ rubygems_version: 3.5.17
187
201
  signing_key:
188
202
  specification_version: 4
189
203
  summary: A domain specific language for creating composable command line interfaces.
metadata.gz.sig CHANGED
Binary file