gemsmith 15.5.0 → 17.0.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/LICENSE.adoc +207 -155
- data/README.adoc +189 -196
- data/exe/gemsmith +6 -0
- data/gemsmith.gemspec +40 -0
- data/lib/gemsmith/builders/bundler.rb +34 -0
- data/lib/gemsmith/builders/circle_ci.rb +32 -0
- data/lib/gemsmith/builders/cli.rb +86 -0
- data/lib/gemsmith/builders/documentation/readme.rb +37 -0
- data/lib/gemsmith/builders/git/commit.rb +46 -0
- data/lib/gemsmith/builders/git/ignore.rb +37 -0
- data/lib/gemsmith/builders/rspec/helper.rb +47 -0
- data/lib/gemsmith/builders/specification.rb +35 -0
- data/lib/gemsmith/cli/actions/build.rb +55 -0
- data/lib/gemsmith/cli/actions/config.rb +35 -0
- data/lib/gemsmith/cli/actions/edit.rb +39 -0
- data/lib/gemsmith/cli/actions/install.rb +37 -0
- data/lib/gemsmith/cli/actions/publish.rb +37 -0
- data/lib/gemsmith/cli/actions/view.rb +39 -0
- data/lib/gemsmith/cli/parser.rb +33 -0
- data/lib/gemsmith/cli/parsers/build.rb +54 -0
- data/lib/gemsmith/cli/parsers/core.rb +94 -0
- data/lib/gemsmith/cli/shell.rb +66 -0
- data/lib/gemsmith/configuration/enhancers/template_root.rb +20 -0
- data/lib/gemsmith/configuration/loader.rb +50 -0
- data/lib/gemsmith/container.rb +22 -0
- data/lib/gemsmith/templates/%project_name%/%project_name%.gemspec.erb +50 -0
- data/lib/gemsmith/templates/%project_name%/exe/%project_name%.erb +5 -0
- data/lib/gemsmith/templates/%project_name%/lib/%project_path%/cli/actions/config.rb.erb +35 -0
- data/lib/gemsmith/templates/%project_name%/lib/%project_path%/cli/parser.rb.erb +31 -0
- data/lib/gemsmith/templates/%project_name%/lib/%project_path%/cli/parsers/core.rb.erb +59 -0
- data/lib/gemsmith/templates/%project_name%/lib/%project_path%/cli/shell.rb.erb +40 -0
- data/lib/gemsmith/templates/%project_name%/lib/%project_path%/configuration/content.rb.erb +18 -0
- data/lib/gemsmith/templates/%project_name%/lib/%project_path%/configuration/defaults.yml.erb +0 -0
- data/lib/gemsmith/templates/%project_name%/lib/%project_path%/configuration/loader.rb.erb +33 -0
- data/lib/gemsmith/templates/%project_name%/lib/%project_path%/container.rb.erb +37 -0
- data/lib/gemsmith/templates/{%gem_name%/lib/%gem_path%/identity.rb.tt → %project_name%/lib/%project_path%/identity.rb.erb} +1 -1
- data/lib/gemsmith/templates/%project_name%/spec/lib/%project_path%/cli/actions/config_spec.rb.erb +24 -0
- data/lib/gemsmith/templates/%project_name%/spec/lib/%project_path%/cli/parser_spec.rb.erb +25 -0
- data/lib/gemsmith/templates/%project_name%/spec/lib/%project_path%/cli/parsers/core_spec.rb.erb +53 -0
- data/lib/gemsmith/templates/%project_name%/spec/lib/%project_path%/cli/shell_spec.rb.erb +43 -0
- data/lib/gemsmith/templates/%project_name%/spec/lib/%project_path%/configuration/content_spec.rb.erb +15 -0
- data/lib/gemsmith/templates/%project_name%/spec/lib/%project_path%/configuration/loader_spec.rb.erb +29 -0
- data/lib/gemsmith/templates/%project_name%/spec/support/shared_contexts/application_container.rb.erb +22 -0
- data/lib/gemsmith/templates/%project_name%/spec/support/shared_examples/a_parser.rb.erb +7 -0
- data/lib/gemsmith/tools/cleaner.rb +29 -0
- data/lib/gemsmith/tools/editor.rb +30 -0
- data/lib/gemsmith/tools/installer.rb +39 -0
- data/lib/gemsmith/tools/packager.rb +33 -0
- data/lib/gemsmith/tools/publisher.rb +34 -0
- data/lib/gemsmith/tools/pusher.rb +36 -0
- data/lib/gemsmith/tools/validator.rb +28 -0
- data/lib/gemsmith/tools/versioner.rb +43 -0
- data/lib/gemsmith/tools/viewer.rb +28 -0
- data/lib/gemsmith.rb +5 -3
- data.tar.gz.sig +0 -0
- metadata +88 -104
- metadata.gz.sig +0 -0
- data/bin/gemsmith +0 -9
- data/lib/gemsmith/authenticators/basic.rb +0 -28
- data/lib/gemsmith/authenticators/ruby_gems.rb +0 -41
- data/lib/gemsmith/cli.rb +0 -259
- data/lib/gemsmith/credentials.rb +0 -82
- data/lib/gemsmith/errors/base.rb +0 -9
- data/lib/gemsmith/errors/requirement_conversion.rb +0 -9
- data/lib/gemsmith/errors/requirement_operator.rb +0 -9
- data/lib/gemsmith/errors/specification.rb +0 -9
- data/lib/gemsmith/gem/inspector.rb +0 -30
- data/lib/gemsmith/gem/module_formatter.rb +0 -50
- data/lib/gemsmith/gem/requirement.rb +0 -55
- data/lib/gemsmith/gem/specification.rb +0 -74
- data/lib/gemsmith/generators/base.rb +0 -46
- data/lib/gemsmith/generators/bundler.rb +0 -19
- data/lib/gemsmith/generators/bundler_audit.rb +0 -15
- data/lib/gemsmith/generators/circle_ci.rb +0 -14
- data/lib/gemsmith/generators/cli.rb +0 -17
- data/lib/gemsmith/generators/documentation.rb +0 -36
- data/lib/gemsmith/generators/engine.rb +0 -77
- data/lib/gemsmith/generators/gem.rb +0 -29
- data/lib/gemsmith/generators/git.rb +0 -45
- data/lib/gemsmith/generators/git_hub.rb +0 -15
- data/lib/gemsmith/generators/git_lint.rb +0 -14
- data/lib/gemsmith/generators/guard.rb +0 -14
- data/lib/gemsmith/generators/pragma.rb +0 -49
- data/lib/gemsmith/generators/rake.rb +0 -76
- data/lib/gemsmith/generators/reek.rb +0 -17
- data/lib/gemsmith/generators/rspec.rb +0 -39
- data/lib/gemsmith/generators/rubocop.rb +0 -18
- data/lib/gemsmith/generators/ruby.rb +0 -12
- data/lib/gemsmith/helpers/cli.rb +0 -59
- data/lib/gemsmith/helpers/template.rb +0 -30
- data/lib/gemsmith/identity.rb +0 -12
- data/lib/gemsmith/rake/builder.rb +0 -57
- data/lib/gemsmith/rake/publisher.rb +0 -100
- data/lib/gemsmith/rake/setup.rb +0 -4
- data/lib/gemsmith/rake/tasks.rb +0 -83
- data/lib/gemsmith/templates/%gem_name%/%gem_name%.gemspec.tt +0 -44
- data/lib/gemsmith/templates/%gem_name%/.circleci/config.yml.tt +0 -31
- data/lib/gemsmith/templates/%gem_name%/.github/ISSUE_TEMPLATE.md.tt +0 -14
- data/lib/gemsmith/templates/%gem_name%/.github/PULL_REQUEST_TEMPLATE.md.tt +0 -11
- data/lib/gemsmith/templates/%gem_name%/.gitignore.tt +0 -6
- data/lib/gemsmith/templates/%gem_name%/.reek.yml.tt +0 -6
- data/lib/gemsmith/templates/%gem_name%/.rubocop.yml.tt +0 -15
- data/lib/gemsmith/templates/%gem_name%/.ruby-version.tt +0 -1
- data/lib/gemsmith/templates/%gem_name%/CHANGES.md.tt +0 -3
- data/lib/gemsmith/templates/%gem_name%/CODE_OF_CONDUCT.md.tt +0 -66
- data/lib/gemsmith/templates/%gem_name%/CONTRIBUTING.md.tt +0 -22
- data/lib/gemsmith/templates/%gem_name%/Gemfile.tt +0 -47
- data/lib/gemsmith/templates/%gem_name%/Guardfile.tt +0 -21
- data/lib/gemsmith/templates/%gem_name%/LICENSE.md.tt +0 -20
- data/lib/gemsmith/templates/%gem_name%/README.md.tt +0 -93
- data/lib/gemsmith/templates/%gem_name%/Rakefile.tt +0 -12
- data/lib/gemsmith/templates/%gem_name%/bin/%gem_name%.tt +0 -8
- data/lib/gemsmith/templates/%gem_name%/bin/console.tt +0 -10
- data/lib/gemsmith/templates/%gem_name%/bin/setup.tt +0 -8
- data/lib/gemsmith/templates/%gem_name%/lib/%gem_path%/cli.rb.tt +0 -61
- data/lib/gemsmith/templates/%gem_name%/lib/%gem_path%/engine.rb.tt +0 -6
- data/lib/gemsmith/templates/%gem_name%/lib/%gem_path%.rb.tt +0 -7
- data/lib/gemsmith/templates/%gem_name%/lib/generators/%gem_path%/install/USAGE.tt +0 -8
- data/lib/gemsmith/templates/%gem_name%/lib/generators/%gem_path%/install/install_generator.rb.tt +0 -12
- data/lib/gemsmith/templates/%gem_name%/lib/generators/%gem_path%/upgrade/USAGE.tt +0 -8
- data/lib/gemsmith/templates/%gem_name%/lib/generators/%gem_path%/upgrade/upgrade_generator.rb.tt +0 -10
- data/lib/gemsmith/templates/%gem_name%/spec/lib/%gem_path%/cli_spec.rb.tt +0 -81
- data/lib/gemsmith/templates/%gem_name%/spec/rails_helper.rb.tt +0 -14
- data/lib/gemsmith/templates/%gem_name%/spec/spec_helper.rb.tt +0 -37
- data/lib/gemsmith/templates/%gem_name%/spec/support/shared_contexts/temp_dir.rb.tt +0 -9
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pathname"
|
4
|
+
require "refinements/structs"
|
5
|
+
|
6
|
+
module Gemsmith
|
7
|
+
module CLI
|
8
|
+
module Parsers
|
9
|
+
# Handles parsing of Command Line Interface (CLI) core options.
|
10
|
+
class Core
|
11
|
+
using Refinements::Structs
|
12
|
+
|
13
|
+
def self.call(...) = new(...).call
|
14
|
+
|
15
|
+
def initialize configuration = Container[:configuration],
|
16
|
+
client: Parser::CLIENT,
|
17
|
+
container: Container
|
18
|
+
@configuration = configuration
|
19
|
+
@client = client
|
20
|
+
@container = container
|
21
|
+
end
|
22
|
+
|
23
|
+
def call arguments = []
|
24
|
+
client.banner = specification.labeled_summary
|
25
|
+
client.separator "\nUSAGE:\n"
|
26
|
+
collate
|
27
|
+
client.parse arguments
|
28
|
+
configuration
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :configuration, :client, :container
|
34
|
+
|
35
|
+
def collate = private_methods.sort.grep(/add_/).each { |method| __send__ method }
|
36
|
+
|
37
|
+
def add_config
|
38
|
+
client.on "-c",
|
39
|
+
"--config ACTION",
|
40
|
+
%i[edit view],
|
41
|
+
"Manage gem configuration: edit or view." do |action|
|
42
|
+
configuration.merge! action_config: action
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_build
|
47
|
+
client.on "-b", "--build NAME [options]", "Build new project." do |name|
|
48
|
+
configuration.merge! action_build: true, project_name: name
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def add_edit
|
53
|
+
client.on "--edit GEM", "Edit installed gem in default editor." do |gem_name|
|
54
|
+
configuration.merge! action_edit: gem_name
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def add_install
|
59
|
+
client.on "-i", "--install [NAME]", "Install gem for local development." do |name|
|
60
|
+
configuration.merge! action_install: true,
|
61
|
+
project_name: name || Pathname.pwd.basename.to_s
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def add_publish
|
66
|
+
client.on "-p", "--publish [NAME]", "Publish gem to remote gem server." do |name|
|
67
|
+
configuration.merge! action_publish: true,
|
68
|
+
project_name: name || Pathname.pwd.basename.to_s
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def add_view
|
73
|
+
client.on "--view GEM", "View installed gem in default browser." do |gem_name|
|
74
|
+
configuration.merge! action_view: gem_name
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def add_version
|
79
|
+
client.on "-v", "--version", "Show gem version." do
|
80
|
+
configuration.merge! action_version: true
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def add_help
|
85
|
+
client.on "-h", "--help", "Show this message." do
|
86
|
+
configuration.merge! action_help: true
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def specification = container[__method__]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gemsmith
|
4
|
+
module CLI
|
5
|
+
# The main Command Line Interface (CLI) object.
|
6
|
+
class Shell
|
7
|
+
ACTIONS = {
|
8
|
+
config: Actions::Config.new,
|
9
|
+
build: Actions::Build.new,
|
10
|
+
install: Actions::Install.new,
|
11
|
+
publish: Actions::Publish.new,
|
12
|
+
edit: Actions::Edit.new,
|
13
|
+
view: Actions::View.new
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
def initialize parser: Parser.new, actions: ACTIONS, container: Container
|
17
|
+
@parser = parser
|
18
|
+
@actions = actions
|
19
|
+
@container = container
|
20
|
+
end
|
21
|
+
|
22
|
+
def call arguments = []
|
23
|
+
perform parser.call(arguments)
|
24
|
+
rescue OptionParser::ParseError => error
|
25
|
+
logger.error { error.message }
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :parser, :actions, :container
|
31
|
+
|
32
|
+
def perform configuration
|
33
|
+
case configuration
|
34
|
+
in action_config: Symbol => action then config action
|
35
|
+
in action_build: true then build configuration
|
36
|
+
in action_install: true then install configuration
|
37
|
+
in action_publish: true then publish configuration
|
38
|
+
in action_edit: String => gem_name then edit gem_name
|
39
|
+
in action_view: String => gem_name then view gem_name
|
40
|
+
in action_version: true then logger.info { specification.labeled_version }
|
41
|
+
else usage
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def config(action) = actions.fetch(__method__).call(action)
|
46
|
+
|
47
|
+
def build(configuration) = actions.fetch(__method__).call(configuration)
|
48
|
+
|
49
|
+
def install(configuration) = actions.fetch(__method__).call(configuration)
|
50
|
+
|
51
|
+
def publish(configuration) = actions.fetch(__method__).call(configuration)
|
52
|
+
|
53
|
+
def edit(gem_name) = actions.fetch(__method__).call(gem_name)
|
54
|
+
|
55
|
+
def view(gem_name) = actions.fetch(__method__).call(gem_name)
|
56
|
+
|
57
|
+
def usage = logger.unknown { parser.to_s }
|
58
|
+
|
59
|
+
def logger = container[__method__]
|
60
|
+
|
61
|
+
def specification = container[__method__]
|
62
|
+
|
63
|
+
def process = container[__method__]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gemsmith
|
4
|
+
module Configuration
|
5
|
+
module Enhancers
|
6
|
+
# Adds gem specific roots to existing content.
|
7
|
+
class TemplateRoot
|
8
|
+
def initialize overrides = Pathname(__dir__).join("../../templates")
|
9
|
+
@overrides = Array overrides
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(content) = content.add_template_roots(overrides)
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
attr_reader :overrides
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pathname"
|
4
|
+
require "refinements/arrays"
|
5
|
+
require "refinements/hashes"
|
6
|
+
require "refinements/structs"
|
7
|
+
require "runcom"
|
8
|
+
require "yaml"
|
9
|
+
|
10
|
+
module Gemsmith
|
11
|
+
module Configuration
|
12
|
+
# Represents the fully assembled Command Line Interface (CLI) configuration.
|
13
|
+
class Loader
|
14
|
+
using Refinements::Arrays
|
15
|
+
using Refinements::Hashes
|
16
|
+
using Refinements::Structs
|
17
|
+
|
18
|
+
DEFAULTS = Rubysmith::Configuration::Loader::DEFAULTS
|
19
|
+
CLIENT = Runcom::Config.new "gemsmith/configuration.yml", defaults: DEFAULTS
|
20
|
+
|
21
|
+
ENHANCERS = Rubysmith::Configuration::Loader::ENHANCERS.including(Enhancers::TemplateRoot.new)
|
22
|
+
.freeze
|
23
|
+
|
24
|
+
def self.call(...) = new(...).call
|
25
|
+
|
26
|
+
def self.with_defaults = new(client: DEFAULTS, enhancers: [])
|
27
|
+
|
28
|
+
def self.with_overrides = new(client: DEFAULTS, enhancers: [Enhancers::TemplateRoot.new])
|
29
|
+
|
30
|
+
def initialize content: Rubysmith::Configuration::Content.new,
|
31
|
+
client: CLIENT,
|
32
|
+
enhancers: ENHANCERS
|
33
|
+
@content = content
|
34
|
+
@client = client
|
35
|
+
@enhancers = enhancers
|
36
|
+
end
|
37
|
+
|
38
|
+
def call
|
39
|
+
enhancers.reduce(preload_content) { |preload, enhancer| enhancer.call preload }
|
40
|
+
.freeze
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
attr_reader :content, :client, :enhancers
|
46
|
+
|
47
|
+
def preload_content = content.merge(**client.to_h.flatten_keys)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry-container"
|
4
|
+
require "open3"
|
5
|
+
require "spek"
|
6
|
+
|
7
|
+
module Gemsmith
|
8
|
+
# Provides a global gem container for injection into other objects.
|
9
|
+
module Container
|
10
|
+
extend Dry::Container::Mixin
|
11
|
+
|
12
|
+
config.registry = ->(container, key, value, _options) { container[key.to_s] = value }
|
13
|
+
|
14
|
+
merge Rubysmith::Container
|
15
|
+
|
16
|
+
register(:configuration) { Gemsmith::Configuration::Loader.call }
|
17
|
+
register(:specification) { Spek::Loader.call "#{__dir__}/../../gemsmith.gemspec" }
|
18
|
+
register(:environment) { ENV }
|
19
|
+
register(:executor) { Open3 }
|
20
|
+
register(:kernel) { Kernel }
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
Gem::Specification.new do |spec|
|
2
|
+
spec.name = "<%= configuration.project_name %>"
|
3
|
+
spec.version = "<%= configuration.project_version %>"
|
4
|
+
spec.authors = ["<%= configuration.author_name %>"]
|
5
|
+
spec.email = ["<%= configuration.author_email %>"]
|
6
|
+
spec.homepage = "<%= configuration.computed_project_url_home %>"
|
7
|
+
spec.summary = ""
|
8
|
+
spec.license = "<%= configuration.license_label_version %>"
|
9
|
+
|
10
|
+
spec.metadata = {
|
11
|
+
"bug_tracker_uri" => "<%= configuration.computed_project_url_issues %>",
|
12
|
+
"changelog_uri" => "<%= configuration.computed_project_url_versions %>",
|
13
|
+
"documentation_uri" => "<%= configuration.computed_project_url_home %>",
|
14
|
+
"label" => "<%= configuration.project_label %>",
|
15
|
+
"rubygems_mfa_required" => "true",
|
16
|
+
"source_code_uri" => "<%= configuration.computed_project_url_source %>"
|
17
|
+
}
|
18
|
+
|
19
|
+
<% if configuration.build_security %>
|
20
|
+
spec.signing_key = Gem.default_key_path
|
21
|
+
spec.cert_chain = [Gem.default_cert_path]
|
22
|
+
<% end %>
|
23
|
+
|
24
|
+
spec.required_ruby_version = "~> <%= RUBY_VERSION[/\d+\.\d+/] %>"
|
25
|
+
<% if configuration.build_cli %>
|
26
|
+
spec.add_dependency "dry-container", "~> 0.9"
|
27
|
+
<% end %>
|
28
|
+
<% if configuration.build_cli %>
|
29
|
+
spec.add_dependency "pastel", "~> 0.8"
|
30
|
+
<% end %>
|
31
|
+
<% if configuration.build_refinements %>
|
32
|
+
spec.add_dependency "refinements", "~> 9.2"
|
33
|
+
<% end %>
|
34
|
+
<% if configuration.build_cli %>
|
35
|
+
spec.add_dependency "runcom", "~> 8.2"
|
36
|
+
<% end %>
|
37
|
+
<% if configuration.build_cli %>
|
38
|
+
spec.add_dependency "spek", "~> 0.0"
|
39
|
+
<% end %>
|
40
|
+
<% if configuration.build_zeitwerk %>
|
41
|
+
spec.add_dependency "zeitwerk", "~> 2.5"
|
42
|
+
<% end %>
|
43
|
+
|
44
|
+
<% if configuration.build_cli %>
|
45
|
+
spec.bindir = "exe"
|
46
|
+
spec.executables << "<%= configuration.project_name %>"
|
47
|
+
<% end %>
|
48
|
+
spec.extra_rdoc_files = Dir["README*", "LICENSE*"]
|
49
|
+
spec.files = Dir["*.gemspec", "lib/**/*"]
|
50
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
<% namespace do %>
|
4
|
+
module CLI
|
5
|
+
module Actions
|
6
|
+
# Handles the config action.
|
7
|
+
class Config
|
8
|
+
def initialize client: Configuration::Loader::CLIENT, container: Container
|
9
|
+
@client = client
|
10
|
+
@container = container
|
11
|
+
end
|
12
|
+
|
13
|
+
def call selection
|
14
|
+
case selection
|
15
|
+
when :edit then edit
|
16
|
+
when :view then view
|
17
|
+
else logger.error { "Invalid configuration selection: #{selection}." }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :client, :container
|
24
|
+
|
25
|
+
def edit = kernel.system("$EDITOR #{client.current}")
|
26
|
+
|
27
|
+
def view = kernel.system("cat #{client.current}")
|
28
|
+
|
29
|
+
def kernel = container[__method__]
|
30
|
+
|
31
|
+
def logger = container[__method__]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
<% end %>
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "optparse"
|
2
|
+
|
3
|
+
<% namespace do %>
|
4
|
+
module CLI
|
5
|
+
# Assembles and parses all Command Line Interface (CLI) options.
|
6
|
+
class Parser
|
7
|
+
CLIENT = OptionParser.new nil, 40, " "
|
8
|
+
|
9
|
+
# Order is important.
|
10
|
+
SECTIONS = [Parsers::Core].freeze
|
11
|
+
|
12
|
+
def initialize sections: SECTIONS, client: CLIENT, container: Container
|
13
|
+
@sections = sections
|
14
|
+
@client = client
|
15
|
+
@configuration = container[:configuration].dup
|
16
|
+
end
|
17
|
+
|
18
|
+
def call arguments = []
|
19
|
+
sections.each { |section| section.call configuration, client: }
|
20
|
+
client.parse arguments
|
21
|
+
configuration.freeze
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s = client.to_s
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_reader :sections, :client, :configuration
|
29
|
+
end
|
30
|
+
end
|
31
|
+
<% end %>
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "refinements/structs"
|
2
|
+
|
3
|
+
<% namespace do %>
|
4
|
+
module CLI
|
5
|
+
module Parsers
|
6
|
+
# Handles parsing of Command Line Interface (CLI) core options.
|
7
|
+
class Core
|
8
|
+
using Refinements::Structs
|
9
|
+
|
10
|
+
def self.call(...) = new(...).call
|
11
|
+
|
12
|
+
def initialize configuration = Container[:configuration],
|
13
|
+
client: Parser::CLIENT,
|
14
|
+
container: Container
|
15
|
+
@configuration = configuration
|
16
|
+
@client = client
|
17
|
+
@container = container
|
18
|
+
end
|
19
|
+
|
20
|
+
def call arguments = []
|
21
|
+
client.banner = specification.labeled_summary
|
22
|
+
client.separator "\nUSAGE:\n"
|
23
|
+
collate
|
24
|
+
client.parse arguments
|
25
|
+
configuration
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :configuration, :client, :container
|
31
|
+
|
32
|
+
def collate = private_methods.sort.grep(/add_/).each { |method| __send__ method }
|
33
|
+
|
34
|
+
def add_config
|
35
|
+
client.on "-c",
|
36
|
+
"--config ACTION",
|
37
|
+
%i[edit view],
|
38
|
+
"Manage gem configuration: edit or view." do |action|
|
39
|
+
configuration.merge! action_config: action
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_version
|
44
|
+
client.on "-v", "--version", "Show gem version." do
|
45
|
+
configuration.merge! action_version: true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_help
|
50
|
+
client.on "-h", "--help", "Show this message." do
|
51
|
+
configuration.merge! action_help: true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def specification = container[__method__]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
<% end %>
|
@@ -0,0 +1,40 @@
|
|
1
|
+
<% namespace do %>
|
2
|
+
module CLI
|
3
|
+
# The main Command Line Interface (CLI) object.
|
4
|
+
class Shell
|
5
|
+
ACTIONS = {config: Actions::Config.new}.freeze
|
6
|
+
|
7
|
+
def initialize parser: Parser.new, actions: ACTIONS, container: Container
|
8
|
+
@parser = parser
|
9
|
+
@actions = actions
|
10
|
+
@container = container
|
11
|
+
end
|
12
|
+
|
13
|
+
def call arguments = []
|
14
|
+
perform parser.call(arguments)
|
15
|
+
rescue OptionParser::ParseError => error
|
16
|
+
logger.error { error.message }
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
attr_reader :parser, :actions, :container
|
22
|
+
|
23
|
+
def perform configuration
|
24
|
+
case configuration
|
25
|
+
in action_config: Symbol => action then config action
|
26
|
+
in action_version: true then logger.info { specification.labeled_version }
|
27
|
+
else usage
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def config(action) = actions.fetch(__method__).call(action)
|
32
|
+
|
33
|
+
def usage = logger.unknown { parser.to_s }
|
34
|
+
|
35
|
+
def specification = container[__method__]
|
36
|
+
|
37
|
+
def logger = container[__method__]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
<% end %>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
<% namespace do %>
|
4
|
+
module Configuration
|
5
|
+
# Defines the content of the configuration for use throughout the gem.
|
6
|
+
Content = Struct.new(
|
7
|
+
:action_config,
|
8
|
+
:action_help,
|
9
|
+
:action_version,
|
10
|
+
keyword_init: true
|
11
|
+
) do
|
12
|
+
def initialize *arguments
|
13
|
+
super
|
14
|
+
freeze
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
<% end %>
|
File without changes
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "pathname"
|
2
|
+
require "refinements/hashes"
|
3
|
+
require "refinements/structs"
|
4
|
+
require "runcom"
|
5
|
+
require "yaml"
|
6
|
+
|
7
|
+
<% namespace do %>
|
8
|
+
module Configuration
|
9
|
+
# Represents the fully assembled Command Line Interface (CLI) configuration.
|
10
|
+
class Loader
|
11
|
+
using Refinements::Hashes
|
12
|
+
using Refinements::Structs
|
13
|
+
|
14
|
+
DEFAULTS = (YAML.load_file(Pathname(__dir__).join("defaults.yml")) || {}).freeze
|
15
|
+
CLIENT = Runcom::Config.new "<%= configuration.project_name %>/configuration.yml", defaults: DEFAULTS
|
16
|
+
|
17
|
+
def self.call = new.call
|
18
|
+
|
19
|
+
def self.with_defaults = new client: DEFAULTS
|
20
|
+
|
21
|
+
def initialize content: Content.new, client: CLIENT
|
22
|
+
@content = content
|
23
|
+
@client = client
|
24
|
+
end
|
25
|
+
|
26
|
+
def call = content.merge(**client.to_h.flatten_keys).freeze
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :content, :client
|
31
|
+
end
|
32
|
+
end
|
33
|
+
<% end %>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "dry-container"
|
2
|
+
require "logger"
|
3
|
+
require "pastel"
|
4
|
+
require "spek"
|
5
|
+
|
6
|
+
<% namespace do %>
|
7
|
+
# Provides a global gem container for injection into other objects.
|
8
|
+
module Container
|
9
|
+
extend Dry::Container::Mixin
|
10
|
+
|
11
|
+
register(:configuration) { Configuration::Loader.call }
|
12
|
+
register(:specification) { Spek::Loader.call "#{__dir__}/<%= Array.new(2 + configuration.project_levels, "../").join %><%= configuration.project_name %>.gemspec" }
|
13
|
+
register(:colorizer) { Pastel.new enabled: $stdout.tty? }
|
14
|
+
register(:kernel) { Kernel }
|
15
|
+
|
16
|
+
register :log_colors do
|
17
|
+
{
|
18
|
+
"DEBUG" => self[:colorizer].white.detach,
|
19
|
+
"INFO" => self[:colorizer].green.detach,
|
20
|
+
"WARN" => self[:colorizer].yellow.detach,
|
21
|
+
"ERROR" => self[:colorizer].red.detach,
|
22
|
+
"FATAL" => self[:colorizer].white.bold.on_red.detach,
|
23
|
+
"ANY" => self[:colorizer].white.bold.detach
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
register :logger do
|
28
|
+
Logger.new $stdout,
|
29
|
+
level: Logger.const_get(ENV.fetch("LOG_LEVEL", "INFO")),
|
30
|
+
formatter: (
|
31
|
+
lambda do |severity, _at, _name, message|
|
32
|
+
self[:log_colors][severity].call "#{message}\n"
|
33
|
+
end
|
34
|
+
)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
<% end %>
|
data/lib/gemsmith/templates/%project_name%/spec/lib/%project_path%/cli/actions/config_spec.rb.erb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe <%= configuration.project_namespaced_class %>::CLI::Actions::Config do
|
4
|
+
subject(:action) { described_class.new }
|
5
|
+
|
6
|
+
include_context "with application container"
|
7
|
+
|
8
|
+
describe "#call" do
|
9
|
+
it "edits configuration" do
|
10
|
+
action.call :edit
|
11
|
+
expect(kernel).to have_received(:system).with(include("EDITOR"))
|
12
|
+
end
|
13
|
+
|
14
|
+
it "views configuration" do
|
15
|
+
action.call :view
|
16
|
+
expect(kernel).to have_received(:system).with(include("cat"))
|
17
|
+
end
|
18
|
+
|
19
|
+
it "logs invalid configuration" do
|
20
|
+
expectation = proc { action.call :bogus }
|
21
|
+
expect(&expectation).to output(/Invalid configuration selection: bogus./).to_stdout
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe <%= configuration.project_namespaced_class %>::CLI::Parser do
|
4
|
+
subject(:parser) { described_class.new }
|
5
|
+
|
6
|
+
include_context "with application container"
|
7
|
+
|
8
|
+
describe "#call" do
|
9
|
+
it "answers hash with valid option" do
|
10
|
+
expect(parser.call(%w[--help])).to have_attributes(action_help: true)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "fails with invalid option" do
|
14
|
+
expectation = proc { parser.call %w[--bogus] }
|
15
|
+
expect(&expectation).to raise_error(OptionParser::InvalidOption, /--bogus/)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#to_s" do
|
20
|
+
it "answers usage" do
|
21
|
+
parser.call
|
22
|
+
expect(parser.to_s).to match(/.+USAGE.+/m)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|