Code Monkey home page Code Monkey logo

dry-cli's Introduction

dry-cli Gem Version CI Status

Links

Supported Ruby versions

This library officially supports the following Ruby versions:

  • MRI >= 2.4.0
  • jruby >= 9.4 (not tested on CI)

License

See LICENSE file.

dry-cli's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dry-cli's Issues

Subclasses of commands do not inherit options and params.

Describe the bug

When creating a subclass of a class, which inherits Dry::CLI::Command behaviour, options and arguments, that are declared in Base class are not inherited in children classes.

To Reproduce

class Base < Dry::CLI::Command
  option :debug

  def call(**args); puts args.inspect; end
end

class Command1 < Base
end

module Commands
  extend Dry::CLI::Registry

  register 'run', Command1
end

Dry::CLI(Commands).call
$ ./cli run --debug=test
Error: "command" was called with arguments "--debug=test"

Expected behavior

 $ ./cli command --debug=test 
{:debug=>"test"}

My environment

  • Affects my production application: NO
  • Ruby version: 2.7
  • OS: mac os

Negative numbers in arguments are not parsed correctly.

Dry CLI doesn't parse negative numbers as arguments and command execution fails.

Lets imagine we have file cli.rb with the following code:

require 'dry/cli'

module Commands
  extend Dry::CLI::Registry

  class Command < Dry::CLI::Command
    argument :amount
    option :rate

    def call(**args)
      puts args
    end
  end

  register 'command', Command
end


Dry::CLI(Commands).call

Then we call it in the following way:

ruby cli.rb command -0.01 --rate -0.01

Current behavior is:

ERROR: "cli.rb command" was called with arguments "-0.01 --rate -0.01"

Expected behavior is to have the following output:

{:rate=>"-0.01", :amount=>"-0.01"}

Environment;

  • Ruby version: 2.6.5
  • OS: Ubuntu 18.04

I think the problem is that Dry CLI uses Ruby OptionParser which handles -0.01 as option. But is there any possibility to fix this incorrect behavior?

Keyword arguments for a command aren't passed correctly to commands that use callbacks

Describe the bug

Keyword arguments for a command aren't passed correctly to commands that use callbacks.

To Reproduce

I have two gems: hanami-cli that depends on dry-cli, and hanami-rspec that depends on hanami-cli.

The first gem (hanami-cli) defines a command hanami generate slice NAME, which loosely correspond to this implementation:

module Hanami
  module CLI
    module Generate
      class Slice < Command
        argument :name, required: true, desc: "The slice name"

        def call(name: )
          puts "generating slice: #{name}"
        end
      end
    end
  end
end

The second gem (hanami-rspec) hooks into the invocation of hanami generate slice with an after callback.

module Hanami
  module RSpec
    module Generate
      class Slice < Command
        def call(options, **)
          puts "generating slice: #{options[:name]}"
        end
      end
    end
  end
end

Expected behavior

I would have expected that Hanami::RSpec::Generate::Slice#call would have accepted the :name keyword argument. What it does instead, it passes a Hash.

My environment

  • Affects my production application: NO
  • Ruby version: ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin19]
  • OS: Darwin out.lan 19.6.0 Darwin Kernel Version 19.6.0: Mon Aug 31 22:12:52 PDT 2020; root:xnu-6153.141.2~1/RELEASE_X86_64 x86_64

Is it possible to have multi-level subcommands?

I saw the example in the README, where a command is registered as two separate words: generate configuration โ€” see below:

#!/usr/bin/env ruby
require "bundler/setup"
require "hanami/cli"

module Foo
  module CLI
    module Commands
      extend Hanami::CLI::Registry

      module Generate
        class Configuration < Hanami::CLI::Command
          def call(*)
          end
        end

        class Project < Hanami::CLI::Command
          def call(*)
          end
        end
      end
    end
  end
end

Foo::CLI::Commands.register "generate configuration", Foo::CLI::Commands::Generate::Configuration
Foo::CLI::Commands.register "generate project", Foo::CLI::Commands::Generate::Project

Hanami::CLI.new(Foo::CLI::Commands).call

However, when run with --help, this produces:

Commands:
  hanami-test generate [SUBCOMMAND]

The word configuration becomes a "subcommand", and is only visible if I run cli generate --help

Commands:
  cli generate configuration
  cli generate project

I would love to be able to see all options in a global help message. For instance:

 cli generate configuration NAME  
 cli generate project NAME
 cli build project NAME
 cli deploy project NAME
 cli deploy static NAME

Alternatively (albeit this does not work if arguments to the commands are different)

 cli generate [ configuration NAME | project NAME ]
 cli build project NAME
 cli deploy [ project NAME | static NAME ]

Is it possible?

Is it desirable?

Generating files over existing file behaves poorly

Going to illustrate this example with hanami/hanami. It's an issue with this project though, not that one.

If you run: bundle exec hanami generate action web books#index, files are generated.

Let's say you edit apps/web/controllers/books/index.rb to look like this:

module Web::Controllers::Books
  class Index
    include Web::Action

    def call(params)
      # do some stuff
      # like process params
      # create some records maybe?
      # return status codes
      # maybe serialize a hash into JSON?
      # doesn't really matter.
      # These comments are just here for length, to show the odd behavior
    end
  end
end

Then run bundle exec hanami generate action web books#index again, here's what it does to that file:

module Web::Controllers::Books
  class Index
    include Web::Action

    def call(params)
    end
  end
end
uff
      # like process params
      # create some records maybe?
      # return status codes
      # maybe serialize a hash into JSON?
      # doesn't really matter.
      # These comments are just here for length, to show the odd behavior
    end
  end
end

(Yes, I pasted that correctly. The output is very strange, and not correct Ruby)

Instead we should ask the user if they want to replace or skip any existing files. And, if they replace, we obviously want to do it properly, by deleting the file first.

Interpreting options as arguments

Hanami::CLI seems to interpret options as values for the required args, when they're missing.

This is illustrated

Generating an action without a name for it (e.g. books#index)

bundle exec hanami generate action web --url=no

Generates the following files:

      create  apps/web/controllers/--url=no.rb
      create  apps/web/views/--url=no.rb
      create  apps/web/templates/--url=no.html.erb
      create  spec/web/controllers/--url=no_spec.rb
      create  spec/web/views/--url=no_spec.rb
      insert  apps/web/config/routes.rb

Generating a mailer without a name for it (e.g. book_added)

bundle exec hanami generate mailer [email protected]

Generates the following files:

      create  lib/blahtest/mailers/[email protected]
      create  spec/blahtest/mailers/[email protected]_spec.rb
      create  lib/blahtest/mailers/templates/[email protected]
      create  lib/blahtest/mailers/templates/[email protected]

Conflicting aliases between global and command options.

Describe the bug

When global options are defined in a Base command class from which all other commands are derived, it is possible for an alias in the global space to be "re-used" in the command space. For example if "-v" is an alias for :verbose in the global space and a command implements and option :validate with a "-v" alias there is now a conflict. I'm not sure what the current functionality is when dealing with this kind of conflict. It might be the command options rule; or, the global options rule. My preference would be to add a notation to the help text that calls out the conflict as a warning.... or maybe something else.

To Reproduce

Provide detailed steps to reproduce, an executable script would be best.

Expected behavior

In my Github repo MadBomber/experiments/cli_options/dry-cli

The base command defines -x for :xyzzy, and :exec command defines -x for the :exit option.

My environment

  • Affects my production application: NO
  • Ruby version: ... 3.2.2
  • OS: ... MacOS::HighSerria

Allow adding subcommand toplevel documentation (usage)

It would be nice to be able to add documentation to command prefixes. The way dry-cli deals with subcommands disallows adding documentation to be shown calling --help over the subcomand prefix.

Examples

Given the code on this example:

require "dry/cli"

module Foo
  module Commands
    extend Dry::CLI::Registry

    module Generate
      class App < Dry::CLI::Command
      end

      class Action < Dry::CLI::Command
      end
    end

    register "generate", aliases: ["g"] do |prefix|
      prefix.register "app",    Generate::App
      prefix.register "action", Generate::Action
    end
  end
end

It would be nice to be able to add documentation over the 'generate' subcommand. So its help will be shown like:

Description:

here you can include a general documentation about the subcommand.

Commands:
  foo.rb generate action
  foo.rb generate app

A possible implementation could be:

    register "generate", aliases: ["g"] do |prefix|
      prefix.desc 'HERE YOU CAN INCLUDE THE DOCUMENTATION OF THE COMMAND'
      prefix.register "app",    Generate::App
      prefix.register "action", Generate::Action
    end

I checked the code and saw that Prefix class was just syntax sugar for self.register, so no idea on how this could be implemented, or whether there is already a way of achieving what I'm looking for.

Thanks in advance! and thanks for dry-cli!

Showing default array option as a comma-separated list

First of all, thank you everyone who participated in creating this tool. I'm using it now for the development of Shrine, the script automatizes setting up the S3 bucket with correct permissions. It's been a joy to use so far โค๏ธ

Onto the issue. I have an array option (in my case permissions) which has a default value. When I print the page, the default value gets shown as a Ruby array:

module Commands
  class S3
    class Create < Dry::CLI::Command
      DEFAULT_PERMISSIONS = %w[
        GetObject
        PutObject
        PutObjectAcl
        DeleteObject
        ListMultipartUploadParts
        AbortMultipartUpload
      ]

      option :permissions, type: :array, default: DEFAULT_PERMISSIONS, desc: "list of S3 permissions"
      # ...
  end
end
$ shrine s3 create --help
Command:
  shrine s3 create

Usage:
  shrine s3 create

Options:
  --permissions=VALUE                   # list of S3 permissions, default: ["GetObject", "PutObject", "PutObjectAcl", "DeleteObject", "ListMultipartUploadParts", "AbortMultipartUpload"]
  ...
  --help, -h                            # Print this help

I think this might be a bit misleading, as the way to pass array arguments is via a comma-separated list. So, I think it would make sense that the default value is also displayed as a comma-separated list, to show the user how the input should look like.

Safely rescue Errno::EPIPE exceptions

When a ruby program is piped into | less and less is exited before the ruby program can exit, Ruby will raise a Errno::EPIPE exception when stdio is accessed, causing an ugly backtrace to be printed. Dry::CLI should rescue the Errno::EPIPE exception and safely exit, calling any additional cleanup blocks/methods, with status code 0.

Example

Without rescue Errno::EPIPE

begin
  (0..Float::INFINITY).each do |i|
    $stdout.puts i
    $stdout.flush
  end
rescue Interrupt
  exit 130
end
$ ruby test.rb | less
q
Traceback (most recent call last):
	3: from ./test.rb:4:in `<main>'
	2: from ./test.rb:4:in `each'
	1: from ./test.rb:6:in `block in <main>'
./test.rb:6:in `flush': Broken pipe (Errno::EPIPE)
$ 

With rescue Errno::EPIPE

begin
  (0..Float::INFINITY).each do |i|
    $stdout.puts i
    $stdout.flush
  end
rescue Interrupt
  exit 130
rescue Errno::EPIPE
end
$ ruby test.rb | less
q
$

Rubocop - Hard to commit changes with issues.

Overview

When committing to this project (in my case, a fork), I'm not able to push new commits to the remote repo due to Rubocop failing. This happens because I always run Rubocop as a Git Hook in order to resolve issues before finding out via a failed CI build.

If you are serious about using Rubocop in this project (kudos for including it), it'd be a good idea to add it to the CI build process to ensure it is being used properly by this project. I might suggest this configuration as a good configuration to use. :)

Steps to Recreate

Regardless of whether you auto-run Rubocop as a Git Hook or not, you can see the issues by running:

bundle exec rubocop

Screenshots/Screencasts

shell

Workaround

Delete this line:

gem 'rubocop', '0.49.1', require: false

...from the project Gemfile. That allows my Git Hook to see that Rubocop isn't configured for the project and I can commit code successfully.

Mutually-exclusive arguments or options

Feature request : In some cases it's required to have mutually-exclusive arguments or options.

For example if I have a program that will convert coordinates to a player name or a player name to its coordinates. I want to either have 1 argument (name) or two arguments (coodinate X and coordinate Y). Pseudo usage: program convert (name | coord_x coord_y).

Same for options, for example for git pull you can't have --ff-only and --no-ff or --ff-only and --rebase at the same time.

Example with docopt:

image

The workaround that is heavy is to provide two sub-sub-commands to the convert sub-command, one that converts from name to coords with one required argument and a second sub-sub-command that converts coords to name with 2 required arguments.

Another workaround is to use one unique and generic array argument name args and then counts the number of entries in it, if only one it's the name if two those are the coords. But the description should explain logic and behavior as well as the description of all three arguments in one, that also wouldn't work if the two mutually exclusive arguments has the same number of entries.

Another workaround is to use three options instead with none as required but of course this will end into errors if none is provided or if only 1 coord is provided or if one corod and a name if provided.

Can't figure out how to pass default values during #register

Looking at the code it seems like you can pass already-filled-in options to #register, but man, I can't for the life of me figure out what's going on with that. I had thought I could do:

MyCLI.register 'staging deploy', MedInstaller::Remote::Deploy, target: "staging"

...and get an option named 'target'. But no combination of register syntax and argument or option declarations in the target class is giving me anything useful. Any help appreciated.

Documentation Error

The installation instructions should specify that you need the version number. Currently, if you follow the instructions it's installing v 0.0.0. Or rubygems needs to be fixed so that it picks up the latest version.

Argument that is variable (or rather, all remaining args, up until the --)

in Thor, I can specify an argument that is like many things, say DOMAINS..., I would really like to be able to specify multiple arguments to my CLI app in this variable way. I dug into the source code a bit, and I didn't find any option to arguments that would make that happen, so logging the feature request.

Fantastic job on this library, I really like it!

Examples

./the_app one two three

would extract into the domains argument domains: ['one', 'two', 'three']

Resources

https://www.rubydoc.info/github/wycats/thor/Thor/Argument

If the argument type is an array, it'll grab all the arguments, I guess until the end of the line, which would conflict with the variadic arguments ability, but that could probably be supported also.

More powerful options

Flags

options are always considered having a value --mode=http2 or --mode http2.

  • Having flags (options without value)

E.g. just having --color, having the flag equals options.fetch(:color) returning true, not having the flag equals options.fetch(:color) returning false. It's better than having --color true and --color false with values: %w[true false] and casting the string to a boolean.

Aliases

It would be nice to have has many aliases as we want for an option.

  • Having aliases

E.g. -c, --color or --color, --colour.

It's often handy to have long flags that are mnemotechnic and short flags for those familiar with the tool that want to type faster.

Mutually exclusive

Sometimes it is required to have mutually exclusive options, see #128.

  • Having mutually exclusive options #128

Casting type

As passed from the CLI, all values are strings, so it would be handy to have cast types for options.

  • Having cast types

E.g. --age 21 or --advanced true. It's allows to cast type in the definition of the option, avoiding forgetting to cast it when the value is retrieved or to cast it several times if the option is used at multiple places.

Default Arguments are not propagated

Steps to reproduce:

  • Specify default argument(s) in the Command code:
      argument(
        :output_file,
        default: DEFAULT_HTML_FILE,
        desc: "Specify HTML output file. Defaults to #{DEFAULT_HTML_FILE}"
      )
  • Specify an option:
      option(
        :sort_order,
        aliases: ['-s'],
        type: :string,
        values: %w[alpha date],
        default: 'alpha',
        desc: 'Sorty by name-alpha (ascending), or date of last commit (descending)'
      )

Help output

โžค exe/codemonkey dig ../ -h
Command:
  codemonkey dig

Usage:
  codemonkey dig [OUTPUT_FILE]

Arguments:
  OUTPUT_FILE         	# Specify HTML output file. Defaults to 'git_repos.hml'

Options:
  --sort-order=VALUE, -s VALUE    	# Sort by name-alpha (ascending), or date of last commit (descending)
  --help, -h                      	# Print this help
  • Then run the command as follows:
codemonkey dig --sort-order=date

It will then assume the OUTPUT_FILE is --sort-order=date and create a file with that name.

Need more informative error messages

i try:

hanami new laanz-backend --database=postgresql --test=rspec --aplication-name=api

And got error, that contains absolutely uninformative message:

Error: Invalid param provided

Not problem, i can find error, but can it be more informative?

Unlisted value fails for option, but passes for argument

Describe the bug

Given the following example:

#!/usr/bin/env ruby
require "bundler/setup"
require "dry/cli"

module Foo
  module CLI
    module Commands
      extend Dry::CLI::Registry

      class Start < Dry::CLI::Command
        argument :arg, values: %w(one two)
        option :opt, values: %w(one two)

        def call(arg: nil, opt: nil, **)
          puts "ARG: #{arg.inspect} / OPT: #{opt.inspect}"
        end
      end

      register "start", Start
    end
  end
end

Dry::CLI.new(Foo::CLI::Commands).call

The following somewhat unexpected behaviour occurs:

example start --opt=one      # ARG: nil / OPT: "one"
example start --opt=three    # ERROR: was called with arguments "--opt=three"
example start one            # ARG: "one" / OPT: nil
example start three          # ARG: "three" / OPT: nil     <-- OOPS

The last one should also result in an error IMO.

My environment

  • Affects my production application: NO (but soon :-)
  • Ruby version: 3.2
  • OS: macOS Ventura

Add ability to use extensions with dry-cli

Dry::Core enables us to add extensions to our libraries, which is a nice feature to have. Other than being nice, it enables us to make our own local patches to dry-rb libraries in a controlled fashion.

I'd love to be able use extensions with dry-cli too.

Here's my use-case: I'm writing a CLI which will prompt users for input. Naturally, I use tty-prompt for that, and want a better integration with dry-cli: I don't want to write too much boilerplate across the whole project.

When looking at other CLI libraries, I found that Thor has a collection of similar methods, but they don't use an external dependency. Their interface is seamless: they just use say and ask on self, with no intermediate object.

Right now, I've managed to devise a simple extension:

# lib/dry/cli/extensions/tty_prompt.rb
# frozen_string_literal: true

require 'dry/cli'
require 'tty-prompt'

module Dry
  class CLI
    class Command
      def prompt
        @prompt ||= TTY::Prompt.new
      end
    end
  end
end

module Dry
  class CLI
    extend Dry::Core::Extensions

    register_extension(:tty_prompt) do
      require 'dry/cli/extensions/tty_prompt'
    end

    load_extensions(:tty_prompt)
  end
end

Usage:

      class Version < Dry::CLI::Command
        def call(**)
          prompt.say "Gem version #{MyGEM::VERSION}"
        end
      end

However, there's two major issues:

  • I have to install dry-core manually
  • I have to extend Dry::CLI with Dry::Core::Extensions

So my proposal is to add dry-core as a dependency and enable extensions as an out-of-the-box feature.

Would be even nicer to add a tty-prompt integration, but it'll take a while

Strange behaviour with option flags

Hi hanami-cli maintainers,

First of all, thank you for making a CLI framework which works well for what we have been doing! The command registry and commands as classes have been working really well for us.

We wanted to add aliases for some common options that are used throughout the CLI that we are building, they aren't documented as being available on options, but they seemed to work in a quick test when hacking around with the code. However we've seen some strange behaviour with options in general, it looks to be a problem with some pattern matching somewhere?

Given the following CLI structure:

require 'hanami/cli'

module MyCommands
  extend Hanami::CLI::Registry

  class TheCommand < Hanami::CLI::Command
    option :aaa, aliases: ['a'], default: 'aaa-default', desc: 'aaa'
    option :abc, default: 'abc-default', desc: 'abc'

    def call(**opts)
      puts opts.inspect
    end
  end

  register 'the-command', TheCommand
end

cli = Hanami::CLI.new(MyCommands)
cli.call

When running the following: bundle exec scratch.rb the-command -a hello then we get the following error:

Error: "the-command" was called with arguments "-a hello"

However if we remove the first option

require 'hanami/cli'

module MyCommands
  extend Hanami::CLI::Registry

  class TheCommand < Hanami::CLI::Command
    option :aaa, aliases: ['a'], default: 'aaa-default', desc: 'aaa'

    def call(**opts)
      puts opts.inspect
    end
  end

  register 'the-command', TheCommand
end

cli = Hanami::CLI.new(MyCommands)
cli.call

Then everything seems to work. Running bundle exec scratch.rb the-command -a hello we get the following (expected) output:

{:aaa=>"hello"}

I think this is by accident though, since if I remove the alias then the shorthand option...

require 'hanami/cli'

module MyCommands
  extend Hanami::CLI::Registry

  class TheCommand < Hanami::CLI::Command
    option :aaa, default: 'aaa-default', desc: 'aaa'

    def call(**opts)
      puts opts.inspect
    end
  end

  register 'the-command', TheCommand
end

cli = Hanami::CLI.new(MyCommands)
cli.call

Then everything seems to work, even though this time I don't think it should. Running bundle exec scratch.rb the-command -a hello we get the following (unexpected) output:

{:aaa=>"hello"}

Then if I add back in the second option you get the following behaviour:

require 'hanami/cli'

module MyCommands
  extend Hanami::CLI::Registry

  class TheCommand < Hanami::CLI::Command
    option :aaa, default: 'aaa-default', desc: 'aaa'
    option :abc, default: 'abc-default', desc: 'abc'

    def call(**opts)
      puts opts.inspect
    end
  end

  register 'the-command', TheCommand
end

cli = Hanami::CLI.new(MyCommands)
cli.call

Then if we run the same command again bundle exec scratch.rb the-command -a hello we get an error again:

Error: "the-command" was called with arguments "-a hello"

Many thanks,
Michael

Return non-zero exit status

Hi,

If I create a command and would like to automate the use of CLI, is there any way to return a non-zero exit status?

I tried the most intuitive way:

def call(*)
  do_something
  return 3
end

but doesn't work, any tip on this? ๐Ÿ˜‰

Can't use option with type array

#!/usr/bin/env ruby

require 'bundler/inline'

gemfile do
  source 'https://rubygems.org'

  gem 'hanami-cli', github: 'hanami/cli'
end

require 'hanami/cli'

module HanamiTestCLI
  extend Hanami::CLI::Registry

  class TestCommand < Hanami::CLI::Command
    option :only, type: :array

    def call(**options)
      puts options
    end
  end

  register 'test', TestCommand
end

Hanami::CLI.new(HanamiTestCLI).call
โ€บ ruby hanami-cli-bug.rb test --only foo bar
{:only=>"foo"}

I think thor allow to pass options separated by space as an array, but hanami-cli no, @jodosha WDYT?

Request for comments: Support for shell autocompletion

I would like to get your feedback whether you would accept a PR for shell autocompletion.

I am in the process of migrating a RubyGems from gli (GitHub) to dry-cli. gli generates output for bash and zsh autocompletion e.g. rubygem-command help -c (see documentation).

I am pretty sure hanami-cli would profit from such a feature as well.

Should I take a shot at this or is this something you are not interested?

Add command to recursively print `help`

Right now, --help only goes one level deep. I think we should keep it that way, but it'd be nice to have another command that recursively prints out every command (that is, print the whole tree, including subcommands).

I could see it being useful for getting an idea of all the available commands, and also something someone could pipe to grep to find the command they're looking for.

Examples

Right now:
hanami --help

Commands:
  hanami assets [SUBCOMMAND]
  hanami console                              # Starts Hanami console
  hanami db [SUBCOMMAND]
...

Proposed:
hanami --help-all (Not sure what the best name would be)

Commands:
  hanami assets precompile                  # Precompile assets for deployment
  hanami console                            # Starts Hanami console
  hanami db apply                           # Migrate, dump the SQL schema, and delete the migrations (experimental)
  hanami db console                         # Starts a database console
  hanami db create                          # Create the database (only for development/test)
  hanami db drop                            # Drop the database (only for development/test)
  hanami db migrate [VERSION]               # Migrate the database
  hanami db prepare                         # Drop, create, and migrate the database (only for development/test)
  hanami db rollback [STEPS]                # Rollback migrations
  hanami db version                         # Print the current migrated version
...

Resources

This request from @unrooty #55 (comment)
This request from @kigster #55 (comment)

Variadic Arguments

Hello,
Firstly thanks for Hanami CLI! It is so much more elegant than Thor, which has been my goto tool up to this point.

On to business, I'm in the process of porting an old Thor based CLI to Hanami CLI and I found a dealbreaker missing feature for me - being able to parse and use a variable number of arguments to the CLI.

The use case here is something similar to ssh, docker run, and the like where the user provides a series of arguments that'll become the command our program will execute somewhere.

Example:

ssh [email protected] -- /bin/bash -c "echo '127.0.0.1 myapp.com' > /etc/hosts"
docker run -it --rm postgres:latest /bin/bash
docker run -it --rm postgres:latest -- psql postgres://user:pass@myhost/mydb -c "select * from users"

As you can see they can get pretty elaborate ;)

I've kind of hacked it in this way - but I'd rather make it official and do it more elegantly:

diff --git a/lib/hanami/cli/parser.rb b/lib/hanami/cli/parser.rb
index e9b7534..2cc18b1 100644
--- a/lib/hanami/cli/parser.rb
+++ b/lib/hanami/cli/parser.rb
@@ -27,6 +27,8 @@ module Hanami
           end
         end.parse!(arguments)

+        Hanami::CLI.define_singleton_method(:all_arguments) { arguments }
+
         parsed_options = command.default_params.merge(parsed_options)
         parse_required_params(command, arguments, names, parsed_options)
       rescue ::OptionParser::ParseError

I'm happy to do the work on it but figured I'd ask first as to how it should be done - any opinions?

Avoid hardcoding ERROR: into error messages

I noticed that failure Result objects have ERROR: hardcoded into their message. How errors are formatting or printed should be left up to the user or some print_error method that can be overrode.

"Global Options"

It would be nice to be able to specify global options that can apply to any command/sub command, say something like:
--debug or --json or --verbose that would add that option to the options of any CLI out there.

Examples

I suppose it'd be handy to have something at the CLI Registry level that allowed you to specify the same arguments that apply to option, but globally available to all commands.

#some classes for commands

global_option :profile, aliases: ['-p'], required: true, type: :string, desc: 'AWS Profile'
global_option :debug, required: false, type: :boolean, desc: 'enable debug output'
global_option :json, required: false, type: :boolean, desc: 'change output to JSON format'

register 'version', Version, aliases: %w[v -v --version]

Resources

Something similar to what the teletype gem creates. There's some subcommands going on in here that I'm refactoring out, but --debug and --json are globally available options.

master> bundle exec exe/dns --help
Commands:
  dns dns [SUBCOMMAND]  # Command description...
  dns help [COMMAND]    # Describe available commands or one specific command
  dns version           # sparklint version

Options:
  [--debug], [--no-debug]  # enable debug logging
  [--json], [--no-json]    # output results as json

Inline command syntax

I would love to address the problem, that there is no way right now to create a single command executable. The easiest way for doing this IMO is to define DefaultCommand which will be called if there is nothing else in the Registry.

But I'd love to suggest another approach. Let's think of the reasoning for using dry-cli. I believe that the developer is looking for some gem to him drop-in syntax just to make a single command to do exactly one thing without many options.

And I really like the way how Sinatra does it.

Examples

hellonamer

# /usr/bin/env ruby
require 'dry/cli/global'

argument :name, required: true, desc: "The name of the person to greet"
argument :age, desc: "The age of the person to greet"

run do |name:, age:|
  result = "Hello, #{name}."
  result = "#{result} You are #{age} years old." unless age.nil?

  puts result
end

And then just run

$ chmod +x hellonamer
$ ./hellonamer Homer

If that is not enough, you are still free to use all the features with a registry, subcommands and sub-subcommands, running Dry::CLI::Core instance

require "dry/cli/core"
module Commands
  extend Dry::CLI::Registry

  class Hello < Dry::CLI::Command
    def call(*)
    end
  end
end

Commands.register "hi", Commands::Hello
Dry::CLI::Base.new(Foo::CLI::Commands).call

WDYT?

Feature request: colorized output

This is a feature I started working on long ago for Hanami: hanami/hanami#906

There could be two parts to this:

  • Colorizing the output of dry-cli, like each command's help and the list of subcommands
  • Adding something like say that users of the library can use to print in color (instead of using print).

e.g.

say "create", color: :red

This should probably be like print rather than puts (no trailing newline), since people should be able to print in several colors on one line.

The concrete use-case for this could be like hanami/hanami#906, being able to colorize the output of file generation for Hanami.

Is this something we'd be interested in adding to dry-cli? Any thoughts on the API?

Argument repeated on require

If I put a class that inherits from Hanami::CLI::Command in a different file to the file where I register the command...

  • If the command has no arguments
    • I have to require it (ok)
  • but if it has arguments
    • If I don't require it it works (huh)
    • and if I do require it, then the argument gets repeated

eg

class Notice < Hanami::CLI::Command
# etc
def call(file:, **) 
# etc

results in
app notice FILE FILE

not
app notice FILE

cheers, Mike (with thanks for your work on this and the usual caveat that it may be me screwing something up :-) )

Can't generate a new project

Hi,
I'm new here. so, i'm trying to generate a project from the main page example.
http://hanamirb.org/#install

OS: ubuntu 16.04
Ruby: 2.3.0
Hanami: 1.2.0

Here is output:

hanami new test-project
wrong number of arguments (given 0, expected 2..3)
(erb):2:in `test'
	(erb):2:in `binding'
	/home/user/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/erb.rb:864:in `eval'
	/home/user/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/erb.rb:864:in `result'
	/home/user/.rvm/gems/ruby-2.3.0/gems/hanami-1.2.0/lib/hanami/cli/commands/command.rb:123:in `call'
	/home/user/.rvm/gems/ruby-2.3.0/gems/hanami-1.2.0/lib/hanami/cli/commands/command.rb:149:in `render'
	/home/user/.rvm/gems/ruby-2.3.0/gems/hanami-1.2.0/lib/hanami/cli/commands/command.rb:157:in `generate_file'
	/home/user/.rvm/gems/ruby-2.3.0/gems/hanami-1.2.0/lib/hanami/cli/commands/new.rb:554:in `generate_file'
	/home/user/.rvm/gems/ruby-2.3.0/gems/hanami-1.2.0/lib/hanami/cli/commands/new.rb:347:in `generate_application_templates'
	/home/user/.rvm/gems/ruby-2.3.0/gems/hanami-1.2.0/lib/hanami/cli/commands/new.rb:315:in `block in call'
	/home/user/.rvm/gems/ruby-2.3.0/gems/hanami-1.2.0/lib/hanami/cli/commands/new.rb:314:in `chdir'
	/home/user/.rvm/gems/ruby-2.3.0/gems/hanami-1.2.0/lib/hanami/cli/commands/new.rb:314:in `call'
	/home/user/.rvm/gems/ruby-2.3.0/gems/hanami-1.2.0/lib/hanami/cli/commands/command.rb:85:in `call'
	/home/user/.rvm/gems/ruby-2.3.0/gems/hanami-cli-0.2.0/lib/hanami/cli.rb:57:in `call'
	/home/user/.rvm/gems/ruby-2.3.0/gems/hanami-1.2.0/bin/hanami:6:in `<top (required)>'
	/home/user/.rvm/gems/ruby-2.3.0/bin/hanami:23:in `load'
	/home/user/.rvm/gems/ruby-2.3.0/bin/hanami:23:in `<main>'
	/home/user/.rvm/gems/ruby-2.3.0/bin/ruby_executable_hooks:15:in `eval'
	/home/user/.rvm/gems/ruby-2.3.0/bin/ruby_executable_hooks:15:in `<main>'

Thank you.
Let me know if you need more information

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.