simple-cli 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|