Do One Thing - ess/belafonte GitHub Wiki
There is a long tradition (well, going back to the 1970s, at least) that dictates that a command-line application should do one thing, and it should do that one thing well. This applies well to the idea of a Belafonte::App instance.
Keep your app small. A single app class shouldn’t do more than one thing, if you can help it. If you find yourself using options and switches to control the flow of your application's handle
method, chances are very good that you'd be better served by breaking that App into several smaller apps, then mounting them under a common command name.
Example
This is a pretty simple example, but it is actually a very minimal alteration of a real-world scenario that has been seen recently.
BadApp
This is the "ssh" command, which supports both connecting to servers and running one-off commands on servers.
class BadApp < Belafonte::App
title "bad-app"
summary "connect to a remote server"
option :command, short: 'c', long: 'command', argument: 'command',
description: 'command to run'
arg :server
def handle
if option(:command)
SSH.run(arg(:server).first, option(:command))
else
SSH.connect(server)
end
end
end
This is how one uses this app:
# Connecting to a server
bad-app some.server.com
# Running a command on a server
bad-app -c 'hostname' some.server.com
That seems too simple to worry about, but what happens when a third, fourth, Nth path is added? Additionally, how hard or easy is it to describe the behavior of this application?
BetterApp
Here, we have created a namespace to contain the entire application, broken both actions out into their own apps, and then wrapped them with a "main" app. This provides arguably better UX than the single-app example, and it is also generally easier to describe the behavior of each part.
module BetterApp
# An application that allows one to open a shell on a remote server
class Connect < Belafonte::App
title 'connect'
summary 'connect to a remote server via SSH'
arg :server
def handle
SSH.connect(arg(:server).first)
end
end
# An application that allows one to run a command on a remote server
class Run < Belafonte::App
title 'run'
summary 'run a command on a remote server'
arg :server
arg :remote_command, times: :unlimited
def handle
SSH.run(arg(:server).first, remote_command)
end
private
def remote_command
arg(:remote_command).join(' ')
end
end
# The main entry point for the "better-app" application
class Main < Belafonte::App
title 'better-app'
summary 'operations for remote servers'
mount Connect
mount Run
end
end
This is how the app is used:
# connect to a server
better-app connect some.server.com
# run a command on a server
better-app run some.server.com hostname