sod 0.0.0 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/README.adoc +57 -49
- data/lib/sod/action.rb +16 -4
- data/lib/sod/command.rb +20 -10
- data/lib/sod/context.rb +3 -4
- data/lib/sod/graph/node.rb +9 -8
- data/lib/sod/presenters/node.rb +6 -4
- data/sod.gemspec +2 -2
- data.tar.gz.sig +3 -4
- metadata +3 -3
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c23697cbcb0d37aa7c92b530d1952f535e8f8d338a1bfb1bc8a0f1279b589eb6
|
4
|
+
data.tar.gz: 2cbeba3bf1b3a47507735469537db88a8f9ea47fe98277a4802816d25eaf7163
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 003a046aacafec0865dd1b64b450b4fe3e4b1f6896fed015110f3df766a99f4c56fc5188fdd8c7fd1fdd94fb04cc8046b81fb67e2aeafaa6d311709cc488b7fb
|
7
|
+
data.tar.gz: d4c84e4ab0c83cfd4a537e8584c6f62ad6d5b5af737a94244b8222f1dd0a93afb6b340befe1da7d7c7ffc869c7593428c5dcf1306e4ca3ee3132e8edec69c056
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/README.adoc
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
:figure-caption!:
|
4
4
|
|
5
5
|
:cogger_link: link:https://alchemists.io/projects/cogger[Cogger]
|
6
|
-
:dry_container_link: link:https://dry-rb.org/gems/dry-container[Dry Container]
|
7
6
|
:etcher_link: link:https://alchemists.io/projects/etcher[Etcher]
|
8
7
|
:gemsmith_link: link:https://alchemists.io/projects/gemsmith[Gemsmith]
|
9
8
|
:git-lint_link: link:https://alchemists.io/projects/git-lint[Git Lint]
|
@@ -13,7 +12,6 @@
|
|
13
12
|
:option_parser_link: link:https://rubyapi.org/o/s?q=OptionParser[Option Parser]
|
14
13
|
:pennyworth_link: link:https://alchemists.io/projects/pennyworth[Pennyworth]
|
15
14
|
:pragmater_link: link:https://alchemists.io/projects/pragmater[Pragmater]
|
16
|
-
:rake_link: link:https://github.com/ruby/rake[Rake]
|
17
15
|
:rubysmith_link: link:https://alchemists.io/projects/rubysmith[Rubysmith]
|
18
16
|
:runcom_link: link:https://alchemists.io/projects/runcom[Runcom]
|
19
17
|
:spek_link: link:https://alchemists.io/projects/spek[Spek]
|
@@ -43,11 +41,11 @@ toc::[]
|
|
43
41
|
|
44
42
|
*DSL*
|
45
43
|
|
46
|
-
image::https://alchemists.io/images/projects/sod/screenshots/dsl.png[A screenshot of the DSL syntax,width=
|
44
|
+
image::https://alchemists.io/images/projects/sod/screenshots/dsl.png[A screenshot of the DSL syntax,width=539,height=479,role=focal_point]
|
47
45
|
|
48
46
|
*Output*
|
49
47
|
|
50
|
-
image::https://alchemists.io/images/projects/sod/screenshots/output.png[A screenshot of the generated help documentation,width=
|
48
|
+
image::https://alchemists.io/images/projects/sod/screenshots/output.png[A screenshot of the generated help documentation,width=472,height=498,role=focal_point]
|
51
49
|
|
52
50
|
== Requirements
|
53
51
|
|
@@ -122,8 +120,8 @@ Notice, with only a few extra lines of code, you can build upon the initial _bla
|
|
122
120
|
cli = Sod.new :demo, banner: "Demo 0.0.0: A demonstration." do
|
123
121
|
on Sod::Prefabs::Actions::Help, self
|
124
122
|
|
125
|
-
on
|
126
|
-
on
|
123
|
+
on :generate, "Generate project templates."
|
124
|
+
on :db, "Manage database."
|
127
125
|
end
|
128
126
|
|
129
127
|
cli.call
|
@@ -208,11 +206,11 @@ You've already seen some of the DSL syntax, via the earlier examples, but now we
|
|
208
206
|
[source,ruby]
|
209
207
|
----
|
210
208
|
Sod.new :demo, banner: "Demo 0.0.0: A demonstration." do
|
211
|
-
on
|
209
|
+
on :db, "Manage database." do
|
212
210
|
on Start
|
213
211
|
on Stop
|
214
212
|
|
215
|
-
on
|
213
|
+
on :structure, "Manage database structure." do
|
216
214
|
on Dump
|
217
215
|
end
|
218
216
|
end
|
@@ -265,11 +263,11 @@ To further understand the DSL, commands, and actions you'll need to start with a
|
|
265
263
|
|
266
264
|
==== Actions
|
267
265
|
|
268
|
-
Actions are the lowest building blocks of the DSL which allow you to quickly implement, test, reuse, and compose more complex architectures. They provide a nice
|
266
|
+
Actions are the lowest building blocks of the DSL which allow you to quickly implement, test, reuse, and compose more complex architectures. They provide a nice layer atop native `OptionParser#on` functionality.
|
269
267
|
|
270
268
|
There are two kinds of actions: custom and prefabricated. We'll start with custom actions and explore prefabricated actions later. Custom actions allow you to define your own functionality by inheriting from `Sod::Action` and leveraging the DSL that comes with it. Here's a high level breakdown of the macros you can use:
|
271
269
|
|
272
|
-
* `description`: Optional (but strongly encouraged). Allows you to describe your action and appears within help documentation. If the description is not defined, then your action
|
270
|
+
* `description`: Optional (but strongly encouraged). Allows you to describe your action and appears within help documentation. If the description is not defined, then only your action's handle (i.e. aliases) will be shown.
|
273
271
|
* `ancillary`: Optional. Allows you to provide supplemental text in addition to your description that might be helpful to know about when displaying help documentation. This can accept single or multiple arguments. Order matters since each argument will appear on a separate line in the order listed.
|
274
272
|
* `on`: Required. Allows you to define the behavior of your action through keyword arguments. Otherwise, if not defined, the action will be ignored. This macro mimics {option_parser_link} `#on` behavior via the following positional and keyword arguments:
|
275
273
|
** `aliases`: Required. This is a positional argument and defines the short and long form aliases of your action. Your aliases can be a single string (i.e. `on "--version"`) or an array of short and long form aliases. For example, using `on %w[-v --version]` would allow you to use `-v` or `--version` from the command line to call your action. You can also use boolean aliases such as `--build` or `--[no-]build` which the option parser will supply to your `#call` method as a boolean value.
|
@@ -283,7 +281,7 @@ There are two kinds of actions: custom and prefabricated. We'll start with custo
|
|
283
281
|
** `default`: Optional. Allows you to supply a default value and is a handy for simple values which don't require lazy evaluation via the corresponding default macro. ⚠️ This is ignored if the corresponding macro is used so ensure you use one or the other but not both.
|
284
282
|
** `description`: Optional. Allows you to define a description. Handy for short descriptions that can fit on a single line. Otherwise, for longer descriptions, use the macro. ⚠️ This is ignored if the corresponding macro is used so ensure you use one or the other but not both.
|
285
283
|
** `ancillary`: Optional. Allows you to define ancillary text to supplement your description. It can accept a string or an array. Handy for short, supplementary, text that can fit on a single line. Otherwise, for more verbose details, use the macro. ⚠️ This is ignored if the corresponding macro is used so ensure you use one or the other but not both.
|
286
|
-
* `default`: Optional. Uses a block, which is lazy evaluated, so you can define a default value. This is most helpful when used in combination with an _optional_ `argument` and/or `type` which can fallback to a safe default. This information shows up in the help text
|
284
|
+
* `default`: Optional. Uses a block, which is lazy evaluated, so you can define a default value. This is most helpful when used in combination with an _optional_ `argument` and/or `type` which can fallback to a safe default. This information shows up in the help text where the value will be rendered green text. In the case of booleans, they will be rendered as green for `true` and red for `false`.
|
287
285
|
|
288
286
|
At a minimum, you want to define the `description` and `on` macros while implementing the `#call` message. Here's an example of implementing an action that echoes input as output:
|
289
287
|
|
@@ -451,53 +449,57 @@ Commands are a step up from actions in that they allow you to organize and group
|
|
451
449
|
|
452
450
|
[source,ruby]
|
453
451
|
----
|
454
|
-
|
452
|
+
#! /usr/bin/env ruby
|
453
|
+
# frozen_string_literal: true
|
454
|
+
|
455
|
+
# Save as `snippet`, then `chmod 755 snippet`, and run as `./snippet`.
|
456
|
+
|
457
|
+
require "optparse"
|
458
|
+
|
459
|
+
input = {}
|
455
460
|
|
456
461
|
# Command
|
457
|
-
OptionParser.new do |
|
462
|
+
parser = OptionParser.new do |instance|
|
458
463
|
# Actions
|
459
|
-
|
460
|
-
|
461
|
-
parser.on("--three", "Three.") { |value| options[:three] = value }
|
464
|
+
instance.on("--[no-]one", "One.") { |value| input[:one] = value }
|
465
|
+
instance.on("--[no-]two", "Two.") { |value| input[:two] = value }
|
462
466
|
end
|
467
|
+
|
468
|
+
parser.parse ["--one", "--no-two"]
|
469
|
+
puts input
|
470
|
+
|
471
|
+
# {:one=>true, :two=>false}
|
463
472
|
----
|
464
473
|
|
465
474
|
The equivalent of the above, as provided by this gem, is:
|
466
475
|
|
467
476
|
[source,ruby]
|
468
477
|
----
|
469
|
-
|
470
|
-
|
471
|
-
require "refinements/structs"
|
472
|
-
require "sod"
|
478
|
+
#! /usr/bin/env ruby
|
479
|
+
# frozen_string_literal: true
|
473
480
|
|
474
|
-
|
475
|
-
extend Dry::Container::Mixin
|
481
|
+
# Save as `snippet`, then `chmod 755 snippet`, and run as `./snippet`.
|
476
482
|
|
477
|
-
|
478
|
-
end
|
483
|
+
require "bundler/inline"
|
479
484
|
|
480
|
-
|
485
|
+
gemfile true do
|
486
|
+
source "https://rubygems.org"
|
487
|
+
gem "sod", path: "~/Engineering/OSS/sod"
|
488
|
+
end
|
481
489
|
|
482
490
|
class One < Sod::Action
|
483
|
-
include Import[:input]
|
484
|
-
|
485
491
|
on "--[no-]one", description: "One."
|
486
492
|
|
487
|
-
def call(value) = input[:one] = value
|
493
|
+
def call(value) = context.input[:one] = value
|
488
494
|
end
|
489
495
|
|
490
496
|
class Two < Sod::Action
|
491
|
-
include Import[:input]
|
492
|
-
|
493
497
|
on "--[no-]two", description: "Two."
|
494
498
|
|
495
|
-
def call(value) = input[:two] = value
|
499
|
+
def call(value) = context.input[:two] = value
|
496
500
|
end
|
497
501
|
|
498
502
|
class Demo < Sod::Command
|
499
|
-
include Import[:input]
|
500
|
-
|
501
503
|
handle "demo"
|
502
504
|
|
503
505
|
description "A demonstration command."
|
@@ -505,11 +507,13 @@ class Demo < Sod::Command
|
|
505
507
|
on One
|
506
508
|
on Two
|
507
509
|
|
508
|
-
def call = puts input
|
510
|
+
def call = puts context.input
|
509
511
|
end
|
510
512
|
|
511
|
-
|
512
|
-
|
513
|
+
context = Sod::Context[input: {}]
|
514
|
+
|
515
|
+
cli = Sod.new banner: "Demo 0.0.0: A demonstration" do
|
516
|
+
on(Demo, context:)
|
513
517
|
on Sod::Prefabs::Actions::Help, self
|
514
518
|
end
|
515
519
|
|
@@ -518,15 +522,13 @@ cli.call ["demo", "--one", "--no-two"]
|
|
518
522
|
# {:one => true, :two => false}
|
519
523
|
----
|
520
524
|
|
521
|
-
You might be thinking: "Hey, that's more lines of code!" True but -- more importantly -- you get the benefit of composible and reusable architectures -- because each command/action is encapsulated -- which you don't get with {option_parser_link}.
|
522
|
-
|
523
|
-
By the way, the above example uses the {dry_container_link} gem for defining dependencies and the {infusible_link} gem for injecting those dependencies. You'll also notice that the `input` hash is memoized within the container to allow for mutation. The fact that you have to mutate input is a bummer and you should strive to avoid mutation whenever you can. In this case, mutation is necessary because the underlining architecture of the {option_parser_link} doesn't provide any other way to share state amongst your commands and actions. So this is one example of how you can do that.
|
525
|
+
You might be thinking: "Hey, that's more lines of code!" True but -- more importantly -- you get the benefit of composible and reusable architectures -- because each command/action is encapsulated -- which you don't get with {option_parser_link}. You'll also notice that the `input` hash is mutated. The fact that you have to mutate input is a bummer and you should strive to avoid mutation whenever you can. In this case, mutation is necessary because the underlining architecture of the {option_parser_link} doesn't provide any other way to share state amongst your commands and actions. So this is one example of how you can do that.
|
524
526
|
|
525
|
-
|
527
|
+
As mentioned earlier with actions, commands share a similar DSL with a few differences in terms of macros:
|
526
528
|
|
527
|
-
* `handle`: Required. The name of your command or the _namespace_ for which you group multiple actions. Otherwise, if not defined,
|
528
|
-
* `description`: Optional (but strongly recommended). Defines what your command is about and shows up in the help documentation. Otherwise, if not provided, your command's
|
529
|
-
* `ancillary`: Optional. Allows you to provide supplemental text for your description
|
529
|
+
* `handle`: Required. The name of your command or the _namespace_ for which you group multiple actions. Must be a string. Otherwise, if not defined, you'll get a `Sod::Error`.
|
530
|
+
* `description`: Optional (but strongly recommended). Defines what your command is about and shows up in the help documentation. Otherwise, if not provided, only your command's handle will be shown.
|
531
|
+
* `ancillary`: Optional. Allows you to provide supplemental text for your description. Can accept single or multiple arguments. Order matters since each argument will appear on a separate line in the order listed below your description.
|
530
532
|
* `on`: Required. The syntax for this is identical to the CLI DSL where you define your action (constant) as the first positional argument followed by any number of positional and/or keyword arguments that you want to feed into your action when the `.new` method is called.
|
531
533
|
|
532
534
|
If we reuse the above example and print the help documentation, you'll see the following output:
|
@@ -573,7 +575,7 @@ Inline commands provide a lightweight way to namespace your actions when you don
|
|
573
575
|
[source,ruby]
|
574
576
|
----
|
575
577
|
cli = Sod.new :demo, banner: "Demo 0.0.0: A demonstration" do
|
576
|
-
on
|
578
|
+
on :demo, "A demonstration command." do
|
577
579
|
on One
|
578
580
|
on Two
|
579
581
|
end
|
@@ -587,7 +589,7 @@ Inline commands can have ancillary text by passing in additional arguments _afte
|
|
587
589
|
[source,ruby]
|
588
590
|
----
|
589
591
|
cli = Sod.new :demo, banner: "Demo 0.0.0: A demonstration" do
|
590
|
-
on
|
592
|
+
on :demo, "A demonstration command.", "Some text.", "Some more text."
|
591
593
|
end
|
592
594
|
----
|
593
595
|
|
@@ -602,7 +604,7 @@ A _reusable_ command is what you saw earlier where you can subclass from `Sod::C
|
|
602
604
|
[source,ruby]
|
603
605
|
----
|
604
606
|
class Demo < Sod::Command
|
605
|
-
handle
|
607
|
+
handle :demo
|
606
608
|
|
607
609
|
description "A demonstration command."
|
608
610
|
|
@@ -664,7 +666,7 @@ context.defaults_path # "path/to/defaults.yml"
|
|
664
666
|
context["my/path", :defaults_path] # "my/path"
|
665
667
|
----
|
666
668
|
|
667
|
-
The override is handy for situations where you have a
|
669
|
+
The override is handy for situations where you have a value (first argument) that you would prefer to use while still being able to fallback to the `:defaults_path` if the override is `nil`. When you put all of this together, this means you can build a single context and use it within your commands and actions by injecting it:
|
668
670
|
|
669
671
|
[source,ruby]
|
670
672
|
----
|
@@ -681,7 +683,7 @@ end
|
|
681
683
|
|
682
684
|
==== Types
|
683
685
|
|
684
|
-
Types are a way to extend default {option_parser_link} functionality. Two types
|
686
|
+
Types are a way to extend default {option_parser_link} functionality. Two types -- not provided by {option_parser_link} -- worth being aware of are:
|
685
687
|
|
686
688
|
**Pathname**
|
687
689
|
|
@@ -843,7 +845,7 @@ class Echo < Sod::Action
|
|
843
845
|
end
|
844
846
|
|
845
847
|
cli = Sod.new :demo, banner: "Demo 0.0.0: A demonstration." do
|
846
|
-
on
|
848
|
+
on :db, "Manage database." do
|
847
849
|
on Start
|
848
850
|
on Stop
|
849
851
|
end
|
@@ -925,6 +927,12 @@ You can also use the IRB console for direct access to all objects:
|
|
925
927
|
bin/console
|
926
928
|
----
|
927
929
|
|
930
|
+
=== Architecture
|
931
|
+
|
932
|
+
The architecture of this gem is built entirely around {option_parser_link} by using a graph of nodes (i.e. commands) which can be walked since each node within the graph may or may not have children (i.e. nesting).
|
933
|
+
|
934
|
+
image::https://alchemists.io/images/projects/sod/doc/architecture.svg[Architecture Diagram]
|
935
|
+
|
928
936
|
== Tests
|
929
937
|
|
930
938
|
To test, run:
|
data/lib/sod/action.rb
CHANGED
@@ -8,9 +8,9 @@ module Sod
|
|
8
8
|
class Action
|
9
9
|
extend Forwardable
|
10
10
|
|
11
|
-
def self.inherited
|
11
|
+
def self.inherited subclass
|
12
12
|
super
|
13
|
-
|
13
|
+
subclass.class_eval { @attributes = {} }
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.description text
|
@@ -42,11 +42,12 @@ module Sod
|
|
42
42
|
|
43
43
|
@record = model[
|
44
44
|
**klass.instance_variable_get(:@attributes),
|
45
|
-
description:
|
46
|
-
ancillary: Array(
|
45
|
+
description: load(:description),
|
46
|
+
ancillary: Array(load(:ancillary)).compact,
|
47
47
|
default: load_default
|
48
48
|
]
|
49
49
|
|
50
|
+
verify_aliases
|
50
51
|
verify_argument
|
51
52
|
end
|
52
53
|
|
@@ -68,12 +69,23 @@ module Sod
|
|
68
69
|
|
69
70
|
private
|
70
71
|
|
72
|
+
def verify_aliases
|
73
|
+
fail Error, "Aliases must be defined." unless aliases
|
74
|
+
end
|
75
|
+
|
71
76
|
def verify_argument
|
72
77
|
return unless argument && !argument.start_with?("[") && default
|
73
78
|
|
74
79
|
fail Error, "Required argument can't be used with default."
|
75
80
|
end
|
76
81
|
|
82
|
+
def load attribute
|
83
|
+
klass = self.class
|
84
|
+
fallback = klass.instance_variable_get(:@attributes)[attribute]
|
85
|
+
|
86
|
+
klass.instance_variable_get("@#{attribute}") || fallback
|
87
|
+
end
|
88
|
+
|
77
89
|
def load_default
|
78
90
|
klass = self.class
|
79
91
|
fallback = klass.instance_variable_get(:@attributes)[:default].method :itself
|
data/lib/sod/command.rb
CHANGED
@@ -10,9 +10,9 @@ module Sod
|
|
10
10
|
|
11
11
|
include Import[:logger]
|
12
12
|
|
13
|
-
def self.inherited
|
13
|
+
def self.inherited subclass
|
14
14
|
super
|
15
|
-
|
15
|
+
subclass.class_eval { @actions = Set.new }
|
16
16
|
end
|
17
17
|
|
18
18
|
def self.handle text
|
@@ -35,16 +35,10 @@ module Sod
|
|
35
35
|
|
36
36
|
def initialize(context: Context::EMPTY, model: Models::Command, **)
|
37
37
|
super(**)
|
38
|
-
klass = self.class
|
39
38
|
@context = context
|
39
|
+
@record = build_record model
|
40
40
|
|
41
|
-
|
42
|
-
handle: klass.instance_variable_get(:@handle),
|
43
|
-
description: klass.instance_variable_get(:@description),
|
44
|
-
ancillary: Array(klass.instance_variable_get(:@ancillary)).compact,
|
45
|
-
actions: Set[*build_actions],
|
46
|
-
operation: method(:call)
|
47
|
-
]
|
41
|
+
verify_handle
|
48
42
|
end
|
49
43
|
|
50
44
|
def call
|
@@ -65,10 +59,26 @@ module Sod
|
|
65
59
|
|
66
60
|
private
|
67
61
|
|
62
|
+
def build_record model
|
63
|
+
klass = self.class
|
64
|
+
|
65
|
+
model[
|
66
|
+
handle: klass.instance_variable_get(:@handle),
|
67
|
+
description: klass.instance_variable_get(:@description),
|
68
|
+
ancillary: Array(klass.instance_variable_get(:@ancillary)).compact,
|
69
|
+
actions: Set[*build_actions],
|
70
|
+
operation: method(:call)
|
71
|
+
]
|
72
|
+
end
|
73
|
+
|
68
74
|
def build_actions
|
69
75
|
self.class.instance_variable_get(:@actions).map do |action, positionals, keywords|
|
70
76
|
action.new(*positionals, **keywords.merge!(context:))
|
71
77
|
end
|
72
78
|
end
|
79
|
+
|
80
|
+
def verify_handle
|
81
|
+
fail Error, "Invalid handle: #{handle.inspect}. Must be a string." unless handle in String
|
82
|
+
end
|
73
83
|
end
|
74
84
|
end
|
data/lib/sod/context.rb
CHANGED
@@ -11,11 +11,10 @@ module Sod
|
|
11
11
|
@attributes = attributes
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
default || public_send(fallback)
|
14
|
+
def [] override, fallback
|
15
|
+
override || public_send(fallback)
|
17
16
|
rescue NoMethodError
|
18
|
-
raise Error, "Invalid context.
|
17
|
+
raise Error, "Invalid context. Override or fallback (#{fallback.inspect}) values are missing."
|
19
18
|
end
|
20
19
|
|
21
20
|
def to_h = attributes.dup
|
data/lib/sod/graph/node.rb
CHANGED
@@ -28,14 +28,12 @@ module Sod
|
|
28
28
|
|
29
29
|
def get_child(*lineage, node: self) = lineage.empty? ? node : get(lineage, node, __method__)
|
30
30
|
|
31
|
-
def on(object, *, **, &
|
31
|
+
def on(object, *, **, &)
|
32
32
|
lineage.clear if depth.zero?
|
33
33
|
|
34
34
|
process(object, *, **)
|
35
|
-
|
36
|
-
|
37
|
-
instance_eval(&block) if block
|
38
|
-
decrement
|
35
|
+
visit(&)
|
36
|
+
self
|
39
37
|
end
|
40
38
|
|
41
39
|
def call = (operation.call if operation)
|
@@ -46,8 +44,6 @@ module Sod
|
|
46
44
|
|
47
45
|
attr_accessor :depth
|
48
46
|
|
49
|
-
# :reek:TooManyStatements
|
50
|
-
# rubocop:todo Metrics/AbcSize
|
51
47
|
def process(object, *, **)
|
52
48
|
ancestry = object.is_a?(Class) ? object.ancestors : []
|
53
49
|
|
@@ -61,7 +57,6 @@ module Sod
|
|
61
57
|
fail Error, "Invalid command or action. Unable to add: #{object.inspect}."
|
62
58
|
end
|
63
59
|
end
|
64
|
-
# rubocop:enable Metrics/AbcSize
|
65
60
|
|
66
61
|
def add_inline_command handle, *positionals
|
67
62
|
description, *ancillary = positionals
|
@@ -99,6 +94,12 @@ module Sod
|
|
99
94
|
public_send(message, *lineage, node:)
|
100
95
|
end
|
101
96
|
|
97
|
+
def visit &block
|
98
|
+
increment
|
99
|
+
instance_eval(&block) if block
|
100
|
+
decrement
|
101
|
+
end
|
102
|
+
|
102
103
|
def increment = self.depth += 1
|
103
104
|
|
104
105
|
def decrement = self.depth -= 1
|
data/lib/sod/presenters/node.rb
CHANGED
@@ -16,7 +16,7 @@ module Sod
|
|
16
16
|
using Refinements::Arrays
|
17
17
|
using Refinements::Strings
|
18
18
|
|
19
|
-
delegate
|
19
|
+
delegate %i[handle description ancillary operation children] => :node
|
20
20
|
|
21
21
|
attr_reader :actions
|
22
22
|
|
@@ -32,9 +32,9 @@ module Sod
|
|
32
32
|
# rubocop:enable Metrics/ParameterLists
|
33
33
|
|
34
34
|
def to_s
|
35
|
-
[banner, "", *usage, "", *colored_actions, "", *colored_commands].tap(&:compact!)
|
36
|
-
|
37
|
-
|
35
|
+
[banner, body, "", *usage, "", *colored_actions, "", *colored_commands].tap(&:compact!)
|
36
|
+
.join("\n")
|
37
|
+
.strip
|
38
38
|
end
|
39
39
|
|
40
40
|
private
|
@@ -43,6 +43,8 @@ module Sod
|
|
43
43
|
|
44
44
|
def banner = color[description, :bold]
|
45
45
|
|
46
|
+
def body = ancillary.empty? ? nil : ancillary.join("\n").prepend("\n")
|
47
|
+
|
46
48
|
def usage
|
47
49
|
actions = " #{colored_handle} [OPTIONS]" unless all.empty?
|
48
50
|
commands = " #{colored_handle} COMMAND [OPTIONS]" unless children.empty?
|
data/sod.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |spec|
|
4
4
|
spec.name = "sod"
|
5
|
-
spec.version = "0.
|
5
|
+
spec.version = "0.1.0"
|
6
6
|
spec.authors = ["Brooke Kuhlmann"]
|
7
7
|
spec.email = ["brooke@alchemists.io"]
|
8
8
|
spec.homepage = "https://alchemists.io/projects/sod"
|
9
|
-
spec.summary = "A
|
9
|
+
spec.summary = "A domain specific language for creating composable command line interfaces."
|
10
10
|
spec.license = "Hippocratic-2.1"
|
11
11
|
|
12
12
|
spec.metadata = {
|
data.tar.gz.sig
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
���
|
4
|
-
i�����7/v|
|
1
|
+
I�n�����M`U�sbÉn�aV����m�-ᭌ�bi���1�U ����%�8���Y���x�mV�j���n��t��JQ�tx�gH��i��z*y�����~�iV٢�X�R4z o�.S��,z��3�w{
|
2
|
+
i���ȖR��a�J�Kjx��l�aA-�w �M�z�"t/B��H[�1Ę��F@O��
|
3
|
+
^(�RF���~(x���X׳�r��|6�^).�r?�m�O�\�x�y�0�
|
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.
|
4
|
+
version: 0.1.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: 2023-06-
|
38
|
+
date: 2023-06-20 00:00:00.000000000 Z
|
39
39
|
dependencies:
|
40
40
|
- !ruby/object:Gem::Dependency
|
41
41
|
name: cogger
|
@@ -186,5 +186,5 @@ requirements: []
|
|
186
186
|
rubygems_version: 3.4.14
|
187
187
|
signing_key:
|
188
188
|
specification_version: 4
|
189
|
-
summary: A
|
189
|
+
summary: A domain specific language for creating composable command line interfaces.
|
190
190
|
test_files: []
|
metadata.gz.sig
CHANGED
Binary file
|