sod 0.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.
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "refinements/pathnames"
4
+
5
+ module Sod
6
+ module Prefabs
7
+ module Actions
8
+ module Config
9
+ # Displays project configuration.
10
+ class View < Action
11
+ include Import[:kernel, :logger]
12
+
13
+ using Refinements::Pathnames
14
+
15
+ description "View project configuration."
16
+
17
+ on %w[-v --view]
18
+
19
+ # :reek:ControlParameter
20
+ def initialize(path = nil, **)
21
+ super(**)
22
+ @path = Pathname(path || context.xdg_config.active)
23
+ end
24
+
25
+ def call(*)
26
+ return unless check
27
+
28
+ logger.info { "Viewing (#{path.to_s.inspect}):" }
29
+ kernel.puts path.read
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :path
35
+
36
+ def check
37
+ return true if path.exist?
38
+
39
+ logger.error { "Configuration doesn't exist: #{path.to_s.inspect}." }
40
+ kernel.abort
41
+ false
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sod
4
+ module Prefabs
5
+ module Actions
6
+ # Displays help (usage) information.
7
+ class Help < Action
8
+ include Import[:kernel]
9
+
10
+ description "Show this message."
11
+
12
+ on %w[-h --help], argument: "[COMMAND]"
13
+
14
+ def initialize(graph, presenter: Presenters::Node, **)
15
+ super(**)
16
+ @graph = graph
17
+ @presenter = presenter
18
+ end
19
+
20
+ def call *lineage
21
+ if lineage.empty?
22
+ kernel.puts presenter.new(graph).to_s
23
+ else
24
+ kernel.puts presenter.new(graph.get_child(*lineage)).to_s
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :graph, :presenter
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sod
4
+ module Prefabs
5
+ module Actions
6
+ # Provides a generic version action for use in upstream applications.
7
+ class Version < Action
8
+ include Import[:kernel]
9
+
10
+ description "Show version."
11
+
12
+ on %w[-v --version]
13
+
14
+ def initialize(label = nil, **)
15
+ super(**)
16
+ @label = context[label, :version_label]
17
+ end
18
+
19
+ def call(*) = kernel.puts label
20
+
21
+ private
22
+
23
+ attr_reader :label
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sod
4
+ module Prefabs
5
+ module Commands
6
+ # Provides a generic configuration command for use in upstream applications.
7
+ class Config < Sod::Command
8
+ handle "config"
9
+
10
+ description "Manage configuration."
11
+
12
+ ancillary "Path is dynamic per current directory."
13
+
14
+ on Prefabs::Actions::Config::Create
15
+ on Prefabs::Actions::Config::Edit
16
+ on Prefabs::Actions::Config::View
17
+ on Prefabs::Actions::Config::Delete
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+ require "refinements/arrays"
5
+
6
+ module Sod
7
+ module Presenters
8
+ # Aids in rendering an action for display.
9
+ class Action
10
+ include Import[:color]
11
+
12
+ extend Forwardable
13
+
14
+ using Refinements::Arrays
15
+
16
+ delegate [*Models::Action.members, :handle] => :record
17
+
18
+ def initialize(record, **)
19
+ super(**)
20
+ @record = record
21
+ end
22
+
23
+ def colored_handle = [color_aliases, argument].tap(&:compact!).join(" ")
24
+
25
+ def colored_documentation = [*ancillary, color_allows, color_default].tap(&:compact!)
26
+
27
+ private
28
+
29
+ attr_reader :record
30
+
31
+ def color_aliases
32
+ Array(record.aliases).map { |value| color[value, :cyan] }
33
+ .join ", "
34
+ end
35
+
36
+ def color_allows
37
+ return unless allow
38
+
39
+ values = Array(allow).map { |value| color[value, :green] }
40
+ .to_sentence "or"
41
+ "Use: #{values}."
42
+ end
43
+
44
+ def color_default
45
+ cast = default.to_s
46
+
47
+ return if cast.empty?
48
+
49
+ value = cast == "false" ? color[default, :red] : color[default, :green]
50
+ "Default: #{value}."
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+ require "refinements/arrays"
5
+ require "refinements/strings"
6
+
7
+ module Sod
8
+ module Presenters
9
+ # Aids in rendering a node for display.
10
+ # :reek:TooManyInstanceVariables
11
+ class Node
12
+ include Import[:color]
13
+
14
+ extend Forwardable
15
+
16
+ using Refinements::Arrays
17
+ using Refinements::Strings
18
+
19
+ delegate Graph::Node.members => :node
20
+
21
+ attr_reader :actions
22
+
23
+ # rubocop:todo Metrics/ParameterLists
24
+ def initialize(node, indent: 2, gap: 5, action_presenter: Presenters::Action, **)
25
+ super(**)
26
+ @node = node
27
+ @indent = indent
28
+ @gap = gap
29
+ @actions = node.actions.map { |action| action_presenter.new action.record }
30
+ @all = actions + children.to_a
31
+ end
32
+ # rubocop:enable Metrics/ParameterLists
33
+
34
+ def to_s
35
+ [banner, "", *usage, "", *colored_actions, "", *colored_commands].tap(&:compact!)
36
+ .join("\n")
37
+ .strip
38
+ end
39
+
40
+ private
41
+
42
+ attr_reader :node, :indent, :gap, :all
43
+
44
+ def banner = color[description, :bold]
45
+
46
+ def usage
47
+ actions = " #{colored_handle} [OPTIONS]" unless all.empty?
48
+ commands = " #{colored_handle} COMMAND [OPTIONS]" unless children.empty?
49
+
50
+ add_section "USAGE", [actions, commands].tap(&:compact!)
51
+ end
52
+
53
+ def colored_handle = color[handle, :cyan]
54
+
55
+ def colored_actions
56
+ return if actions.empty?
57
+
58
+ collection = actions.each_with_object [] do |action, content|
59
+ content.append " #{action.colored_handle}#{description_padding action}" \
60
+ "#{action.description}"
61
+ add_ancillary action, :colored_documentation, content
62
+ end
63
+
64
+ add_section "OPTIONS", collection
65
+ end
66
+
67
+ def colored_commands
68
+ return if children.empty?
69
+
70
+ collection = children.each_with_object [] do |command, content|
71
+ content.append " #{color[command.handle, :cyan]}#{description_padding command}" \
72
+ "#{command.description}"
73
+ add_ancillary command, :ancillary, content
74
+ end
75
+
76
+ add_section "COMMANDS", collection
77
+ end
78
+
79
+ def description_padding(item) = " " * ((max_handle_size - item.handle.size) + gap)
80
+
81
+ def max_handle_size = all.map(&:handle).maximum :size
82
+
83
+ def add_ancillary target, message, content
84
+ target.public_send(message).each do |line|
85
+ content.append line.indent (max_handle_size + gap + indent), pad: " "
86
+ end
87
+ end
88
+
89
+ # :reek:FeatureEnvy
90
+ def add_section text, collection
91
+ collection.empty? ? collection : collection.prepend(color[text, :bold, :underline])
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+
5
+ module Sod
6
+ module Refines
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:, &)
12
+ command.call if command
13
+ end
14
+
15
+ def replicate
16
+ self.class.new banner, summary_width, summary_indent do |instance|
17
+ instance.set_program_name program_name
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
data/lib/sod/shell.rb ADDED
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cogger"
4
+
5
+ module Sod
6
+ # The Command Line Interface (CLI).
7
+ class Shell
8
+ attr_reader :name, :banner
9
+
10
+ # rubocop:todo Metrics/ParameterLists
11
+ def initialize name = Cogger::Program.call,
12
+ banner: nil,
13
+ node: Graph::Node,
14
+ runner: Graph::Runner,
15
+ &block
16
+ @name = name.to_s
17
+ @banner = banner
18
+ graph = node[handle: name, description: banner]
19
+ graph.instance_eval(&block) if block
20
+ @runner = runner.new graph
21
+ end
22
+ # rubocop:enable Metrics/ParameterLists
23
+
24
+ def call arguments = ARGV, process: Process
25
+ process.setproctitle name
26
+ runner.call arguments
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :runner
32
+ end
33
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+ require "pathname"
5
+
6
+ OptionParser.accept(Pathname) { |value| Pathname value }
data/lib/sod.rb ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zeitwerk"
4
+
5
+ Zeitwerk::Loader.for_gem.then do |loader|
6
+ loader.ignore "#{__dir__}/sod/types"
7
+ loader.setup
8
+ end
9
+
10
+ # Main namespace.
11
+ module Sod
12
+ def self.new(...) = Shell.new(...)
13
+ end
data/sod.gemspec ADDED
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "sod"
5
+ spec.version = "0.0.0"
6
+ spec.authors = ["Brooke Kuhlmann"]
7
+ spec.email = ["brooke@alchemists.io"]
8
+ spec.homepage = "https://alchemists.io/projects/sod"
9
+ spec.summary = "A Domain Specific Language for creating composable Command Line Interfaces."
10
+ spec.license = "Hippocratic-2.1"
11
+
12
+ spec.metadata = {
13
+ "bug_tracker_uri" => "https://github.com/bkuhlmann/sod/issues",
14
+ "changelog_uri" => "https://alchemists.io/projects/sod/versions",
15
+ "documentation_uri" => "https://alchemists.io/projects/sod",
16
+ "funding_uri" => "https://github.com/sponsors/bkuhlmann",
17
+ "label" => "Sod",
18
+ "rubygems_mfa_required" => "true",
19
+ "source_code_uri" => "https://github.com/bkuhlmann/sod"
20
+ }
21
+
22
+ spec.signing_key = Gem.default_key_path
23
+ spec.cert_chain = [Gem.default_cert_path]
24
+
25
+ spec.required_ruby_version = "~> 3.2"
26
+ spec.add_dependency "cogger", "~> 0.10"
27
+ spec.add_dependency "dry-container", "~> 0.11"
28
+ spec.add_dependency "infusible", "~> 2.0"
29
+ spec.add_dependency "refinements", "~> 11.0"
30
+ spec.add_dependency "tone", "~> 0.3"
31
+ spec.add_dependency "zeitwerk", "~> 2.6"
32
+
33
+ spec.extra_rdoc_files = Dir["README*", "LICENSE*"]
34
+ spec.files = Dir["*.gemspec", "lib/**/*"]
35
+ end
data.tar.gz.sig ADDED
@@ -0,0 +1,4 @@
1
+ �ē�U����{�RAm���� \�o�CO�%�~���}h�%J�p�g�}�o�ދ[_��N��� ��-ǩ2��&Y�'9���(�$�؍���v�q�����"W���Ǽa��a`)p�.p�0(z2���Gp)r5g��u�X���a����ć��LW�
2
+ ��F���o��c`��'���U�$�^�`cTe!P��
3
+ ���z�����-R?�I%d{L�62=�#}�Pp��ʫ6sVѣ�H
4
+ i�����7/v|
metadata ADDED
@@ -0,0 +1,190 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sod
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Brooke Kuhlmann
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIEeDCCAuCgAwIBAgIBATANBgkqhkiG9w0BAQsFADBBMQ8wDQYDVQQDDAZicm9v
14
+ a2UxGjAYBgoJkiaJk/IsZAEZFgphbGNoZW1pc3RzMRIwEAYKCZImiZPyLGQBGRYC
15
+ aW8wHhcNMjMwMzIyMTYxNDQxWhcNMjUwMzIxMTYxNDQxWjBBMQ8wDQYDVQQDDAZi
16
+ cm9va2UxGjAYBgoJkiaJk/IsZAEZFgphbGNoZW1pc3RzMRIwEAYKCZImiZPyLGQB
17
+ GRYCaW8wggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCro8tj5/E1Hg88
18
+ f4qfiwPVd2zJQHvdYt4GHVvuHRRgx4HGhJuNp+4BId08RBn7V6V1MW6MY3kezRBs
19
+ M+7QOQ4b1xNLTvY7FYQB1wGK5a4x7TTokDrPYQxDB2jmsdDYCzVbIMrAvUfcecRi
20
+ khyGZCdByiiCl4fKv77P12tTT+NfsvXkLt/AYCGwjOUyGKTQ01Z6eC09T27GayPH
21
+ QQvIkakyFgcJtzSyGzs8bzK5q9u7wQ12MNTjJoXzW69lqp0oNvDylu81EiSUb5S6
22
+ QzzPxZBiRB1sgtbt1gUbVI262ZDq1gR+HxPFmp+Cgt7ZLIJZAtesQvtcMzseXpfn
23
+ hpmm0Sw22KGhRAy/mqHBRhDl5HqS1SJp2Ko3lcnpXeFResp0HNlt8NSu13vhC08j
24
+ GUHU9MyIXbFOsnp3K3ADrAVjPWop8EZkmUR3MV/CUm00w2cZHCSGiXl1KMpiVKvk
25
+ Ywr1gd2ZME4QLSo+EXUtLxDUa/W3xnBS8dBOuMMz02FPWYr3PN8CAwEAAaN7MHkw
26
+ CQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFAFgmv0tYMZnItuPycSM
27
+ F5wykJEVMB8GA1UdEQQYMBaBFGJyb29rZUBhbGNoZW1pc3RzLmlvMB8GA1UdEgQY
28
+ MBaBFGJyb29rZUBhbGNoZW1pc3RzLmlvMA0GCSqGSIb3DQEBCwUAA4IBgQAX+EGY
29
+ 9RLYGxF1VLZz+G1ACQc4uyrCB6kXwI06kzUa5dF9tPXqTX9ffnz3/W8ck2IQhKzu
30
+ MKO2FVijzbDWTsZeZGglS4E+4Jxpau1lU9HhOIcKolv6LeC6UdALTFudY+GLb8Xw
31
+ REXgaJkjzzhkUSILmEnRwEbY08dVSl7ZAaxVI679vfI2yapLlIwpbBgmQTiTvPr3
32
+ qyyLUno9flYEOv9fmGHunSrM+gE0/0niGTXa5GgXBXYGS2he4LQGgSBfGp/cTwMU
33
+ rDKJRcusZ12lNBeDfgqACz/BBJF8FLodgk6rGMRZz7+ZmjjHEmpG5bQpR6Q2BuWL
34
+ XMtYk/QzaWuhiR7pWjiF8jbdd7RO6or0ohq7iFkokz/5xrtQ/vPzU2RQ3Qc6YaKw
35
+ 3n5C8/6Zh9DYTkpcwPSuIfAga6wf4nXc9m6JAw8AuMLaiWN/r/2s4zJsUHYERJEu
36
+ gZGm4JqtuSg8pYjPeIJxS960owq+SfuC+jxqmRA54BisFCv/0VOJi7tiJVY=
37
+ -----END CERTIFICATE-----
38
+ date: 2023-06-15 00:00:00.000000000 Z
39
+ dependencies:
40
+ - !ruby/object:Gem::Dependency
41
+ name: cogger
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '0.10'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.10'
54
+ - !ruby/object:Gem::Dependency
55
+ name: dry-container
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.11'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.11'
68
+ - !ruby/object:Gem::Dependency
69
+ name: infusible
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '2.0'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '2.0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: refinements
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '11.0'
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '11.0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: tone
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '0.3'
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '0.3'
110
+ - !ruby/object:Gem::Dependency
111
+ name: zeitwerk
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '2.6'
117
+ type: :runtime
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '2.6'
124
+ description:
125
+ email:
126
+ - brooke@alchemists.io
127
+ executables: []
128
+ extensions: []
129
+ extra_rdoc_files:
130
+ - README.adoc
131
+ - LICENSE.adoc
132
+ files:
133
+ - LICENSE.adoc
134
+ - README.adoc
135
+ - lib/sod.rb
136
+ - lib/sod/action.rb
137
+ - lib/sod/command.rb
138
+ - lib/sod/container.rb
139
+ - lib/sod/context.rb
140
+ - lib/sod/error.rb
141
+ - lib/sod/graph/loader.rb
142
+ - lib/sod/graph/node.rb
143
+ - lib/sod/graph/runner.rb
144
+ - lib/sod/import.rb
145
+ - lib/sod/models/action.rb
146
+ - lib/sod/models/command.rb
147
+ - lib/sod/prefabs/actions/config/create.rb
148
+ - lib/sod/prefabs/actions/config/delete.rb
149
+ - lib/sod/prefabs/actions/config/edit.rb
150
+ - lib/sod/prefabs/actions/config/view.rb
151
+ - lib/sod/prefabs/actions/help.rb
152
+ - lib/sod/prefabs/actions/version.rb
153
+ - lib/sod/prefabs/commands/config.rb
154
+ - lib/sod/presenters/action.rb
155
+ - lib/sod/presenters/node.rb
156
+ - lib/sod/refines/option_parsers.rb
157
+ - lib/sod/shell.rb
158
+ - lib/sod/types/pathname.rb
159
+ - sod.gemspec
160
+ homepage: https://alchemists.io/projects/sod
161
+ licenses:
162
+ - Hippocratic-2.1
163
+ metadata:
164
+ bug_tracker_uri: https://github.com/bkuhlmann/sod/issues
165
+ changelog_uri: https://alchemists.io/projects/sod/versions
166
+ documentation_uri: https://alchemists.io/projects/sod
167
+ funding_uri: https://github.com/sponsors/bkuhlmann
168
+ label: Sod
169
+ rubygems_mfa_required: 'true'
170
+ source_code_uri: https://github.com/bkuhlmann/sod
171
+ post_install_message:
172
+ rdoc_options: []
173
+ require_paths:
174
+ - lib
175
+ required_ruby_version: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - "~>"
178
+ - !ruby/object:Gem::Version
179
+ version: '3.2'
180
+ required_rubygems_version: !ruby/object:Gem::Requirement
181
+ requirements:
182
+ - - ">="
183
+ - !ruby/object:Gem::Version
184
+ version: '0'
185
+ requirements: []
186
+ rubygems_version: 3.4.14
187
+ signing_key:
188
+ specification_version: 4
189
+ summary: A Domain Specific Language for creating composable Command Line Interfaces.
190
+ test_files: []
metadata.gz.sig ADDED
Binary file