Project

clive

0.0
No commit activity in last 3 years
No release in over 3 years
Clive provides a DSL for building command line interfaces. It allows you to define commands and options, which can also take arguments, and then runs the correct stuff!
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 2.6
~> 0.10
 Project Readme

clive

Clive lets you easily create command line interfaces, from simply checking for the presence of a few flags to multiple command behemoths.

Install

Install with:

$ gem install clive

Usage

NOTE: Throughout I will be using the new 1.9 {a: b} hash syntax, obviously the old {:a => b} syntax still works and can be used if you want or where 1.8 compatibility is needed.

Clive is generally used by creating a class to hold your command line interface as shown in the example below.

# my_app.rb
require 'clive'

module MyApp
  VERSION = "0.1.4"

  # some code

  class CLI < Clive
    config name: 'myapp'

    opt :v, :version, 'Display the current version' do
      puts MyApp::Version
      exit 0
    end
  end
end

result = MyApp::CLI.run

Then run with my_app.rb --version to display the version for MyApp. By default #run will use ARGV as the input, but you can pass something else if necessary.

.run returns an instance of Clive::StructHash by default, this is a hybrid hash and struct allowing you to access keys by calling them or using #[]. It also stores any extra arguments that may have been passed which can be accessed with #args or #[:args].

class CLI
  opt :n, :name, args: '<first> <last>'
  opt :age, arg: '<age>', as: Integer
end

result = CLI.run %w(--name John Doe --age 23 "I like coding!")

bio  = result.args #=> "I like coding!"
name = result.name #=> ['John', 'Doe']
age  = result.age  #=> 23

This simple example shows how Clive can handle multiple arguments, casting to types (Integer in this case) and how extra arguments are stored in #args.

Options

Options are defined using #opt and/or #option they can have short (-a) or long names (--abc) and can also have arguments. The description can be given as an argument to #opt or can be defined before it using #desc (or #description).

opt :v, :version, 'Display the current version' do
  # ...
end

# is equivalent to

desc 'Display the current version'
opt :v, :version do
  # ...
end

Longer option names, containing _s are called by replacing the _ with a - so

opt :longer_name_than_expected

would be called with --longer-name-than-expected.

Boolean Options

Boolean options are options which can be called with a no- prefix, which then passes false to the block/state. For example,

bool :a, :auto

Can be called with -a or --auto which would set :auto to true, or --no-auto which sets :auto to false. If a block is given you can retrieve the truth by adding block parameters or using the truth variable which is automatically set.

bool :a, :auto do |t|
  puts t
end

# OR

bool :a, :auto do
  puts truth
end

Boolean options must have a long name.

Commands

Commands are defined using #command. They can be used to group related Options together acting like a namespace, but Commands are fully featured Options so can also take arguments. Commands can be created with multiple names.

desc 'Create a new project'
command :new, :create, arg: '<dir>', as: Pathname do

  # Set the default type to use
  set :type, :basic

  desc 'Select type of template to use'
  opt :type, arg: '<choice>', in: %w(basic complex custom), as: Symbol

  bool :force, 'Force overwrite'

  action do
    puts "Creating #{get :type} in #{dir}"
    # do writing
  end
end

The above example also shows using the #set and #get methods, these allow you to set and get values from the state. Also note how the logic for executing the new command is given to #action, this is because the block passed to #command is used for option definition.

Arguments

As previously talked about Options and Commands can take arguments by passing a hash with the key :arg or :args.

opt :size, args: '<height> <width>'

The option --size takes two arguments. These would be saved to state as an array, for instance running with --size 10 40 would set :size to ['10', '40'].

Arguments can be made optional by enclosing one or more arguments with [ and ]:

# both required
opt :size, args: '<h> <w>'
# --size 10   #=> Error

# first required
opt :size, args: '<h> [<w>]'
# --size 10   #=> [10, nil]

# second required
opt :size, args: '[<h>] <w>'
# --size 10   #=> [nil, 10]

# neither required
opt :size, args: '[<h> <w>]'
# --size 10   #=> [10, nil]

You can make an argument 'infinite' by appending the name with ..., like <arg>.... Optional-ness is respected so <arg>... expects at least one argument whereas [<arg>...] takes 0 or more arguments. Infinite arguments also play well with standard arguments, and will return arrays of items even if only one argument was passed.

opt :items, :arg => '<thing>...'
# --items            #=> Error
# --items iPad       #=> ['iPad']
# --items iPad iPod  #=> ['iPad', 'iPod']

opt :items, :arg => '[<thing>...]'
# --items            #=> []
# and same as previously in other cases

opt :items, :arg => '<amount> <thing>...', :as => [Integer, nil]
# --items 5 iPad iMac  #=> [5, 'iPad', 'iMac']

There are also various options that can be passed to constrain or change the arguments. If one of the below is passed to an option with :arg or :args a generic argument called <arg> will be added.

Types (:types, :type, :kind or :as)

An argument will be checked if it matches how the type should look, then converts it to that type. For more information see lib/clive/type/definitions.rb.

opt :list, as: Array

This accepts a comma delimited list of items, --list a,b,c and sets :list to ['a', 'b', 'c'].

Matches (:matches or :match)

Allows you to say that an argument must match a regular expression (or any object which responds to #match).

opt :word, match: /^\w+$/

This accepts --word hello but not --word 123.

Withins (:withins, :within or :in)

Allows you to say that an argument must be within a passed Array, Set or Range (any object which responds to #include?).

opt :num, in: "1".."100"

This accepts --num 50 but not --num 900. The example above had to use a range of Strings because that is what is passed, you can use this with :type to use Integers.

opt :num, as: Integer, in: 1..100

Would work in the same way as the one above but return an Integer.

Defaults (:defaults or :default)

Allows you to give a default value that should be used if an argument is not given.

opt :type, default: 'house'

So --type would set :type to 'house', but --type shed would set :type to 'shed'. The default value is only set if the option is used. To set a value regardless of whether the option is used use #set in the class or commands body.

set :type, 'house'
opt :type

Would always set :type to 'house' even when --type is not used.

Constraints (:constraint or :constraint)

Allows you to constrain the argument using a Proc, this is to cover the very few events where the above options do not satisfy the requirements.

opt :long_word, constraint: -> i { i.size >= 7 }

Accepts --long-word eventually but not --long-word event. You can also pass a symbol which will have #to_proc called on it.

opt :odd, as: Integer, constraint: :odd?

This only accepts odd Integers.

Runner

All blocks passed to options or given to a command's action are run in the Runner class. This provides a few shortcuts to make life easier. Here is a quick run down with examples.

Argument Referencing

You can reference an options or commands arguments directly by name without having to use block parameters.

opt :size, args: '<height> <width>', as: [Float, Float] do # no params!
  puts "Area = #{height} * #{width} = #{height * width}"
end

As shown earlier the truthiness of a boolean option is set to the value truth.

Working with the State

Four fundamental methods are defined for working with the state, #get, #set, #update and #has?.

#get allows you to get values previously set.

opt :get_some_key do
  puts get(:some_key) #=> value set for :some_key
end

#set allows you to set values in the state.

opt :set_some_key, arg: '<value>' do
  set :some_key, value
end

#update allows you to modify a value in the state.

set :list, []
opt :add, arg: '<item>' do
  update :list, :<<, item

  # or using a block
  update(:list) {|l| l << item }
end

#has? tells you whether a value has been set for the key.

opt :has_some_key do
  puts has?(:some_key)
end

Help Formatters

Clive comes with two help formatters, one with colour and the other without. To use the plain formatter (colour is default), use

class CLI < Clive
  config formatter: Clive::Formatter::Plain.new

  # ...
end

CLI.run

To create your own formatter take a look at lib/clive/formatter.rb.

Clive::Output

clive/output contains various monkey patches on String that allow you to easily colourise output.

puts "I'm blue".blue                     # will print blue text
puts "I'm green and bold".green.bold     # will print green and bold text
puts "Crazy".blue.l_yellow_bg.underline
# etc

Colours available: white, green, red, magenta, yellow, blue, cyan, black. Effects available: bold, underline, blink, reverse.

Light versions can be used by prepending the name with l_, ie. l_red, the light version of black is called grey.

All colours can be used as backgrounds by appending _bg, ie. red_bg, light backgrounds are as expected, l_red_bg.

Copyright

Copyright (c) 2010-12 Joshua Hawxwell. See LICENSE for details.