Getting Started

This guide explains how to use samovar to build command-line tools and applications.

Installation

Add the gem to your project:

<s>~ bash $ bundle add samovar </s>~

Or install it yourself as:

<s>~ bash $ gem install samovar </s>~

Core Concepts

Samovar provides a declarative class-based DSL for building command-line parsers. The main concepts include:

Usage

Create Command classes that represent specific functions in your program. The top level command might look something like this:

~~~ ruby require “samovar”

class List < Samovar::Command self.description = “List the current directory”

    def call
            system("ls -lah")
    end

end

class Application < Samovar::Command options do option “–help”, “Do you need help?” end

    nested :command, {
            "list" => List
    }, default: "list"

    def call
            if @options[:help]
                    self.print_usage
            else
                    @command.call
            end
    end

end

Application.call # Defaults to ARGV. ~~~

Basic Options

Options allow you to parse command-line flags and arguments:

~~~ ruby require “samovar”

class Application < Samovar::Command options do option “-f/–frobulate <text>”, “Frobulate the text” option “-x | -y”, “Specify either x or y axis.”, key: :axis option “-F/–yeah/–flag”, “A boolean flag with several forms.” option “–things <a,b,c>”, “A list of things” do |value| value.split(/s,s/) end end end

application = Application.new([“-f”, “Algebraic!”]) application.options # ‘Algebraic!’

application = Application.new([“-x”, “-y”]) application.options # :y

application = Application.new() application.options # true

application = Application.new([“–things”, “x,y,z”]) application.options # [‘x’, ‘y’, ‘z’] ~~~

Nested Commands

You can create sub-commands that are nested within your main application:

~~~ ruby require “samovar”

class Create < Samovar::Command def invoke(parent) puts “Creating” end end

class Application < Samovar::Command nested “<command>”, “create” => Create

    def invoke(program_name: File.basename($0))
            if @command
                    @command.invoke
            else
                    print_usage(program_name)
            end
    end

end

Application.new().invoke ~~~

ARGV Splits

You can split arguments at a delimiter (typically --):

~~~ ruby require “samovar”

class Application < Samovar::Command many :packages split :argv end

application = Application.new([“foo”, “bar”, “baz”, “–”, “apples”, “oranges”, “feijoas”]) application.packages # [‘foo’, ‘bar’, ‘baz’] application.argv # [‘apples’, ‘oranges’, ‘feijoas’] ~~~

Parsing Tokens

You can parse positional arguments using one and many:

~~~ ruby require “samovar”

class Application < Samovar::Command self.description = “Mix together your favorite things.”

    one :fruit, "Name one fruit"
    many :cakes, "Any cakes you like"

end

application = Application.new([“apple”, “chocolate cake”, “fruit cake”]) application.fruit # ‘apple’ application.cakes # [‘chocolate cake’, ‘fruit cake’] ~~~

Explicit Commands

Given a custom Samovar::Command subclass, you can instantiate it with options:

<s>~ ruby application = Application[“–root”, path] </s>~

You can also duplicate an existing command instance with additions/changes:

<s>~ ruby concurrent_application = application[“–threads”, 12] </s>~

These forms can be useful when invoking one command from another, or in unit tests.

Error Handling

Samovar provides two entry points with different error handling behaviors:

call() - High-Level Entry Point

Use call() for CLI applications. It handles parsing errors gracefully by printing usage information:

~~~ ruby

Automatically handles errors and prints usage

Application.call(ARGV) ~~~

If parsing fails or the command raises a {ruby Samovar::Error}, it will: - Print the usage information with the error highlighted - Return nil instead of raising an exception

parse() - Low-Level Parsing

Use parse() when you need explicit error handling or for testing:

<s>~ ruby begin app = Application.parse() rescue Samovar::InvalidInputError => error # Handle the error yourself puts “Invalid input: #{error.message}” end </s>~

The parse() method raises exceptions on parsing errors, giving you full control over error handling.

Error Types

Samovar defines several error types: