From 65964140ba9e657f1f63bcdc7f63fac84f8b55f9 Mon Sep 17 00:00:00 2001 From: Aaron Allen Date: Fri, 25 Jul 2025 01:54:24 -0500 Subject: [PATCH] Refactor `example` DSL for better consistency and clarity Simplified the `example` DSL syntax in commands by replacing array-based declarations with clearer method calls. Updated tests and fixtures to align with the new structure, ensuring improved readability and maintainability. --- lib/dry/cli/banner.rb | 108 ++++++++++----- lib/dry/cli/command.rb | 16 +-- lib/dry/cli/inline.rb | 2 +- spec/integration/inline_spec.rb | 10 +- spec/integration/single_command_spec.rb | 14 +- spec/integration/subcommands_spec.rb | 11 +- spec/support/fixtures/based | 14 +- spec/support/fixtures/foo | 106 ++++++--------- spec/support/fixtures/shared_commands.rb | 123 +++++++----------- spec/support/shared_examples/commands.rb | 17 ++- .../shared_examples/inherited_commands.rb | 44 +++---- spec/support/shared_examples/rendering.rb | 8 +- spec/support/shared_examples/subcommands.rb | 20 ++- spec/unit/dry/cli/cli_spec.rb | 14 +- 14 files changed, 235 insertions(+), 272 deletions(-) diff --git a/lib/dry/cli/banner.rb b/lib/dry/cli/banner.rb index bb1a474..78b451d 100644 --- a/lib/dry/cli/banner.rb +++ b/lib/dry/cli/banner.rb @@ -8,7 +8,7 @@ class CLI # # @since 0.1.0 # @api private - module Banner + module Banner # rubocop:disable Metrics/ModuleLength # Prints command banner # # @param command [Dry::CLI::Command] the command @@ -17,17 +17,56 @@ module Banner # @since 0.1.0 # @api private def self.call(command, name) + extended_arguments = extended_command_arguments(command) + extended_examples = extended_command_examples(command, name) + extended_options = extended_command_options(command) + indent = capture_indent(extended_arguments, extended_options, extended_examples) + [ command_name(name), command_name_and_arguments(command, name), command_description(command), command_subcommands(command), - command_arguments(command), - command_options(command), - command_examples(command, name) + command_arguments(extended_arguments, indent), + command_options(extended_options, indent), + command_examples(extended_examples, indent) ].compact.join("\n") end + # @since unreleased + # @api private + def self.capture_indent(extended_arguments, extended_options, extended_examples) + strings = extended_arguments + extended_options + extended_examples + strings.map { |string, _| string.length }.max + 1 + end + + # @since unreleased + # @api private + def self.build_option_right(option) + description = option.desc + unless option.default.nil? + description = "#{description}, default: #{option.default.inspect}" + end + description + end + + # @since unreleased + # @api private + def self.build_option_left(option) + name = Inflector.dasherize(option.name) + name = if option.boolean? + "--[no-]#{name}" + elsif option.flag? + "--#{name}" + elsif option.array? + "--#{name}=VALUE1,VALUE2,.." + else + "--#{name}=VALUE" + end + name = "#{name}, #{option.alias_names.join(", ")}" if option.aliases.any? + name + end + # @since 0.1.0 # @api private def self.command_name(name) @@ -46,10 +85,13 @@ def self.command_name_and_arguments(command, name) # @since 0.1.0 # @api private - def self.command_examples(command, name) - return if command.examples.empty? + def self.command_examples(extended_examples, indent) + return if extended_examples.empty? - "\nExamples:\n#{command.examples.map { |example| " #{name} #{example}" }.join("\n")}" + examples = extended_examples.map { |example, description| + " #{example.ljust(indent)} # #{description}" + } + "\nExamples:\n#{examples.join("\n")}" end # @since 0.1.0 @@ -68,16 +110,22 @@ def self.command_subcommands(command) # @since 0.1.0 # @api private - def self.command_arguments(command) - return if command.arguments.empty? + def self.command_arguments(extended_arguments, indent) + return if extended_arguments.empty? - "\nArguments:\n#{extended_command_arguments(command)}" + arguments = extended_arguments.map { |argument, description| + " #{argument.ljust(indent)} # #{description}" + } + "\nArguments:\n#{arguments.join("\n")}" end # @since 0.1.0 # @api private - def self.command_options(command) - "\nOptions:\n#{extended_command_options(command)}" + def self.command_options(extended_options, indent) + options = extended_options.map { |option, description| + " #{option.ljust(indent)} # #{description}" + } + "\nOptions:\n#{options.join("\n")}" end # @since 0.1.0 @@ -97,34 +145,26 @@ def self.arguments(command) # @api private def self.extended_command_arguments(command) command.arguments.map do |argument| - " #{argument.name.to_s.upcase.ljust(32)} # #{"REQUIRED " if argument.required?}#{argument.desc}" # rubocop:disable Layout/LineLength - end.join("\n") + [argument.name.to_s.upcase, "#{"REQUIRED " if argument.required?}#{argument.desc}"] + end end # @since 0.1.0 # @api private - # - def self.extended_command_options(command) - result = command.options.map do |option| - name = Inflector.dasherize(option.name) - name = if option.boolean? - "[no-]#{name}" - elsif option.flag? - name - elsif option.array? - "#{name}=VALUE1,VALUE2,.." - else - "#{name}=VALUE" - end - name = "#{name}, #{option.alias_names.join(", ")}" if option.aliases.any? - name = " --#{name.ljust(30)}" - name = "#{name} # #{option.desc}" - name = "#{name}, default: #{option.default.inspect}" unless option.default.nil? - name + def self.extended_command_examples(command, name) + command.examples.map do |example, description| + ["#{name} #{example}", description] end + end - result << " --#{"help, -h".ljust(30)} # Print this help" - result.join("\n") + # @since 0.1.0 + # @api private + # + def self.extended_command_options(command) + result = command.options.map { |option| + [build_option_left(option), build_option_right(option)] + } + result << ["--help, -h", "Print this help"] end def self.build_subcommands_list(subcommands) diff --git a/lib/dry/cli/command.rb b/lib/dry/cli/command.rb index 3e6a601..1d7dc2c 100644 --- a/lib/dry/cli/command.rb +++ b/lib/dry/cli/command.rb @@ -78,13 +78,11 @@ def self.desc(description) # require "dry/cli" # # class Server < Dry::CLI::Command - # example [ - # " # Basic usage (it uses the bundled server engine)", - # "--server=webrick # Force `webrick` server engine", - # "--host=0.0.0.0 # Bind to a host", - # "--port=2306 # Bind to a port", - # "--no-code-reloading # Disable code reloading" - # ] + # example "", "Basic usage (it uses the bundled server engine)", + # example "--server=webrick, "Force `webrick` server engine", + # example "--host=0.0.0.0, "Bind to a host", + # example "--port=2306", "Bind to a port", + # example "--no-code-reloading", "Disable code reloading" # # def call(*) # # ... @@ -100,8 +98,8 @@ def self.desc(description) # # foo server --host=0.0.0.0 # Bind to a host # # foo server --port=2306 # Bind to a port # # foo server --no-code-reloading # Disable code reloading - def self.example(*examples) - @examples += examples.flatten(1) + def self.example(example, description = "") + @examples.push([example, description]) end # Specify an argument diff --git a/lib/dry/cli/inline.rb b/lib/dry/cli/inline.rb index 1dde7fa..87c1f79 100644 --- a/lib/dry/cli/inline.rb +++ b/lib/dry/cli/inline.rb @@ -13,7 +13,7 @@ class CLI # methods below # # DSL consists of 5 methods: - # `desc`, `example`, `argument`, `option`  + # `desc`, `example`, `argument`, `option` # — are similar to methods from Command class # # `run` accepts a block to execute diff --git a/spec/integration/inline_spec.rb b/spec/integration/inline_spec.rb index b00d95f..d23fc91 100644 --- a/spec/integration/inline_spec.rb +++ b/spec/integration/inline_spec.rb @@ -19,14 +19,14 @@ Baz command line interface Arguments: - MANDATORY_ARG # REQUIRED Mandatory argument - OPTIONAL_ARG # Optional argument (has to have default value in call method) + MANDATORY_ARG # REQUIRED Mandatory argument + OPTIONAL_ARG # Optional argument (has to have default value in call method) Options: - --option-one=VALUE, -1 VALUE # Option one - --[no-]boolean-option, -b # Option boolean + --option-one=VALUE, -1 VALUE # Option one + --[no-]boolean-option, -b # Option boolean --option-with-default=VALUE, -d VALUE # Option default, default: "test" - --help, -h # Print this help + --help, -h # Print this help OUTPUT expect(output).to eq(expected_output) end diff --git a/spec/integration/single_command_spec.rb b/spec/integration/single_command_spec.rb index d0a5ba7..0028dae 100644 --- a/spec/integration/single_command_spec.rb +++ b/spec/integration/single_command_spec.rb @@ -17,23 +17,23 @@ output = `baz -h` expected_output = <<~OUTPUT Command: - #{cmd} + baz Usage: - #{cmd} MANDATORY_ARG [OPTIONAL_ARG] + baz MANDATORY_ARG [OPTIONAL_ARG] Description: Baz command line interface Arguments: - MANDATORY_ARG # REQUIRED Mandatory argument - OPTIONAL_ARG # Optional argument (has to have default value in call method) + MANDATORY_ARG # REQUIRED Mandatory argument + OPTIONAL_ARG # Optional argument (has to have default value in call method) Options: - --option-one=VALUE, -1 VALUE # Option one - --[no-]boolean-option, -b # Option boolean + --option-one=VALUE, -1 VALUE # Option one + --[no-]boolean-option, -b # Option boolean --option-with-default=VALUE, -d VALUE # Option default, default: "test" - --help, -h # Print this help + --help, -h # Print this help OUTPUT expect(output).to eq(expected_output) end diff --git a/spec/integration/subcommands_spec.rb b/spec/integration/subcommands_spec.rb index aaa3d30..f661f43 100644 --- a/spec/integration/subcommands_spec.rb +++ b/spec/integration/subcommands_spec.rb @@ -18,17 +18,16 @@ Generate a model Arguments: - MODEL # REQUIRED Model name (eg. `user`) + MODEL # REQUIRED Model name (eg. `user`) Options: - --[no-]skip-migration # Skip migration, default: false - --help, -h # Print this help + --[no-]skip-migration # Skip migration, default: false + --help, -h # Print this help Examples: - foo generate model user # Generate `User` entity, `UserRepository` repository, and the migration - foo generate model user --skip-migration # Generate `User` entity and `UserRepository` repository + foo generate model user # Generate `User` entity, `UserRepository` repository, and the migration + foo generate model user --skip-migration # Generate `User` entity and `UserRepository` repository DESC - expect(output).to eq(expected) end end diff --git a/spec/support/fixtures/based b/spec/support/fixtures/based index 03b4cce..dfb69a2 100755 --- a/spec/support/fixtures/based +++ b/spec/support/fixtures/based @@ -38,11 +38,9 @@ module Based option :num, desc: "number of lines to display" option :tail, desc: "continually stream log", type: :boolean - example [ - "APP_NAME", - "APP_NAME --num=50", - "APP_NAME --tail" - ] + example "APP_NAME" + example "APP_NAME --num=50" + example "APP_NAME --tail" end class Addons < Base @@ -50,10 +48,8 @@ module Based option :json, desc: "return add-ons in json format", type: :boolean, default: false - example [ - "APP_NAME", - "APP_NAME --json" - ] + example "APP_NAME" + example "APP_NAME --json" end register "run", Run diff --git a/spec/support/fixtures/foo b/spec/support/fixtures/foo index 3debab6..94038a1 100755 --- a/spec/support/fixtures/foo +++ b/spec/support/fixtures/foo @@ -17,9 +17,7 @@ module Foo class Precompile < Dry::CLI::Command desc "Precompile assets for deployment" - example [ - "FOO_ENV=production # Precompile assets for production environment" - ] + example "FOO_ENV=production", "Precompile assets for production environment" def call(*); end end @@ -29,10 +27,8 @@ module Foo desc "Starts Foo console" option :engine, desc: "Force a console engine", values: %w[irb pry ripl] - example [ - " # Uses the bundled engine", - "--engine=pry # Force to use Pry" - ] + example "", "Uses the bundled engine" + example "--engine=pry", "Force to use Pry" def call(engine: nil, **) puts "console - engine: #{engine}" @@ -68,10 +64,8 @@ module Foo desc "Migrate the database" argument :version, desc: "The target version of the migration (see `foo db version`)" - example [ - " # Migrate to the last version", - "20170721120747 # Migrate to a specific version" - ] + example "", "Migrate to the last version" + example "20170721120747", "Migrate to a specific version" def call(*); end end @@ -103,10 +97,8 @@ module Foo class Action < Dry::CLI::Command desc "Destroy an action from app" - example [ - "web home#index # Basic usage", - "admin users#index # Destroy from `admin` app" - ] + example "web home#index", "Basic usage" + example "admin users#index", "Destroy from `admin` app" argument :app, required: true, desc: "The application name (eg. `web`)" argument :action, required: true, desc: "The action name (eg. `home#index`)" @@ -121,9 +113,7 @@ module Foo argument :app, required: true, desc: "The application name (eg. `web`)" - example [ - "admin # Destroy `admin` app" - ] + example "admin", "Destroy `admin` app" def call(*); end end @@ -133,9 +123,7 @@ module Foo argument :mailer, required: true, desc: "The mailer name (eg. `welcome`)" - example [ - "welcome # Destroy `WelcomeMailer` mailer" - ] + example "welcome", "Destroy `WelcomeMailer` mailer" def call(*); end end @@ -145,9 +133,7 @@ module Foo argument :migration, required: true, desc: "The migration name (eg. `create_users`)" - example [ - "create_users # Destroy `db/migrations/20170721120747_create_users.rb`" - ] + example "create_users", "Destroy `db/migrations/20170721120747_create_users.rb`" def call(*); end end @@ -157,9 +143,7 @@ module Foo argument :model, required: true, desc: "The model name (eg. `user`)" - example [ - "user # Destroy `User` entity and `UserRepository` repository" - ] + example "user", "Destroy `User` entity and `UserRepository` repository" def call(*); end end @@ -169,13 +153,11 @@ module Foo class Action < Dry::CLI::Command desc "Generate an action for app" - example [ - "web home#index # Basic usage", - "admin home#index # Generate for `admin` app", - "web home#index --url=/ # Specify URL", - "web sessions#destroy --method=GET # Specify HTTP method", - "web books#create --skip-view # Skip view and template" - ] + example "web home#index", "Basic usage" + example "admin home#index", "Generate for `admin` app" + example "web home#index --url=/", "Specify URL" + example "web sessions#destroy --method=GET", "Specify HTTP method" + example "web books#create --skip-view", "Skip view and template" argument :app, required: true, desc: "The application name (eg. `web`)" argument :action, required: true, desc: "The action name (eg. `home#index`)" @@ -195,10 +177,8 @@ module Foo argument :app, required: true, desc: "The application name (eg. `web`)" option :application_base_url, desc: "The app base URL (eg. `/api/v1`)" - example [ - "admin # Generate `admin` app", - "api --application-base-url=/api/v1 # Generate `api` app and mount at `/api/v1`" - ] + example "admin", "Generate `admin` app" + example "api --application-base-url=/api/v1", "Generate `api` app and mount at `/api/v1`" def call(*) loop { sleep(0.1) } @@ -214,12 +194,10 @@ module Foo option :to, desc: "The default `to` field of the mail" option :subject, desc: "The mail subject" - example [ - "welcome # Basic usage", - 'welcome --from="noreply@example.com" # Generate with default `from` value', - 'announcement --to="users@example.com" # Generate with default `to` value', - 'forgot_password --subject="Your password reset" # Generate with default `subject`' - ] + example "welcome", "Basic usage" + example 'welcome --from="noreply@example.com"', "Generate with default `from` value" + example 'announcement --to="users@example.com"', "Generate with default `to` value" + example 'forgot_password --subject="Your password reset"', "Generate with default `subject`" def call(*); end end @@ -229,9 +207,7 @@ module Foo argument :migration, required: true, desc: "The migration name (eg. `create_users`)" - example [ - "create_users # Generate `db/migrations/20170721120747_create_users.rb`" - ] + example "create_users", "Generate `db/migrations/20170721120747_create_users.rb`" def call(*); end end @@ -242,10 +218,8 @@ module Foo argument :model, required: true, desc: "Model name (eg. `user`)" option :skip_migration, type: :boolean, default: false, desc: "Skip migration" - example [ - "user # Generate `User` entity, `UserRepository` repository, and the migration", - "user --skip-migration # Generate `User` entity and `UserRepository` repository" - ] + example "user", "Generate `User` entity, `UserRepository` repository, and the migration" + example "user --skip-migration", "Generate `User` entity and `UserRepository` repository" def call(model:, **) puts "generate model - model: #{model}" @@ -257,10 +231,8 @@ module Foo argument :app, desc: "The application name (eg. `web`)" - example [ - " # Prints secret (eg. `6fad60e21f3f6bfcaf8e56cdb0f835d644b4892c3badc58328126812429bf073`)", - "web # Prints session secret (eg. `WEB_SESSIONS_SECRET=6fad60e21f3f6bfcaf8e56cdb0f835d644b4892c3badc58328126812429bf073`)" - ] + example "", "Prints secret (eg. `6fad60e21f3f6bfcaf8e56cdb0f835d644b4892c3badc58328126812429bf073`)" + example "web", "Prints session secret (eg. `WEB_SESSIONS_SECRET=6fad60e21f3f6bfcaf8e56cdb0f835d644b4892c3badc58328126812429bf073`)" def call(app: nil, **) puts "generate secret - app: #{app}" @@ -279,13 +251,11 @@ module Foo option :test, desc: "Project testing framework (minitest/rspec)", default: "minitest" option :foo_head, desc: "Use Foo HEAD (true/false)", type: :boolean, default: false - example [ - "bookshelf # Basic usage", - "bookshelf --test=rspec # Setup RSpec testing framework", - "bookshelf --database=postgres # Setup Postgres database", - "bookshelf --template=slim # Setup Slim template engine", - "bookshelf --foo-head # Use Foo HEAD" - ] + example "bookshelf", "Basic usage" + example "bookshelf --test=rspec", "Setup RSpec testing framework" + example "bookshelf --database=postgres", "Setup Postgres database" + example "bookshelf --template=slim", "Setup Slim template engine" + example "bookshelf --foo-head", "Use Foo HEAD" def call(project:, **) puts "new - project: #{project}" @@ -310,13 +280,11 @@ module Foo option :pid, desc: "Path to write a pid file after daemonize" option :code_reloading, desc: "Code reloading", type: :boolean, default: true - example [ - " # Basic usage (it uses the bundled server engine)", - "--server=webrick # Force `webrick` server engine", - "--host=0.0.0.0 # Bind to a host", - "--port=2306 # Bind to a port", - "--no-code-reloading # Disable code reloading" - ] + example "", "Basic usage (it uses the bundled server engine)" + example "--server=webrick", "Force `webrick` server engine" + example "--host=0.0.0.0", "Bind to a host" + example "--port=2306", "Bind to a port" + example "--no-code-reloading", "Disable code reloading" def call(options) puts "server - #{options.inspect}" diff --git a/spec/support/fixtures/shared_commands.rb b/spec/support/fixtures/shared_commands.rb index f694cd9..049814b 100644 --- a/spec/support/fixtures/shared_commands.rb +++ b/spec/support/fixtures/shared_commands.rb @@ -8,9 +8,7 @@ module Assets class Precompile < Dry::CLI::Command desc "Precompile assets for deployment" - example [ - "FOO_ENV=production # Precompile assets for production environment" - ] + example "FOO_ENV=production", "Precompile assets for production environment" def call(*); end end @@ -20,10 +18,8 @@ class Console < Dry::CLI::Command desc "Starts Foo console" option :engine, desc: "Force a console engine", values: %w[irb pry ripl] - example [ - " # Uses the bundled engine", - "--engine=pry # Force to use Pry" - ] + example "", "Uses the bundled engine" + example "--engine=pry", "Force to use Pry" def call(engine: nil, **) puts "console - engine: #{engine}" @@ -59,10 +55,8 @@ class Migrate < Dry::CLI::Command desc "Migrate the database" argument :version, desc: "The target version of the migration (see `foo db version`)" - example [ - " # Migrate to the last version", - "20170721120747 # Migrate to a specific version" - ] + example "", "Migrate to the last version" + example "20170721120747", "Migrate to a specific version" def call(*); end end @@ -94,10 +88,8 @@ module Destroy class Action < Dry::CLI::Command desc "Destroy an action from app" - example [ - "web home#index # Basic usage", - "admin users#index # Destroy from `admin` app" - ] + example "web home#index", "Basic usage" + example "admin users#index", "Destroy from `admin` app" argument :app, required: true, desc: "The application name (eg. `web`)" argument :action, required: true, desc: "The action name (eg. `home#index`)" @@ -112,9 +104,7 @@ class App < Dry::CLI::Command argument :app, required: true, desc: "The application name (eg. `web`)" - example [ - "admin # Destroy `admin` app" - ] + example "admin", "Destroy `admin` app" def call(*); end end @@ -124,9 +114,7 @@ class Mailer < Dry::CLI::Command argument :mailer, required: true, desc: "The mailer name (eg. `welcome`)" - example [ - "welcome # Destroy `WelcomeMailer` mailer" - ] + example "welcome", "Destroy `WelcomeMailer` mailer" def call(*); end end @@ -136,9 +124,7 @@ class Migration < Dry::CLI::Command argument :migration, required: true, desc: "The migration name (eg. `create_users`)" - example [ - "create_users # Destroy `db/migrations/20170721120747_create_users.rb`" - ] + example "create_users", "Destroy `db/migrations/20170721120747_create_users.rb`" def call(*); end end @@ -148,9 +134,7 @@ class Model < Dry::CLI::Command argument :model, required: true, desc: "The model name (eg. `user`)" - example [ - "user # Destroy `User` entity and `UserRepository` repository" - ] + example "user", "Destroy `User` entity and `UserRepository` repository" def call(*); end end @@ -160,13 +144,11 @@ module Generate class Action < Dry::CLI::Command desc "Generate an action for app" - example [ - "web home#index # Basic usage", - "admin home#index # Generate for `admin` app", - "web home#index --url=/ # Specify URL", - "web sessions#destroy --method=GET # Specify HTTP method", - "web books#create --skip-view # Skip view and template" - ] + example "web home#index", "Basic usage" + example "admin home#index", "Generate for `admin` app" + example "web home#index --url=/", "Specify URL" + example "web sessions#destroy --method=GET", "Specify HTTP method" + example "web books#create --skip-view", "Skip view and template" argument :app, required: true, desc: "The application name (eg. `web`)" argument :action, required: true, desc: "The action name (eg. `home#index`)" @@ -186,10 +168,8 @@ class App < Dry::CLI::Command argument :app, required: true, desc: "The application name (eg. `web`)" option :application_base_url, desc: "The app base URL (eg. `/api/v1`)" - example [ - "admin # Generate `admin` app", - "api --application-base-url=/api/v1 # Generate `api` app and mount at `/api/v1`" - ] + example "admin", "Generate `admin` app" + example "api --application-base-url=/api/v1", "Generate `api` app and mount at `/api/v1`" def call(*); end end @@ -203,12 +183,10 @@ class Mailer < Dry::CLI::Command option :to, desc: "The default `to` field of the mail" option :subject, desc: "The mail subject" - example [ - "welcome # Basic usage", - 'welcome --from="noreply@example.com" # Generate with default `from` value', - 'announcement --to="users@example.com" # Generate with default `to` value', - 'forgot_password --subject="Your password reset" # Generate with default `subject`' - ] + example "welcome", "Basic usage" + example 'welcome --from="noreply@example.com"', "Generate with default `from` value" + example 'announcement --to="users@example.com"', "Generate with default `to` value" + example 'forgot_password --subject="Your password reset"', "Generate with default `subject`" def call(*); end end @@ -218,9 +196,7 @@ class Migration < Dry::CLI::Command argument :migration, required: true, desc: "The migration name (eg. `create_users`)" - example [ - "create_users # Generate `db/migrations/20170721120747_create_users.rb`" - ] + example "create_users", "Generate `db/migrations/20170721120747_create_users.rb`" def call(*); end end @@ -231,10 +207,8 @@ class Model < Dry::CLI::Command argument :model, required: true, desc: "Model name (eg. `user`)" option :skip_migration, type: :boolean, default: false, desc: "Skip migration" - example [ - "user # Generate `User` entity, `UserRepository` repository, and the migration", - "user --skip-migration # Generate `User` entity and `UserRepository` repository" - ] + example "user", "Generate `User` entity, `UserRepository` repository, and the migration" + example "user --skip-migration", "Generate `User` entity and `UserRepository` repository" def call(model:, **) puts "generate model - model: #{model}" @@ -246,10 +220,8 @@ class Secret < Dry::CLI::Command argument :app, desc: "The application name (eg. `web`)" - example [ - " # Prints secret (eg. `6fad60e21f3f6bfcaf8e56cdb0f835d644b4892c3badc58328126812429bf073`)", - "web # Prints session secret (eg. `WEB_SESSIONS_SECRET=6fad60e21f3f6bfcaf8e56cdb0f835d644b4892c3badc58328126812429bf073`)" - ] + example "", "Prints secret (eg. `6fad60e21f3f6bfcaf8e56cdb0f835d644b4892c3badc58328126812429bf073`)" + example "web", "Prints session secret (eg. `WEB_SESSIONS_SECRET=6fad60e21f3f6bfcaf8e56cdb0f835d644b4892c3badc58328126812429bf073`)" def call(app: nil, **) puts "generate secret - app: #{app}" @@ -268,13 +240,11 @@ class New < Dry::CLI::Command option :test, desc: "Project testing framework (minitest/rspec)", default: "minitest" option :foo_head, desc: "Use Foo HEAD (true/false)", type: :boolean, default: false - example [ - "bookshelf # Basic usage", - "bookshelf --test=rspec # Setup RSpec testing framework", - "bookshelf --database=postgres # Setup Postgres database", - "bookshelf --template=slim # Setup Slim template engine", - "bookshelf --foo-head # Use Foo HEAD" - ] + example "bookshelf", "Basic usage" + example "bookshelf --test=rspec", "Setup RSpec testing framework" + example "bookshelf --database=postgres", "Setup Postgres database" + example "bookshelf --template=slim", "Setup Slim template engine" + example "bookshelf --foo-head", "Use Foo HEAD" def call(project:, **) puts "new - project: #{project}" @@ -301,13 +271,11 @@ class Server < Dry::CLI::Command option :quiet, desc: "Suppress output to stdout", type: :flag option :deps, desc: "List of extra dependencies", type: :array, default: %w[dep1 dep2] - example [ - " # Basic usage (it uses the bundled server engine)", - "--server=webrick # Force `webrick` server engine", - "--host=0.0.0.0 # Bind to a host", - "--port=2306 # Bind to a port", - "--no-code-reloading # Disable code reloading" - ] + example "", "Basic usage (it uses the bundled server engine)" + example "--server=webrick", "Force `webrick` server engine" + example "--host=0.0.0.0", "Bind to a host" + example "--port=2306", "Bind to a port" + example "--no-code-reloading", "Disable code reloading" def call(**options) puts "server - #{options.inspect}" @@ -547,7 +515,8 @@ class Base < Dry::CLI::Command desc "Base description" argument :app, desc: "Application name", type: :string, required: true option :verbosity, desc: "Verbosity level", type: :string, default: "INFO" - example "Base example" + + example "app_name", "Base example" def call(app:, **options) puts "Base - App: #{app} - Options: #{options.inspect}" @@ -572,11 +541,9 @@ class Logs < Base option :num, desc: "number of lines to display" option :tail, desc: "continually stream log", type: :boolean - example [ - "APP_NAME", - "APP_NAME --num=50", - "APP_NAME --tail" - ] + example "APP_NAME", "Basic usage" + example "APP_NAME --num=50", "Display 50 lines" + example "APP_NAME --tail", "Continually stream log" def call(app:, **options) puts "Logs - App: #{app} - Options: #{options.inspect}" @@ -588,9 +555,7 @@ class Addons < Base option :json, desc: "return add-ons in json format", type: :boolean, default: false - example [ - "APP_NAME", - "APP_NAME --json" - ] + example "APP_NAME", "Basic usage" + example "APP_NAME --json", "Return add-ons in JSON format" end end diff --git a/spec/support/shared_examples/commands.rb b/spec/support/shared_examples/commands.rb index 372c848..a70825c 100644 --- a/spec/support/shared_examples/commands.rb +++ b/spec/support/shared_examples/commands.rb @@ -205,13 +205,12 @@ --help, -h # Print this help Examples: - #{cmd} server # Basic usage (it uses the bundled server engine) - #{cmd} server --server=webrick # Force `webrick` server engine - #{cmd} server --host=0.0.0.0 # Bind to a host - #{cmd} server --port=2306 # Bind to a port - #{cmd} server --no-code-reloading # Disable code reloading + #{cmd} server # Basic usage (it uses the bundled server engine) + #{cmd} server --server=webrick # Force `webrick` server engine + #{cmd} server --host=0.0.0.0 # Bind to a host + #{cmd} server --port=2306 # Bind to a port + #{cmd} server --no-code-reloading # Disable code reloading DESC - expect(output).to eq(expected) end @@ -297,11 +296,11 @@ sub-command # Root command sub command Arguments: - ROOT_COMMAND_ARGUMENT # REQUIRED Root command argument + ROOT_COMMAND_ARGUMENT # REQUIRED Root command argument Options: - --root-command-option=VALUE # Root command option - --help, -h # Print this help + --root-command-option=VALUE # Root command option + --help, -h # Print this help DESC expect(output).to eq(expected) diff --git a/spec/support/shared_examples/inherited_commands.rb b/spec/support/shared_examples/inherited_commands.rb index b725ed7..460add4 100644 --- a/spec/support/shared_examples/inherited_commands.rb +++ b/spec/support/shared_examples/inherited_commands.rb @@ -31,12 +31,12 @@ Run a one-off process inside your app Arguments: - APP # REQUIRED Application name - CMD # REQUIRED Command to execute + APP # REQUIRED Application name + CMD # REQUIRED Command to execute Options: - --verbosity=VALUE # Verbosity level, default: "INFO" - --help, -h # Print this help + --verbosity=VALUE # Verbosity level, default: "INFO" + --help, -h # Print this help DESC expect(output).to eq(expected) end @@ -51,12 +51,12 @@ #{cmd} i subrun APP CMD Arguments: - APP # REQUIRED Application name - CMD # REQUIRED Command to execute + APP # REQUIRED Application name + CMD # REQUIRED Command to execute Options: - --verbosity=VALUE # Verbosity level, default: "INFO" - --help, -h # Print this help + --verbosity=VALUE # Verbosity level, default: "INFO" + --help, -h # Print this help DESC expect(output).to eq(expected) end @@ -74,16 +74,16 @@ Lists your add-ons and attachments Arguments: - APP # REQUIRED Application name + APP # REQUIRED Application name Options: - --verbosity=VALUE # Verbosity level, default: "INFO" - --[no-]json # return add-ons in json format, default: false - --help, -h # Print this help + --verbosity=VALUE # Verbosity level, default: "INFO" + --[no-]json # return add-ons in json format, default: false + --help, -h # Print this help Examples: - #{cmd} i addons APP_NAME - #{cmd} i addons APP_NAME --json + #{cmd} i addons APP_NAME # Basic usage + #{cmd} i addons APP_NAME --json # Return add-ons in JSON format DESC expect(output).to eq(expected) end @@ -101,18 +101,18 @@ Display recent log output Arguments: - APP # REQUIRED Application name + APP # REQUIRED Application name Options: - --verbosity=VALUE # Verbosity level, default: "INFO" - --num=VALUE # number of lines to display - --[no-]tail # continually stream log - --help, -h # Print this help + --verbosity=VALUE # Verbosity level, default: "INFO" + --num=VALUE # number of lines to display + --[no-]tail # continually stream log + --help, -h # Print this help Examples: - #{cmd} i logs APP_NAME - #{cmd} i logs APP_NAME --num=50 - #{cmd} i logs APP_NAME --tail + #{cmd} i logs APP_NAME # Basic usage + #{cmd} i logs APP_NAME --num=50 # Display 50 lines + #{cmd} i logs APP_NAME --tail # Continually stream log DESC expect(output).to eq(expected) end diff --git a/spec/support/shared_examples/rendering.rb b/spec/support/shared_examples/rendering.rb index 61669a8..3f7b7bb 100644 --- a/spec/support/shared_examples/rendering.rb +++ b/spec/support/shared_examples/rendering.rb @@ -170,10 +170,10 @@ Accepts options with aliases Options: - --url=VALUE, -u VALUE # The action URL - --[no-]flag, -f # The flag - --[no-]opt, -o # The opt, default: false - --help, -h # Print this help + --url=VALUE, -u VALUE # The action URL + --[no-]flag, -f # The flag + --[no-]opt, -o # The opt, default: false + --help, -h # Print this help DESC expect(output).to eq(expected) diff --git a/spec/support/shared_examples/subcommands.rb b/spec/support/shared_examples/subcommands.rb index fceabf3..0527eb0 100644 --- a/spec/support/shared_examples/subcommands.rb +++ b/spec/support/shared_examples/subcommands.rb @@ -83,17 +83,16 @@ Generate a model Arguments: - MODEL # REQUIRED Model name (eg. `user`) + MODEL # REQUIRED Model name (eg. `user`) Options: - --[no-]skip-migration # Skip migration, default: false - --help, -h # Print this help + --[no-]skip-migration # Skip migration, default: false + --help, -h # Print this help Examples: - #{cmd} generate model user # Generate `User` entity, `UserRepository` repository, and the migration - #{cmd} generate model user --skip-migration # Generate `User` entity and `UserRepository` repository + #{cmd} generate model user # Generate `User` entity, `UserRepository` repository, and the migration + #{cmd} generate model user --skip-migration # Generate `User` entity and `UserRepository` repository DESC - expect(output).to eq(expected) end @@ -174,22 +173,21 @@ output = capture_output { cli.call(arguments: %w[root-command sub-command -h]) } expected = <<~DESC Command: - rspec root-command sub-command + #{cmd} root-command sub-command Usage: - rspec root-command sub-command ROOT_COMMAND_SUB_COMMAND_ARGUMENT + #{cmd} root-command sub-command ROOT_COMMAND_SUB_COMMAND_ARGUMENT Description: Root command sub command Arguments: - ROOT_COMMAND_SUB_COMMAND_ARGUMENT # REQUIRED Root command sub command argument + ROOT_COMMAND_SUB_COMMAND_ARGUMENT # REQUIRED Root command sub command argument Options: --root-command-sub-command-option=VALUE # Root command sub command option - --help, -h # Print this help + --help, -h # Print this help DESC - expect(output).to eq(expected) output = capture_output { cli.call(arguments: %w[root-command sub-command --help]) } diff --git a/spec/unit/dry/cli/cli_spec.rb b/spec/unit/dry/cli/cli_spec.rb index d51d3fc..4c9f136 100644 --- a/spec/unit/dry/cli/cli_spec.rb +++ b/spec/unit/dry/cli/cli_spec.rb @@ -35,23 +35,23 @@ output = capture_output { cli.call(arguments: ["-h"]) } expected_output = <<~OUTPUT Command: - #{cmd} + rspec Usage: - #{cmd} MANDATORY_ARG [OPTIONAL_ARG] + rspec MANDATORY_ARG [OPTIONAL_ARG] Description: Baz command line interface Arguments: - MANDATORY_ARG # REQUIRED Mandatory argument - OPTIONAL_ARG # Optional argument (has to have default value in call method) + MANDATORY_ARG # REQUIRED Mandatory argument + OPTIONAL_ARG # Optional argument (has to have default value in call method) Options: - --option-one=VALUE, -1 VALUE # Option one - --[no-]boolean-option, -b # Option boolean + --option-one=VALUE, -1 VALUE # Option one + --[no-]boolean-option, -b # Option boolean --option-with-default=VALUE, -d VALUE # Option default, default: "test" - --help, -h # Print this help + --help, -h # Print this help OUTPUT expect(output).to eq(expected_output) end