simple-cli 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +35 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +42 -0
- data/Rakefile +6 -0
- data/bin/rake +16 -0
- data/lib/simple-cli.rb +1 -0
- data/lib/simple/cli.rb +40 -0
- data/lib/simple/cli/adapter.rb +7 -0
- data/lib/simple/cli/helpers.rb +88 -0
- data/lib/simple/cli/logger.rb +22 -0
- data/lib/simple/cli/runner.rb +160 -0
- data/lib/simple/cli/runner/autocompletion.rb +80 -0
- data/lib/simple/cli/runner/command_help.rb +103 -0
- data/lib/simple/cli/version.rb +5 -0
- data/log/.gitkeep +0 -0
- data/simple-cli.gemspec +36 -0
- data/spec/.keep +0 -0
- data/spec/spec_helper.rb +21 -0
- data/tasks/release.rake +89 -0
- metadata +107 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4e7286cc589a5fcc4c98b61f5cc4967a609458c9
|
4
|
+
data.tar.gz: cd3b2fa51ad21a07ea348a1f5987961f85b92174
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 059f1af84110d1977b95f842032a88c3302147f65bf0ca2c55d6481bb258268c9204a583a14baade03da4db5a44be5f8b6bcfb39bcf5c4b98f462f39197bf07f
|
7
|
+
data.tar.gz: db1efaa0dd5abb8bdb51e12bc2eb59d77ce5b57eca17721b46893f3bacf557c17755137a67b697585c96bc3798d01c1c7b59d7efc6209b8251142890c3f28437
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.3
|
3
|
+
Exclude:
|
4
|
+
- 'spec/**/*'
|
5
|
+
- 'test/**/*'
|
6
|
+
- 'bin/**/*'
|
7
|
+
- 'tasks/release.rake'
|
8
|
+
- '*.gemspec'
|
9
|
+
- 'Gemfile'
|
10
|
+
- 'Rakefile'
|
11
|
+
|
12
|
+
Metrics/LineLength:
|
13
|
+
Max: 115
|
14
|
+
|
15
|
+
Style/SpecialGlobalVars:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
Style/StringLiterals:
|
19
|
+
EnforcedStyle: double_quotes
|
20
|
+
ConsistentQuotesInMultiline: false
|
21
|
+
|
22
|
+
Style/ClassAndModuleChildren:
|
23
|
+
Enabled: false
|
24
|
+
|
25
|
+
Style/ModuleFunction:
|
26
|
+
Enabled: false
|
27
|
+
|
28
|
+
Style/FrozenStringLiteralComment:
|
29
|
+
Enabled: false
|
30
|
+
|
31
|
+
Style/Documentation:
|
32
|
+
Enabled: false
|
33
|
+
|
34
|
+
Style/MutableConstant:
|
35
|
+
Enabled: false
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
simple-cli (0.1.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.3)
|
10
|
+
docile (1.1.5)
|
11
|
+
json (2.1.0)
|
12
|
+
rake (11.3.0)
|
13
|
+
rspec (3.7.0)
|
14
|
+
rspec-core (~> 3.7.0)
|
15
|
+
rspec-expectations (~> 3.7.0)
|
16
|
+
rspec-mocks (~> 3.7.0)
|
17
|
+
rspec-core (3.7.0)
|
18
|
+
rspec-support (~> 3.7.0)
|
19
|
+
rspec-expectations (3.7.0)
|
20
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
21
|
+
rspec-support (~> 3.7.0)
|
22
|
+
rspec-mocks (3.7.0)
|
23
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
24
|
+
rspec-support (~> 3.7.0)
|
25
|
+
rspec-support (3.7.0)
|
26
|
+
simplecov (0.15.1)
|
27
|
+
docile (~> 1.1.0)
|
28
|
+
json (>= 1.8, < 3)
|
29
|
+
simplecov-html (~> 0.10.0)
|
30
|
+
simplecov-html (0.10.2)
|
31
|
+
|
32
|
+
PLATFORMS
|
33
|
+
ruby
|
34
|
+
|
35
|
+
DEPENDENCIES
|
36
|
+
rake (~> 11)
|
37
|
+
rspec (~> 3.7)
|
38
|
+
simple-cli!
|
39
|
+
simplecov (~> 0)
|
40
|
+
|
41
|
+
BUNDLED WITH
|
42
|
+
1.13.6
|
data/Rakefile
ADDED
data/bin/rake
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'rake' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require "pathname"
|
10
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require "rubygems"
|
14
|
+
require "bundler/setup"
|
15
|
+
|
16
|
+
load Gem.bin_path("rake", "rake")
|
data/lib/simple-cli.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "simple/cli"
|
data/lib/simple/cli.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Simple; end
|
2
|
+
module Simple::CLI; end
|
3
|
+
|
4
|
+
require_relative "cli/helpers"
|
5
|
+
require_relative "cli/runner"
|
6
|
+
require_relative "cli/adapter"
|
7
|
+
require_relative "cli/logger"
|
8
|
+
|
9
|
+
require "pp"
|
10
|
+
|
11
|
+
module Simple::CLI
|
12
|
+
DEBUG = true
|
13
|
+
|
14
|
+
def self.included(base)
|
15
|
+
base.extend(::Simple::CLI::Adapter)
|
16
|
+
base.include(::Simple::CLI::Helpers)
|
17
|
+
end
|
18
|
+
|
19
|
+
extend ::Simple::CLI::Logger
|
20
|
+
def logger
|
21
|
+
Simple::CLI::Logger.logger
|
22
|
+
end
|
23
|
+
|
24
|
+
# Simple::CLI.run! is called from Runner.run. It is called with a method
|
25
|
+
# name, which is derived from the command passed in via the command line,
|
26
|
+
# and parsed arguments.
|
27
|
+
#
|
28
|
+
# The default implementation just calls the respective method on self.
|
29
|
+
# Implementations might override this method to provide some before/after
|
30
|
+
# functionality.
|
31
|
+
def run!(command, *args)
|
32
|
+
send(command, *args)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Print help on a subcommand
|
36
|
+
def help(subcommand = nil)
|
37
|
+
# This method is a dummy. It is necessary to provide a stub for the purpose
|
38
|
+
# of documentation - BUT THIS METHOD WILL NEVER BE CALLED.
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require "open3"
|
2
|
+
|
3
|
+
# Helpers are mixed into all CLI modules. They implement the following methods,
|
4
|
+
# mostly to help with integrating external commands:
|
5
|
+
#
|
6
|
+
# - sys
|
7
|
+
# - sys!
|
8
|
+
# - sh!
|
9
|
+
# - die!
|
10
|
+
#
|
11
|
+
module Simple::CLI::Helpers
|
12
|
+
def die!(msg)
|
13
|
+
STDERR.puts msg
|
14
|
+
exit 1
|
15
|
+
end
|
16
|
+
|
17
|
+
def sh!(cmd, *args)
|
18
|
+
command = Command.new(cmd, *args)
|
19
|
+
result = command.sh
|
20
|
+
command.check_success!
|
21
|
+
first_line, more = result.split("\n", 2)
|
22
|
+
if more == ""
|
23
|
+
first_line
|
24
|
+
else
|
25
|
+
result
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def sys(cmd, *args)
|
30
|
+
command = Command.new(cmd, *args)
|
31
|
+
command.run
|
32
|
+
command.success?
|
33
|
+
end
|
34
|
+
|
35
|
+
def sys!(cmd, *args)
|
36
|
+
command = Command.new(cmd, *args)
|
37
|
+
command.run
|
38
|
+
command.check_success!
|
39
|
+
true
|
40
|
+
end
|
41
|
+
|
42
|
+
class Command
|
43
|
+
def initialize(cmd, *args)
|
44
|
+
@cmd = cmd
|
45
|
+
@args = [ cmd ] + args
|
46
|
+
end
|
47
|
+
|
48
|
+
def sh
|
49
|
+
STDERR.puts "> #{self}"
|
50
|
+
stdout_str, @process_status = Open3.capture2(*@args, binmode: true)
|
51
|
+
stdout_str
|
52
|
+
end
|
53
|
+
|
54
|
+
def run
|
55
|
+
STDERR.puts "> #{self}"
|
56
|
+
rv = if @args.length > 1
|
57
|
+
system to_s
|
58
|
+
else
|
59
|
+
system *@args
|
60
|
+
end
|
61
|
+
|
62
|
+
@process_status = $?
|
63
|
+
rv
|
64
|
+
end
|
65
|
+
|
66
|
+
def success?
|
67
|
+
@process_status.success?
|
68
|
+
end
|
69
|
+
|
70
|
+
def check_success!
|
71
|
+
return if @process_status.success?
|
72
|
+
raise "#{@cmd} failed with #{@process_status.exitstatus}: #{self}"
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns the command as a single string, escaping things as necessary.
|
76
|
+
def to_s
|
77
|
+
require "shellwords"
|
78
|
+
|
79
|
+
escaped_args = @args.map do |arg|
|
80
|
+
escaped = Shellwords.escape(arg)
|
81
|
+
next escaped if escaped == arg
|
82
|
+
next escaped if arg.include?("'")
|
83
|
+
"'#{arg}'"
|
84
|
+
end
|
85
|
+
escaped_args.join(" ")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "logger"
|
2
|
+
|
3
|
+
module Simple::CLI::Logger
|
4
|
+
def logger=(logger)
|
5
|
+
@logger = logger
|
6
|
+
end
|
7
|
+
|
8
|
+
def logger
|
9
|
+
@logger ||= build_default_logger
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def build_default_logger
|
15
|
+
logger = Logger.new(STDOUT)
|
16
|
+
logger.formatter = proc do |severity, datetime, progname, msg|
|
17
|
+
"#{severity}: #{msg}\n"
|
18
|
+
end
|
19
|
+
logger.level = Logger::INFO
|
20
|
+
logger
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
class Simple::CLI::Runner
|
2
|
+
end
|
3
|
+
|
4
|
+
require_relative "runner/command_help"
|
5
|
+
require_relative "runner/autocompletion"
|
6
|
+
|
7
|
+
# A Runner object manages running a CLI application module with a set
|
8
|
+
# of string arguments (usually taken from ARGV)
|
9
|
+
class Simple::CLI::Runner
|
10
|
+
include Autocompletion
|
11
|
+
|
12
|
+
def self.run(app, *args)
|
13
|
+
new(app).run(*args)
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(app)
|
17
|
+
@app = app
|
18
|
+
end
|
19
|
+
|
20
|
+
def extract_default_flags!(args)
|
21
|
+
args.reject! do |arg|
|
22
|
+
case arg
|
23
|
+
when "-v", "--verbose" then
|
24
|
+
logger.level = Logger::DEBUG
|
25
|
+
when "-q", "--quiet" then
|
26
|
+
logger.level = Logger::WARN
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def run(*args)
|
32
|
+
extract_default_flags!(args)
|
33
|
+
|
34
|
+
@instance = Object.new.extend(@app)
|
35
|
+
command_name = args.shift || help!
|
36
|
+
command = string_to_command(command_name)
|
37
|
+
|
38
|
+
if command == :help
|
39
|
+
do_help!(*args)
|
40
|
+
elsif command == :autocomplete
|
41
|
+
autocomplete *args
|
42
|
+
elsif command == :autocomplete_bash
|
43
|
+
autocomplete_bash *args
|
44
|
+
elsif commands.include?(command)
|
45
|
+
@instance.run! command, *args_with_options(args)
|
46
|
+
else
|
47
|
+
help!
|
48
|
+
end
|
49
|
+
rescue StandardError => e
|
50
|
+
on_exception(e)
|
51
|
+
end
|
52
|
+
|
53
|
+
def do_help!(subcommand = nil)
|
54
|
+
if !subcommand
|
55
|
+
help!
|
56
|
+
else
|
57
|
+
help_subcommand!(subcommand)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def help_subcommand!(subcommand)
|
62
|
+
edoc = CommandHelp.new(@app, subcommand)
|
63
|
+
|
64
|
+
puts <<~MSG
|
65
|
+
#{help_for_command(subcommand)}
|
66
|
+
|
67
|
+
#{edoc.full}
|
68
|
+
MSG
|
69
|
+
end
|
70
|
+
|
71
|
+
def logger
|
72
|
+
Simple::CLI.logger
|
73
|
+
end
|
74
|
+
|
75
|
+
def on_exception(e)
|
76
|
+
raise(e) if Simple::CLI::DEBUG
|
77
|
+
|
78
|
+
case e
|
79
|
+
when ArgumentError
|
80
|
+
logger.error "#{e}\n\n"
|
81
|
+
help!
|
82
|
+
else
|
83
|
+
msg = e.to_s
|
84
|
+
msg += " (#{e.class.name})" unless $!.class.name == "RuntimeError"
|
85
|
+
logger.error msg
|
86
|
+
exit 2
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def args_with_options(args)
|
91
|
+
r = []
|
92
|
+
options = {}
|
93
|
+
while arg = args.shift
|
94
|
+
case arg
|
95
|
+
when /^--(.*)=(.*)/ then options[$1.to_sym] = $2
|
96
|
+
when /^--no-(.*)/ then options[$1.to_sym] = false
|
97
|
+
when /^--(.*)/ then options[$1.to_sym] = true
|
98
|
+
else r << arg
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
r << options unless options.empty?
|
103
|
+
r
|
104
|
+
end
|
105
|
+
|
106
|
+
def command_to_string(sym)
|
107
|
+
sym.to_s.gsub("_", ":")
|
108
|
+
end
|
109
|
+
|
110
|
+
def string_to_command(s)
|
111
|
+
s.gsub(":", "_").to_sym
|
112
|
+
end
|
113
|
+
|
114
|
+
def commands
|
115
|
+
@app.public_instance_methods(false).grep(/^[_a-zA-Z0-9]+$/) + [:help]
|
116
|
+
end
|
117
|
+
|
118
|
+
def help_for_command(sym)
|
119
|
+
if sym == "autocomplete"
|
120
|
+
autocomplete_help
|
121
|
+
return
|
122
|
+
end
|
123
|
+
|
124
|
+
command_name = command_to_string(sym)
|
125
|
+
CommandHelp.new(@app, sym).interface(binary_name, command_name)
|
126
|
+
end
|
127
|
+
|
128
|
+
def binary_name
|
129
|
+
$0.gsub(/.*\//, "")
|
130
|
+
end
|
131
|
+
|
132
|
+
def help!
|
133
|
+
STDERR.puts "Usage:\n\n"
|
134
|
+
command_helps = commands.inject({}) do |hsh, sym|
|
135
|
+
hsh.update sym => help_for_command(sym)
|
136
|
+
end
|
137
|
+
max_command_helps_length = command_helps.values.map(&:length).max
|
138
|
+
|
139
|
+
commands.sort.each do |sym|
|
140
|
+
command_help = command_helps.fetch(sym)
|
141
|
+
command_help = "%-#{max_command_helps_length}s" % command_help
|
142
|
+
edoc = CommandHelp.new(@app, sym)
|
143
|
+
head = "# #{edoc.head}" if edoc.head
|
144
|
+
STDERR.puts " #{command_help} #{ head }"
|
145
|
+
end
|
146
|
+
|
147
|
+
STDERR.puts "\n"
|
148
|
+
|
149
|
+
STDERR.puts <<~DOC
|
150
|
+
Default options include:
|
151
|
+
|
152
|
+
#{binary_name} [ --verbose | -v ] # run on DEBUG log level
|
153
|
+
#{binary_name} [ --quiet | -q ] # run on WARN log level
|
154
|
+
#{binary_name} help autocomplete # print information on autocompletion.
|
155
|
+
|
156
|
+
DOC
|
157
|
+
|
158
|
+
exit 1
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Simple::CLI::Runner::Autocompletion
|
2
|
+
CommandHelp = Simple::CLI::Runner::CommandHelp
|
3
|
+
|
4
|
+
def autocomplete(subcommand=nil, cur=nil)
|
5
|
+
completions = if !cur
|
6
|
+
autocomplete_subcommands(subcommand)
|
7
|
+
else
|
8
|
+
autocomplete_subcommand_options(subcommand, cur)
|
9
|
+
end
|
10
|
+
puts completions.join("\n")
|
11
|
+
end
|
12
|
+
|
13
|
+
def filter_completions(completions, prefix:)
|
14
|
+
completions.select do |completion|
|
15
|
+
!prefix || completion.start_with?(prefix)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def autocomplete_subcommands(cur)
|
20
|
+
completions = filter_completions commands.map(&:to_s), prefix: cur
|
21
|
+
if completions == [ cur ]
|
22
|
+
autocomplete_subcommand_options(cur, nil)
|
23
|
+
else
|
24
|
+
completions
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def autocomplete_subcommand_options(subcommand, cur)
|
29
|
+
completions = if subcommand == "help"
|
30
|
+
commands.map(&:to_s) + [ "autocomplete" ]
|
31
|
+
else
|
32
|
+
CommandHelp.option_names(@app, subcommand)
|
33
|
+
end
|
34
|
+
|
35
|
+
filter_completions completions, prefix: cur
|
36
|
+
end
|
37
|
+
|
38
|
+
def autocomplete_help
|
39
|
+
STDERR.puts <<~DOC
|
40
|
+
#{binary_name} supports autocompletion. To enable autocompletion please run
|
41
|
+
|
42
|
+
eval "$(#{$0} autocomplete:bash)"
|
43
|
+
DOC
|
44
|
+
|
45
|
+
exit 1
|
46
|
+
end
|
47
|
+
|
48
|
+
def autocomplete_bash
|
49
|
+
puts AUTOCOMPLETE_SHELL_CODE.gsub(/{{BINARY}}/, binary_name)
|
50
|
+
end
|
51
|
+
|
52
|
+
# The shell function function is executed in the current shell environment.
|
53
|
+
# When it is executed,
|
54
|
+
#
|
55
|
+
# - $1 is the name of the command whose arguments are being completed,
|
56
|
+
# - $2 is the word being completed, and
|
57
|
+
# - $3 is the word preceding the word being completed
|
58
|
+
#
|
59
|
+
# When it finishes, the possible completions are retrieved from the value of the COMPREPLY array variable.
|
60
|
+
#
|
61
|
+
# see https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion-Builtins.html
|
62
|
+
#
|
63
|
+
AUTOCOMPLETE_SHELL_CODE = <<~BASH
|
64
|
+
_{{BINARY}}()
|
65
|
+
{
|
66
|
+
local cmd=$1
|
67
|
+
local cur=$2
|
68
|
+
|
69
|
+
if [[ $COMP_CWORD == 1 ]]; then
|
70
|
+
COMPREPLY=( $("$cmd" autocomplete "$cur" ))
|
71
|
+
else
|
72
|
+
local subcommand=${COMP_WORDS[1]}
|
73
|
+
COMPREPLY=( $("$cmd" autocomplete "$subcommand" "$cur" ))
|
74
|
+
fi
|
75
|
+
|
76
|
+
return 0
|
77
|
+
}
|
78
|
+
complete -F _{{BINARY}} {{BINARY}}
|
79
|
+
BASH
|
80
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
class Simple::CLI::Runner::CommandHelp
|
2
|
+
def self.option_names(app, subcommand)
|
3
|
+
new(app, subcommand).option_names
|
4
|
+
rescue NameError
|
5
|
+
[]
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(mod, method)
|
9
|
+
@mod, @method = mod, method
|
10
|
+
end
|
11
|
+
|
12
|
+
# First line of the help as read from the method comments.
|
13
|
+
def head
|
14
|
+
comments.first
|
15
|
+
end
|
16
|
+
|
17
|
+
# Full help as read from the method comments
|
18
|
+
def full
|
19
|
+
comments.join("\n") if comments.first
|
20
|
+
end
|
21
|
+
|
22
|
+
def option_names
|
23
|
+
method = @mod.instance_method(@method)
|
24
|
+
|
25
|
+
method.parameters.map do |mode, name|
|
26
|
+
case mode
|
27
|
+
when :key then name
|
28
|
+
when :keyreq then name
|
29
|
+
end
|
30
|
+
end.compact.map do |name|
|
31
|
+
[ "--#{name}", "--#{name}=" ]
|
32
|
+
end.flatten.uniq
|
33
|
+
end
|
34
|
+
|
35
|
+
# A help string constructed from the commands method signature.
|
36
|
+
def interface(binary_name, command_name)
|
37
|
+
method = @mod.instance_method(@method)
|
38
|
+
|
39
|
+
options = []
|
40
|
+
args = []
|
41
|
+
|
42
|
+
method.parameters.each do |mode, name|
|
43
|
+
case mode
|
44
|
+
when :req then args << "<#{name}>"
|
45
|
+
when :key then options << "[ --#{name}[=<#{name}>] ]"
|
46
|
+
when :keyreq then options << "--#{name}[=<#{name}>]"
|
47
|
+
when :opt then args << "[ <#{name}> ]"
|
48
|
+
when :rest then args << "[ <#{name}> .. ]"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
help = "#{binary_name} #{command_name}"
|
53
|
+
help << " #{options.join(' ')}" unless options.empty?
|
54
|
+
help << " #{args.join(' ')}" unless args.empty?
|
55
|
+
help
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def comments
|
61
|
+
@comments ||= begin
|
62
|
+
file, line = @mod.instance_method(@method).source_location
|
63
|
+
extract_comments(from: parsed_source(file), before_line: line)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# reads the source \a file and turns each non-comment into :code and each comment
|
68
|
+
# into a string without the leading comment markup.
|
69
|
+
def parsed_source(file)
|
70
|
+
File.readlines(file).map do |line|
|
71
|
+
case line
|
72
|
+
when /^\s*# ?(.*)$/ then $1
|
73
|
+
when /^\s*end/ then :end
|
74
|
+
else nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def extract_comments(from:, before_line:)
|
80
|
+
parsed_source = from
|
81
|
+
|
82
|
+
# go down from before_line until we see a line which is either a comment
|
83
|
+
# or an :end. Note that the line at before_line-1 should be the first
|
84
|
+
# line of the method definition in question.
|
85
|
+
last_line = before_line-1
|
86
|
+
while last_line >= 0 && !parsed_source[last_line] do
|
87
|
+
last_line -= 1
|
88
|
+
end
|
89
|
+
|
90
|
+
first_line = last_line
|
91
|
+
while first_line >= 0 && parsed_source[first_line] do
|
92
|
+
first_line -= 1
|
93
|
+
end
|
94
|
+
first_line += 1
|
95
|
+
|
96
|
+
comments = parsed_source[first_line .. last_line]
|
97
|
+
if comments.include?(:end)
|
98
|
+
[]
|
99
|
+
else
|
100
|
+
parsed_source[first_line .. last_line]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/log/.gitkeep
ADDED
File without changes
|
data/simple-cli.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# This file is part of the sinatra-sse ruby gem.
|
2
|
+
#
|
3
|
+
# Copyright (c) 2016, 2017 @radiospiel, mediapeers Gem
|
4
|
+
# Distributed under the terms of the modified BSD license, see LICENSE.BSD
|
5
|
+
|
6
|
+
lib = File.expand_path('../lib', __FILE__)
|
7
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
8
|
+
require 'simple/cli/version'
|
9
|
+
|
10
|
+
Gem::Specification.new do |gem|
|
11
|
+
gem.name = "simple-cli"
|
12
|
+
gem.version = Simple::CLI::VERSION
|
13
|
+
|
14
|
+
gem.authors = [ "radiospiel", "mediapeers GmbH" ]
|
15
|
+
gem.email = "eno@radiospiel.org"
|
16
|
+
gem.homepage = "http://github.com/radiospiel/simple-cli"
|
17
|
+
gem.summary = "Simple CLI builder for ruby"
|
18
|
+
|
19
|
+
gem.description = "Simple CLI builder"
|
20
|
+
|
21
|
+
gem.files = `git ls-files`.split($/)
|
22
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
23
|
+
gem.require_paths = %w(lib)
|
24
|
+
|
25
|
+
# executables are used for development purposes only
|
26
|
+
gem.executables = []
|
27
|
+
|
28
|
+
gem.required_ruby_version = '~> 2.3'
|
29
|
+
|
30
|
+
# optional gems (required by some of the parts)
|
31
|
+
|
32
|
+
# development gems
|
33
|
+
gem.add_development_dependency 'rake', '~> 11'
|
34
|
+
gem.add_development_dependency 'rspec', '~> 3.7'
|
35
|
+
gem.add_development_dependency 'simplecov', '~> 0'
|
36
|
+
end
|
data/spec/.keep
ADDED
File without changes
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
%w(auth authentication authorization).each do |library_name|
|
2
|
+
path = File.expand_path("../../#{library_name}/lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(path) unless $LOAD_PATH.include?(path)
|
4
|
+
end
|
5
|
+
|
6
|
+
ENV["RACK_ENV"] = "test"
|
7
|
+
ENV["RAILS_ENV"] = "test"
|
8
|
+
|
9
|
+
require "rspec"
|
10
|
+
|
11
|
+
Dir.glob("./spec/support/**/*.rb").sort.each { |path| load path }
|
12
|
+
|
13
|
+
require "simple/cli"
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
config.run_all_when_everything_filtered = true
|
17
|
+
config.filter_run focus: (ENV["CI"] != "true")
|
18
|
+
config.expect_with(:rspec) { |c| c.syntax = :expect }
|
19
|
+
config.include FactoryGirl::Syntax::Methods
|
20
|
+
config.order = "random"
|
21
|
+
end
|
data/tasks/release.rake
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.setup
|
3
|
+
|
4
|
+
GEM_ROOT = File.expand_path('../../', __FILE__)
|
5
|
+
GEM_SPEC = "simple-cli.gemspec"
|
6
|
+
|
7
|
+
require 'simple/cli/version'
|
8
|
+
VERSION_FILE_PATH = 'lib/simple/cli/version.rb'
|
9
|
+
|
10
|
+
class VersionNumberTracker
|
11
|
+
class << self
|
12
|
+
def update_version_file(old_version_number, new_version_number)
|
13
|
+
old_line = "VERSION = \"#{old_version_number}\""
|
14
|
+
new_line = "VERSION = \"#{new_version_number}\""
|
15
|
+
update = File.read(VERSION_FILE_PATH).gsub(old_line, new_line)
|
16
|
+
File.open(VERSION_FILE_PATH, 'w') { |file| file.puts update }
|
17
|
+
new_version_number
|
18
|
+
end
|
19
|
+
|
20
|
+
def auto_version_bump
|
21
|
+
old_version_number = Simple::CLI::VERSION
|
22
|
+
old = old_version_number.split('.')
|
23
|
+
current = old[0..-2] << old[-1].next
|
24
|
+
new_version_number = current.join('.')
|
25
|
+
|
26
|
+
update_version_file(old_version_number, new_version_number)
|
27
|
+
end
|
28
|
+
|
29
|
+
def manual_version_bump
|
30
|
+
update_version_file(Simple::CLI::VERSION, ENV['VERSION'])
|
31
|
+
end
|
32
|
+
|
33
|
+
def update_version_number
|
34
|
+
@version = ENV['VERSION'] ? manual_version_bump : auto_version_bump
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_reader :version
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
namespace :release do
|
42
|
+
task :version do
|
43
|
+
VersionNumberTracker.update_version_number
|
44
|
+
end
|
45
|
+
|
46
|
+
task :build do
|
47
|
+
Dir.chdir(GEM_ROOT) do
|
48
|
+
sh("gem build #{GEM_SPEC}")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
desc "Commit changes"
|
53
|
+
task :commit do
|
54
|
+
Dir.chdir(GEM_ROOT) do
|
55
|
+
version = VersionNumberTracker.version
|
56
|
+
sh("git add #{VERSION_FILE_PATH}")
|
57
|
+
sh("git commit -m \"bump to v#{version}\"")
|
58
|
+
sh("git tag -a v#{version} -m \"Tag\"")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
desc "Push code and tags"
|
63
|
+
task :push do
|
64
|
+
sh('git push origin master')
|
65
|
+
sh('git push --tags')
|
66
|
+
end
|
67
|
+
|
68
|
+
desc "Cleanup"
|
69
|
+
task :clean do
|
70
|
+
Dir.glob(File.join(GEM_ROOT, '*.gem')).each { |f| FileUtils.rm_rf(f) }
|
71
|
+
end
|
72
|
+
|
73
|
+
desc "Push Gem to gemfury"
|
74
|
+
task :push_to_rubygems do
|
75
|
+
Dir.chdir(GEM_ROOT) { sh("gem push #{Dir.glob('*.gem').first}") }
|
76
|
+
end
|
77
|
+
|
78
|
+
task default: [
|
79
|
+
'version',
|
80
|
+
'clean',
|
81
|
+
'build',
|
82
|
+
'commit',
|
83
|
+
'push',
|
84
|
+
'push_to_rubygems'
|
85
|
+
]
|
86
|
+
end
|
87
|
+
|
88
|
+
desc "Clean, build, commit and push"
|
89
|
+
task release: 'release:default'
|
metadata
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simple-cli
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- radiospiel
|
8
|
+
- mediapeers GmbH
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2018-02-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '11'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '11'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rspec
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '3.7'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '3.7'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: simplecov
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
description: Simple CLI builder
|
57
|
+
email: eno@radiospiel.org
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- ".rubocop.yml"
|
64
|
+
- Gemfile
|
65
|
+
- Gemfile.lock
|
66
|
+
- Rakefile
|
67
|
+
- bin/rake
|
68
|
+
- lib/simple-cli.rb
|
69
|
+
- lib/simple/cli.rb
|
70
|
+
- lib/simple/cli/adapter.rb
|
71
|
+
- lib/simple/cli/helpers.rb
|
72
|
+
- lib/simple/cli/logger.rb
|
73
|
+
- lib/simple/cli/runner.rb
|
74
|
+
- lib/simple/cli/runner/autocompletion.rb
|
75
|
+
- lib/simple/cli/runner/command_help.rb
|
76
|
+
- lib/simple/cli/version.rb
|
77
|
+
- log/.gitkeep
|
78
|
+
- simple-cli.gemspec
|
79
|
+
- spec/.keep
|
80
|
+
- spec/spec_helper.rb
|
81
|
+
- tasks/release.rake
|
82
|
+
homepage: http://github.com/radiospiel/simple-cli
|
83
|
+
licenses: []
|
84
|
+
metadata: {}
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - "~>"
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '2.3'
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
requirements: []
|
100
|
+
rubyforge_project:
|
101
|
+
rubygems_version: 2.5.1
|
102
|
+
signing_key:
|
103
|
+
specification_version: 4
|
104
|
+
summary: Simple CLI builder for ruby
|
105
|
+
test_files:
|
106
|
+
- spec/.keep
|
107
|
+
- spec/spec_helper.rb
|