33
COMMAND LINE APPLICATIONS RUBY Alexander Merkulov / merqlove @merqlove

Command Line Applications with Ruby

Embed Size (px)

Citation preview

COMMANDLINEAPPLICATIONS RUBY

AlexanderMerkulov

/

merqlove @merqlove

DO_SNAPSHOTCLI

Acommand-linesnapshotmakerforDigitalOceandroplets.

LOOKINGFORGEMS&

RakeOptionParserOptitronCommanderMain...manuallybuilditanywhereelse

BESTPRACTICES

BASEFEATURES&FACTSCOMPARISON

Feature OP Thor GLI

Numberofdownloads asRuby 63596500 898088

Authors Community Y.Katz D.Copeland

Noruntimedependencies Yes Yes Yes

Theargumentspecificationandthecode Yes Yes Yes

Outputanoptionsummary Yes Yes Yes

Optionalandmandatoryarguments Yes Yes Yes

Argumentscanbeautomaticallyconverted Yes Yes Yes

Argumentscanberestrictedtoacertainset Yes Yes Yes

ADVANCEDFEATURESCOMPARISONFeature OP Thor GLI

Descriptionhelper No Yes Yes

Longdescriptionhelper No Yes Yes

Actions No Yes Yes/No

Subcommands No Yes Yes

EasyChildclassloading No Yes No

Minimalisticapplicationcodebase No Yes Yes/No

Activelymaintains Yes/No Yes Yes

Fancycolors No Yes No

GIT-LIKEINTERFACECOMMANDLINEPARSER

Usefulifyouworkonmonolithbinary.Quitereadablecode.NoOOPrequired:)HasscaffoldgeneratorwithTDD/BDDapproach.Regularupdates.

GLI DAVETRON5000/GLI

$gliinitsomecommandversion├── Gemfile├── README.rdoc├── Rakefile├── bin│ └── some # our binary├── dist # tools for deploy├── features # if cucumber│ ├── some.feature│ ├── step_definitions│ │ └── some_steps.rb│ └── support│ └── env.rb├── lib│ ├── some│ │ └── version.rb│ └── some.rb├── some.gemspec└── test ├── default_test.rb └── test_helper.rb

ADDSOMECOMMANDSdesc 'DEFAULT. Create and cleanup snapshot\'s'command [:s, :snap] do |c| c.desc 'Select api version.' c.arg_name '1' c.flag [:p,:protocol], default_value: 2, type: Numeric

c.desc 'How much snapshots you want to keep?' c.arg_name '5' c.flag [:k,:keep], default_value: 10, type: Numeric ... c.desc 'Stop creating snapshots if maximum is reached.' c.switch [:s,:stop] c.action do |global_options,options,args| DoSnapshot::Command.new(options).snap endend

desc 'Shows the version of the currently installed DoSnapshot gem'version DoSnapshot::VERSION

gist

ADDSOMELOGICpre do |global,command,options,args| $some_service = DoSnapshot::Service.new(options) trueend

post do |global,command,options,args| $some_service = nilend

on_error do |exception| $some_service.send(exception) end

exit run(ARGV)

BUTWHATWITHARRAY,HASHARGUMENTS?

TIMETOTRY:)$ do_snapshot helpNAME do_snapshot - `do_snapshot` able to create and cleanup snapshots on your droplets.

SYNOPSIS do_snapshot [global options] command [command options] [arguments...]

GLOBAL OPTIONS -c, --clean=arg - Cleanup snapshots after create. If you have more images than you want to `keep`, older will be deleted. (default: true) -d, --delay=5 - Delay between snapshot operation status requests. (default: 10) --digital_ocean_access_token=YOURLONGAPITOKEN - DIGITAL_OCEAN_ACCESS_TOKEN. if you can't use environment. (default: none) --help - Show this message -k, --keep=5 - How much snapshots you want to keep? (default: 10) -l, --log=/Users/someone/.do_snapshot/main.log - Log file path. By default logging is disabled. (default: none) -p, --protocol=1 - Select api version. (default: 2) --smtp=user_name:[email protected] - SMTP options. (default: none) -q, --quiet=arg - Quiet mode. If don't need any messages and in console. (default: none) -s, --stop=arg - Stop creating snapshots if maximum is reached. (default: none) --timeout=250 - Timeout in sec's for events like Power Off or Create Snapshot. (default: 3600) -v, --trace=arg - Verbose mode. (default: none)

COMMANDS version - Shows the version of the currently installed DoSnapshot gem help - Shows a list of commands or help for one command s, snap - DEFAULT. Create and cleanup snapshot's

WHATABOUTTESTS?

RSpec/MinitestArubaCucumber

FEATURETEST# features/some.featureFeature: My bootstrapped app kinda works In order to get going on coding my awesome app I want to have aruba and cucumber setup So I don't have to do it myself

Scenario: App just runs When I get help for "some" Then the exit status should be 0

# features/step_definitions/some_steps.rbWhen /^I get help for "([^"]*)"$/ do |app_name| @app_name = app_name step %(I run `#{app_name} help`)end

OPTIONS&ARGUMENTSCOMPARISON

Feature OP Thor GLI

Advancedoptionmode No Yes No

Multipleoptionsone-liner No Yes No

Optionsorder No Yes No

Arrayarguments No Yes Yes

Hasharguments Yes/No Yes No

Enumarguments No Yes No

Global(class-wide)arguments No Yes No

CUSTOMPARSERSaccept Array do |value| value.split(/\s/).map(&:strip)end

accept Hash do |value| result = {} value.split(/\s/).each do |pair| k,v = pair.split(/:/) result[k] = v end resultend

...

desc 'SMTP options.'arg_name 'user_name:[email protected]'flag :smtp, type: Hash

$ do_snapshot -k 5 --smtp=user_name:user1 password:123456

gist

binBinaryfile(s)

distRakedeployscripts

lib/*/runnerApplicationrunner

lib/*/cliCLImonolithorparent

lib/*/commandCommandinterface

lib/*/logApplicationlogger

specRSpectestsincludingfeatures&fixtures

├── bin│ └── do_snapshot├── dist│ ├── ...│ ├── gem.rake│ ├── manifest.rake│ └── zip.rake├── lib│ ├── do_snapshot│ │ ├── cli.rb│ │ ├── command.rbb│ │ ├── log.rb│ │ ├── runner.rb│ │ └── ...│ └── do_snapshot.rb├── spec│ ├── do_snapshot│ │ └── ...│ ├── fixtures│ ├── support│ │ ├── aruba.rb│ │ └── ...│ ├── do_snapshot_spec.rb│ └── spec_helper.rb├── Gemfile├── README.md├── Rakefile└── do_snapshot.gemspec

BINARY#!/usr/bin/env ruby

Signal.trap('INT') { exit 1 }

# resolve bin path, ignoring symlinksrequire 'pathname'bin_file = Pathname.new(__FILE__).realpath

# add self to libpath$LOAD_PATH.unshift File.expand_path('../../lib', bin_file)

require 'do_snapshot/runner'

DoSnapshot::Runner.new(ARGV.dup).execute!

RUNNERclass Runner def initialize(argv, stdin = STDIN, stdout = STDOUT, stderr = STDERR, kernel = Kernel) @argv, @stdin, @stdout, @stderr, @kernel = argv, stdin, stdout, stderr, kernel end def execute! exit_code = begin run_cli rescue StandardError => e display_backtrace_otherwise(e) rescue SystemExit => e e.status ensure clean_before_exit end @kernel.exit(exit_code) end ...end

github

SAMPLECLIMONOLITHSampleclassforCLIapplication.

map-mappingcommands.

initialize-prepareenvironment.

no_commands-definehelperorprivatemethods.

module DoSnapshot class CLI < Thor include DoSnapshot::Helpers

default_task :snap

map %w( c s create ) => :snap map %w( -V ) => :version

def initialize(*args) super

# custom initialization end

...

no_commands do # define helpers end endend

github

THORDSLdesc-description&shortcuts

long_desc-multilinecommanddescription.

method_option-commandoption

desc 'c / s / snap / create', '...'long_desc <<-LONGDESC`do_snapshot` able to create snapshots ...

VERSION: #{DoSnapshot::VERSION}LONGDESCmethod_option :mail, type: :hash, aliases: %w( -m ), banner: 'to:[email protected]', desc: 'Receive mail if fail or maximum is reached.'

def snap ...end

github

METHOD_OPTIONaliases—Alistofaliasesforoption.banner—Theshortdescriptionoftheoption,printedoutintheusagedescription.default—Thedefaultvalueofthisoptionifitisnotprovided.lazy_default—Adefaultthatisonlypassediftheclioptionispassedwithoutavalue.desc—Adescriptionfortheoption.required—Indicatesthatanoptionisrequiredtype—:string,:hash,:array,:numeric,or:booleanenum—Alistofallowedvaluesforthisoption.

THORACTIONSsayaskyes?no?add_file...copy_filetemplateinsiderun

TESTTOOLSRSpecArubaWebmockRake

ARUBA&RSPECAREFRIENDS# support/aruba.rb

require 'aruba/rspec'require 'do_snapshot/runner'

Aruba.configure do |config| config.command_launcher = :in_process config.main_class = DoSnapshot::Runnerend

ARUBAUSEFULHELPERSrunrun_simpleall_stdoutall_stderrall_outputexpand_pathwith_environment

ARUBAWITHINRSPECRSpec.describe DoSnapshot::Runner, type: :aruba do context 'commands' do context '.help' do it 'shows a help message' do run 'do_snapshot help' expect(all_stdout) .to match('Commands:') end end

context '.version' do it 'with right version' do run 'do_snapshot version' expect(all_stdout).to include(DoSnapshot::VERSION) end end endend

$ do_snapshot help sUsage: do_snapshot c / s / snap / create

Options: -p, [--protocol=1] # Select api version. # Default: 1 -o, [--only=123456 123456 123456] # Select some droplets. -e, [--exclude=123456 123456 123456] # Except some droplets. -k, [--keep=5] # How much snapshots you want to keep? # Default: 10 -d, [--delay=5] # Delay between snapshot operation status requests. # Default: 10 -m, [--mail=to:[email protected]] # Receive mail if fail or maximum is reached. -t, [--smtp=user_name:[email protected]] # SMTP options. -l, [--log=/Users/someone/.do_snapshot/main.log] # Log file path. By default logging is disabled. -s, [--stop], [--no-stop] # Stop creating snapshots if maximum is reached. -v, [--trace], [--no-trace] # Verbose mode. -q, [--quiet], [--no-quiet] # Quiet mode. If don't need any messages and in console.

Description: `do_snapshot` able to create and cleanup snapshots on your droplets.

CHECKOUTPUT

Try

BESTPRACTICES:Deploytorepositoryofyourchoice.UsesingleRaketaskforpushingtoanyservice.Providezip,tgzpacksofyourCLIapp.UseRubocop&Coveragemetrics.Don'tbetoolazytowritetests.Semanticversioning.Providedocsor/andwiki....So,asforanyRubyapplication.

EXAMPLES&HOW-TOgithub.com/chef/omnibusgithub.com/mitchellh/vagrantgithub.com/merqlove/do_snapshotgithub.com/pseudomuto/bookeryAwesomeCommandLineAppsThor&OptionParsercomparison.

THANKS!

Q?2015

/merqlove @merqlove