Created
August 16, 2018 18:19
-
-
Save ahoward/ef408c76656f964ba22dfc053f6ff29b to your computer and use it in GitHub Desktop.
even though i author'd main.rb - which is a really complete cli tool, i 've been yearning for something simpler for writing small cli tools. eg. in a rails ./scripts/ directory...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env ruby | |
require_relative '../lib/script.rb' | |
script { | |
help ' | |
HELP! | |
' | |
run { | |
p [@mode, @argv, @options] | |
} | |
run :foo do | |
p [@mode, @argv, @options] | |
end | |
run :bar do | |
p [@mode, @argv, @options] | |
end | |
run 'foo-bar' do | |
p [@mode, @argv, @options] | |
end | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env ruby | |
-> do | |
# require_relative '../lib/script.rb' | |
script { | |
help <<-____ | |
NAME | |
script.rb | |
SYNOPSIS | |
script.rb is an extremely minimal but complete command line script dsl and library | |
TL;DR; | |
# | |
~> cat a.rb | |
script { | |
} | |
~> ruby a.rb | |
NAME | |
#TODO | |
SYNOPSIS | |
#TODO | |
DESCRIPTION | |
#TODO | |
EXAMPLES | |
#TODO | |
# | |
~> cat a.rb | |
script { | |
help 'this is the help' | |
} | |
~> ruby a.rb | |
this is the help | |
# | |
~> cat a.rb | |
script { | |
run{ p 42 } | |
} | |
~> ruby a.rb | |
42 | |
# | |
~> cat a.rb 1 2 3 k:v a=b x: y= | |
script { | |
run{ | |
p @argv | |
p @options | |
} | |
} | |
~> ruby a.rb | |
['a', 'b', 'c'] | |
{'k' => 'v', 'a' => 'b', 'x' => nil, 'y' => nil} | |
# | |
~> cat a.rb | |
script { | |
run{ | |
p [@mode, @argv, @options] | |
} | |
run(:foo){ | |
p [@mode, @argv, @options] | |
} | |
} | |
~> ruby a.rb a b c k:v | |
[nil, ['a', 'b', 'c'], {'k' => 'v'}] | |
~> ruby a.rb foo a b c k:v | |
['foo', ['a', 'b', 'c'], {'k' => 'v'}] | |
____ | |
run { | |
p [@mode, @argv, @options] | |
} | |
run :foo do | |
p [@mode, @argv, @options] | |
end | |
run :bar do | |
p [@mode, @argv, @options] | |
end | |
} | |
end | |
BEGIN { | |
# | |
require 'json' | |
require 'base64' | |
require 'securerandom' | |
require 'openssl' | |
require 'uri' | |
require 'cgi' | |
require 'yaml' | |
# | |
def script(*args, &block) | |
Script.run!(*args, &block) | |
end | |
# | |
class Script | |
attr_accessor :env | |
attr_accessor :argv | |
attr_accessor :stdout | |
attr_accessor :stdin | |
attr_accessor :stderr | |
attr_accessor :help | |
def run!(env = ENV, argv = ARGV) | |
setup!(env, argv) | |
parse_command_line! | |
set_mode! | |
run_mode! | |
end | |
def setup!(env, argv) | |
@env = env.to_hash.dup | |
@argv = argv.map{|arg| arg.dup} | |
@stdout = $stdout.dup | |
@stdin = $stdin.dup | |
@stderr = $stderr.dup | |
@help = Util.unindent(DEFAULT_HELP) | |
end | |
DEFAULT_HELP = <<-__ | |
NAME | |
#TODO | |
SYNOPSIS | |
#TODO | |
DESCRIPTION | |
#TODO | |
EXAMPLES | |
#TODO | |
__ | |
def parse_command_line! | |
@options = Hash.new | |
re = %r`[:=]` | |
argv = [] | |
head = [] | |
tail = [] | |
%w[ :: ].each do |stop| | |
if((i = @argv.index(stop))) | |
head = @argv.slice(0 ... i) | |
tail = @argv.slice((i + 1) ... @argv.size) | |
@argv = head | |
break | |
end | |
end | |
@argv.each do |arg| | |
if arg =~ re | |
key, val = arg.split(re).map{|kv| kv.strip} | |
@options[key] = val | |
else | |
argv.push(arg) | |
end | |
end | |
argv += tail | |
@argv.replace(argv) | |
end | |
def set_mode! | |
case | |
when respond_to?("run_#{ @argv[0] }!") | |
@mode = @argv.shift | |
else | |
@mode = nil | |
end | |
end | |
def run_mode! | |
if @mode | |
send("run_#{ @mode }!") | |
else | |
run | |
end | |
end | |
def run | |
run_help! | |
end | |
def run_help! | |
STDOUT.puts(@help) | |
end | |
module Util | |
def unindent(arg) | |
string = arg.to_s.dup | |
margin = nil | |
string.each_line do |line| | |
next if line =~ %r/^\s*$/ | |
margin = line[%r/^\s*/] and break | |
end | |
string.gsub!(%r/^#{ margin }/, "") if margin | |
margin ? string : nil | |
end | |
extend Util | |
end | |
class DSL < ::Object | |
def DSL.evaluate(script, &block) | |
dsl = DSL.new(script) | |
dsl.instance_eval(&block) | |
script | |
end | |
def initialize(script) | |
@script = script | |
end | |
def help(help) | |
@script.help = Util.unindent(help) | |
end | |
def run(mode = nil, &block) | |
method_name = | |
if mode | |
"run_#{ mode }!" | |
else | |
"run" | |
end | |
@script.define_singleton_method(method_name, &block) | |
end | |
end | |
def Script.run!(*args, &block) | |
%w[ PIPE INT ].each{|signal| Signal.trap(signal, "EXIT")} | |
Script.new.tap do |script| | |
DSL.evaluate(script, &block) | |
script.run! | |
end | |
end | |
end | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment