ergane 0.0.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/CHANGELOG.md +5 -0
- data/LICENSE +21 -0
- data/README.md +18 -0
- data/app/commands/ergane/console.rb +22 -0
- data/bin/ergane +24 -0
- data/lib/core_ext/option_parser.rb +15 -0
- data/lib/ergane/command_definition.rb +176 -0
- data/lib/ergane/debug.rb +75 -0
- data/lib/ergane/helpers/hashall.rb +8 -0
- data/lib/ergane/switch_definition.rb +49 -0
- data/lib/ergane/tool.rb +116 -0
- data/lib/ergane/util.rb +77 -0
- data/lib/ergane/version.rb +3 -0
- data/lib/ergane.rb +85 -0
- metadata +201 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6f27bb356ea374b87bf0a12ad73bc945e518dee66aa18797b5f9b06a197de896
|
4
|
+
data.tar.gz: 2de58095dd7014c321d13fcac71f9d16019db72cd6feb0896bafea5f9ae19d6e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3a3431e9dd63b745dc3765a83c98c0d3ed3d60837373d12b69371f1df05e5718be8b2fd5007808e4383ba8d77c0fd6997ba7dc867e317b5b598fc80921429161
|
7
|
+
data.tar.gz: fa7972b5a4019cff455ca2f0631181cb3ce49192a56ee01eb549c22827fbc8d41b77cc86d089178d04e2b63474626faab18dd3acbe45be12aedaec5a37254955
|
data/CHANGELOG.md
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2023 Dale Stevens
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# Ergane
|
2
|
+
**The patron goddess of craftsmen and artisans**
|
3
|
+
|
4
|
+
Library for creating lightweight, yet powerful CLI tools in Ruby.
|
5
|
+
Emphasis and priority on load speed and flexibility.
|
6
|
+
|
7
|
+
## Requirements
|
8
|
+
Ruby 2.7+
|
9
|
+
|
10
|
+
May work with older versions.
|
11
|
+
|
12
|
+
## Contributing
|
13
|
+
|
14
|
+
1. Fork it ( https://github.com/TwilightCoders/ergane/fork )
|
15
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
16
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
17
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
18
|
+
5. Create a new Pull Request
|
@@ -0,0 +1,22 @@
|
|
1
|
+
define :console do
|
2
|
+
|
3
|
+
description "Jump into an interactive REPL console"
|
4
|
+
|
5
|
+
requirements inherit: true do
|
6
|
+
require 'colorize'
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
switches(inherit: false) do
|
11
|
+
# option(:help, description: "display help")
|
12
|
+
end
|
13
|
+
|
14
|
+
run do
|
15
|
+
Pry.config.prompt_name = "#{Process.argv0.split('/').last.capitalize} #{label} ".light_blue
|
16
|
+
Pry.start
|
17
|
+
end
|
18
|
+
|
19
|
+
def support_method
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
data/bin/ergane
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
profile = nil
|
4
|
+
if ARGV.include?('--profile')
|
5
|
+
require 'ruby-prof'
|
6
|
+
profile = RubyProf::Profile.new
|
7
|
+
profile.exclude_common_methods!
|
8
|
+
profile.start
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'bundler/setup'
|
12
|
+
require 'ergane'
|
13
|
+
require 'ergane/tool'
|
14
|
+
|
15
|
+
tool = Ergane::Tool.new :ergane, 'lib/ergane/commands/**/*.rb', 'app/**/commands/**/*.rb'
|
16
|
+
tool.run
|
17
|
+
|
18
|
+
if profile
|
19
|
+
result = profile.stop
|
20
|
+
File.open "profile-graph.html", 'w+' do |file|
|
21
|
+
# RubyProf::GraphHtmlPrinter.new(results).print(file)
|
22
|
+
RubyProf::CallStackPrinter.new(result).print(file)
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class OptionParser
|
2
|
+
|
3
|
+
# Like order!, but leave any unrecognized --switches alone
|
4
|
+
def order_recognized!(args)
|
5
|
+
extra_opts = []
|
6
|
+
begin
|
7
|
+
order!(args) { |a| extra_opts << a }
|
8
|
+
rescue OptionParser::InvalidOption => e
|
9
|
+
extra_opts << e.args[0]
|
10
|
+
retry
|
11
|
+
end
|
12
|
+
args[0, 0] = extra_opts
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
# IDEA: "switches" are just "inline commands".
|
2
|
+
# They share a similar structure to a command.
|
3
|
+
# They have a label, description, and optionally a "run block"
|
4
|
+
|
5
|
+
require 'pry'
|
6
|
+
require 'helpers/hashall'
|
7
|
+
require 'switch_definition'
|
8
|
+
|
9
|
+
module Ergane
|
10
|
+
|
11
|
+
class CommandDefinition < Hashall
|
12
|
+
|
13
|
+
attr_reader :label
|
14
|
+
attr_reader :chain
|
15
|
+
attr_reader :description
|
16
|
+
attr_reader :switch_definitions
|
17
|
+
attr_reader :switch_parser
|
18
|
+
attr_reader :requirements_block
|
19
|
+
attr_reader :run_block
|
20
|
+
|
21
|
+
def pretty_print(q)
|
22
|
+
q.text "#{self.class.name} (#{label})"
|
23
|
+
q.text "\n\tCommands:"
|
24
|
+
q.group(1, " {", "}") do
|
25
|
+
self.each_pair do |key, value|
|
26
|
+
q.breakable
|
27
|
+
q.text "#{key}: #{value}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def run(*args)
|
33
|
+
if run_block
|
34
|
+
instance_exec(&requirements_block) if requirements_block
|
35
|
+
instance_exec(*args, &run_block) if run_block
|
36
|
+
else
|
37
|
+
puts "show help for this command"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.define(label, chain: [], &block)
|
42
|
+
new(label, *chain).tap do |c|
|
43
|
+
c.define(chain: chain, &block)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def define(chain: [], &block)
|
48
|
+
dsl_parse(&block)
|
49
|
+
end
|
50
|
+
|
51
|
+
def commands
|
52
|
+
self.to_h
|
53
|
+
end
|
54
|
+
|
55
|
+
def arguments
|
56
|
+
if commands.any?
|
57
|
+
commands.keys
|
58
|
+
else
|
59
|
+
instance_method(:run).parameters.map { |arg| arg[1] }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def default_switches
|
64
|
+
@default_switches ||= switch_definitions.inject({}) do |collector, (label, switch)|
|
65
|
+
collector[label] = switch.default
|
66
|
+
collector
|
67
|
+
end
|
68
|
+
@default_switches.dup
|
69
|
+
end
|
70
|
+
|
71
|
+
def parse_args(args, path = [])
|
72
|
+
if args.any? && args.first.match(/\A(\w+)\z/) && word = args.first.to_sym
|
73
|
+
case command = self[word]
|
74
|
+
when CommandDefinition
|
75
|
+
path << args.shift
|
76
|
+
command.parse_args(args, path)
|
77
|
+
else
|
78
|
+
puts "no such subcommand #{word} for #{self}"
|
79
|
+
end
|
80
|
+
else
|
81
|
+
# These are args for this command now.
|
82
|
+
Ergane.logger.debug "Now, process these args #{args} for #{label}"
|
83
|
+
switch_parser.order_recognized!(args)
|
84
|
+
[self, args || []]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
protected
|
89
|
+
|
90
|
+
def dsl_parse(&block)
|
91
|
+
dsl = DSL.new.tap do |dsl|
|
92
|
+
dsl.instance_eval(&block)
|
93
|
+
end
|
94
|
+
@switch_definitions.merge!(dsl.config.delete(:switch_definitions))
|
95
|
+
dsl.config.each do |k, v|
|
96
|
+
begin
|
97
|
+
instance_variable_set("@#{k}", v)
|
98
|
+
rescue
|
99
|
+
binding.pry
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
switches = default_switches
|
104
|
+
switch_definitions.values.each do |o|
|
105
|
+
o.attach(@switch_parser, self) do |value|
|
106
|
+
value = case o.kind
|
107
|
+
when TrueClass
|
108
|
+
true
|
109
|
+
when FalseClass
|
110
|
+
false
|
111
|
+
else
|
112
|
+
value.nil? ? true : value
|
113
|
+
end
|
114
|
+
Ergane.logger.debug "Setting switches[#{o.label}] = #{value.inspect}"
|
115
|
+
switches[o.label] = value
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def initialize(label, *chain)
|
123
|
+
super()
|
124
|
+
@chain = chain
|
125
|
+
@label = label
|
126
|
+
@switch_definitions = {}
|
127
|
+
@requirement_options = {
|
128
|
+
inherit: true
|
129
|
+
}
|
130
|
+
@run_options = {
|
131
|
+
inherit: true
|
132
|
+
}
|
133
|
+
@switch_parser = OptionParser.new
|
134
|
+
end
|
135
|
+
|
136
|
+
class DSL
|
137
|
+
attr_reader :config
|
138
|
+
|
139
|
+
def initialize
|
140
|
+
@config = {
|
141
|
+
switch_definitions: {},
|
142
|
+
run_block: nil,
|
143
|
+
requirements_block: nil
|
144
|
+
}
|
145
|
+
end
|
146
|
+
|
147
|
+
def method_missing(k, v)
|
148
|
+
@config[k] = v
|
149
|
+
end
|
150
|
+
|
151
|
+
def switches(inherit: true, drop: [], &block)
|
152
|
+
def option(label, short: nil, kind: nil, description: nil, default: nil, &block)
|
153
|
+
label, argument = case label
|
154
|
+
when Hash
|
155
|
+
[label.keys.first, "=#{label.values.first}"]
|
156
|
+
else
|
157
|
+
[label, nil]
|
158
|
+
end
|
159
|
+
warn "Warning! #{label.inspect} option is being redefined.".red if @config[:switch_definitions][label]
|
160
|
+
@config[:switch_definitions][label] = SwitchDefinition.new(label, argument: argument, short: short, kind: kind, description: description, default: default, &block)
|
161
|
+
end
|
162
|
+
block.call if block_given?
|
163
|
+
@config[:switch_definitions]
|
164
|
+
end
|
165
|
+
|
166
|
+
def requirements(options, &block)
|
167
|
+
@config[:requirements_block] = block
|
168
|
+
end
|
169
|
+
|
170
|
+
def run(&block)
|
171
|
+
# puts "setting run_block"
|
172
|
+
@config[:run_block] = block
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
data/lib/ergane/debug.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
module Ergane
|
2
|
+
module Debug
|
3
|
+
|
4
|
+
class << self
|
5
|
+
|
6
|
+
@nl = false
|
7
|
+
|
8
|
+
def enable_break!
|
9
|
+
class << self
|
10
|
+
define_method(:break, &binding.method(:pry))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def suppress
|
15
|
+
original_stdout, original_stderr = $stdout.clone, $stderr.clone
|
16
|
+
$stderr.reopen File.new('/dev/null', 'w')
|
17
|
+
$stdout.reopen File.new('/dev/null', 'w')
|
18
|
+
yield
|
19
|
+
ensure
|
20
|
+
$stdout.reopen original_stdout
|
21
|
+
$stderr.reopen original_stderr
|
22
|
+
end
|
23
|
+
|
24
|
+
def break
|
25
|
+
puts "Breakpoints Not Enabled"
|
26
|
+
end
|
27
|
+
|
28
|
+
def format_print(m)
|
29
|
+
case m
|
30
|
+
when String
|
31
|
+
format_print(m.split("\n"))
|
32
|
+
when Array
|
33
|
+
m.join("\n\t")
|
34
|
+
else
|
35
|
+
m
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def puts(m=nil, prefix: false, &block)
|
40
|
+
prefix ||= $debug ? 'DEBUG' : false
|
41
|
+
if (prefix)
|
42
|
+
if @nl || m.blank?
|
43
|
+
$stdout.puts format_print(m)
|
44
|
+
else
|
45
|
+
$stdout.print "[#{prefix.upcase.light_magenta}] " unless prefix.blank?
|
46
|
+
$stdout.puts format_print(m)
|
47
|
+
end
|
48
|
+
else
|
49
|
+
yield if block_given?
|
50
|
+
end
|
51
|
+
@nl = false
|
52
|
+
end
|
53
|
+
|
54
|
+
def print(m=nil, prefix: false, &block)
|
55
|
+
@nl = true
|
56
|
+
prefix ||= $debug ? 'DEBUG' : false
|
57
|
+
if (prefix)
|
58
|
+
if (m.blank?)
|
59
|
+
$stdout.print format_print(m)
|
60
|
+
else
|
61
|
+
$stdout.print "[#{prefix.upcase.light_magenta}] " unless prefix.blank?
|
62
|
+
$stdout.print format_print(m)
|
63
|
+
end
|
64
|
+
else
|
65
|
+
yield if block_given?
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
enable_break! if $debug
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Ergane
|
2
|
+
class NamedBlock
|
3
|
+
def self.new(*args, &block)
|
4
|
+
Class.new do
|
5
|
+
args.each do |arg|
|
6
|
+
attr_reader arg.to_sym
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(label, args)
|
10
|
+
@label = label.to_sym
|
11
|
+
args.each do |arg, value|
|
12
|
+
instance_variable_set("@#{args}", value)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
private :initialize
|
18
|
+
end
|
19
|
+
|
20
|
+
class SwitchDefinition < NamedBlock.new(:short, :kind, :argument, :description, :default)
|
21
|
+
attr_reader :label
|
22
|
+
attr_reader :short
|
23
|
+
attr_reader :kind
|
24
|
+
attr_reader :argument
|
25
|
+
attr_reader :description
|
26
|
+
attr_reader :default
|
27
|
+
attr_reader :run_block
|
28
|
+
|
29
|
+
def initialize(label, short: nil, argument: nil, kind: nil, description: nil, default: nil, &block)
|
30
|
+
@label, @short, @argument, @kind, @description, @default = label.to_sym, short, argument, kind, description, default
|
31
|
+
@run_block = block if block_given?
|
32
|
+
end
|
33
|
+
|
34
|
+
def attach(option_parser, command, &block)
|
35
|
+
flag_arg = argument ? "=#{argument}" : ""
|
36
|
+
args = []
|
37
|
+
args << "-#{short}#{flag_arg}" if short
|
38
|
+
args << "--#{label.to_s.gsub(/_/, '-')}#{flag_arg}"
|
39
|
+
args << kind if kind
|
40
|
+
args << description if description
|
41
|
+
|
42
|
+
option_parser.on(*args, Proc.new { |value|
|
43
|
+
command.instance_exec(value, &block) if block_given?
|
44
|
+
command.instance_exec(value, &run_block) if run_block
|
45
|
+
})
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
data/lib/ergane/tool.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
module Ergane
|
2
|
+
class Tool < CommandDefinition
|
3
|
+
attr_reader :title #capitalized :label
|
4
|
+
attr_reader :version
|
5
|
+
|
6
|
+
def initialize(label, *paths)
|
7
|
+
super(label)
|
8
|
+
|
9
|
+
@title = label.capitalize
|
10
|
+
@version = VERSION
|
11
|
+
|
12
|
+
# Dogfood-ing
|
13
|
+
define do
|
14
|
+
description "Basic CLI Tool"
|
15
|
+
switches do
|
16
|
+
switch :help, default: false, kind: TrueClass, description: "Display this help block" do
|
17
|
+
raise Help
|
18
|
+
end
|
19
|
+
switch :version, default: false, kind: TrueClass, description: "Display the version" do
|
20
|
+
# TODO: Push ARGV into a variable at the command level that can be manipulated by flags/switches
|
21
|
+
# NOTE: This would allow a --version to morph into a version command and we could push all this logic to that.
|
22
|
+
# Additional logic for --version would be -1 or --short etc.
|
23
|
+
puts "#{@title} Version: #{Ergane::VERSION}"
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
# switch verbose: FalseClass, short: :v, "Turn on verbose logging"
|
27
|
+
end
|
28
|
+
|
29
|
+
run do
|
30
|
+
begin
|
31
|
+
command, args = self, []
|
32
|
+
Process.setproctitle(label.to_s)
|
33
|
+
command, args = self.parse_args(ARGV.dup)
|
34
|
+
|
35
|
+
command.run(*args)
|
36
|
+
puts "Finished running #{label}"
|
37
|
+
rescue Interrupt
|
38
|
+
puts "\nOkay. Aborting."
|
39
|
+
rescue RuntimeError
|
40
|
+
puts "RuntimeError"
|
41
|
+
binding.pry
|
42
|
+
rescue Help
|
43
|
+
puts help(command, args)
|
44
|
+
ensure
|
45
|
+
system "printf '\033]0;\007'"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Pry.config.prompt_name = "#{title} ".light_blue
|
51
|
+
|
52
|
+
load_commands(paths)
|
53
|
+
end
|
54
|
+
|
55
|
+
def help(command, args=[])
|
56
|
+
missing_args = command.arguments.product([false]).to_h.merge(args.product([true]).to_h).map do |arg, is_missing|
|
57
|
+
a = arg.to_s.light_black.underline.tap do |b|
|
58
|
+
b.blink if is_missing
|
59
|
+
end
|
60
|
+
end.join(' ')
|
61
|
+
|
62
|
+
command.switch_parser.banner = [].tap do |text|
|
63
|
+
text << "About: #{description}"
|
64
|
+
text << "Version: #{version.to_s.light_blue}"
|
65
|
+
text << "Usage: #{([label] + chain).join(' ').light_red} #{'[options]'.light_cyan} "
|
66
|
+
if commands.any?
|
67
|
+
text.last << "[subcommand]".light_black.underline
|
68
|
+
text << (" ┌" + ("─" * (text.last.uncolorize.length - 12)) + "┘").light_black
|
69
|
+
commands.each do |key, command|
|
70
|
+
# text << " ├─┐".light_black + " #{(klass.terms.join(', ')).ljust(24, ' ')} ".send(Athena::Util.next_color) + klass.description.light_black
|
71
|
+
text << " ├─┐".light_black + " #{key.to_s.ljust(24, ' ')} " + command.description.light_black
|
72
|
+
end
|
73
|
+
text << (" └" + "─" * 64).light_black
|
74
|
+
else
|
75
|
+
# text.last << command.arguments(missing_args.keys)
|
76
|
+
end
|
77
|
+
# text << list_examples if examples.any?
|
78
|
+
text << "Options:".light_cyan
|
79
|
+
end.join("\n")
|
80
|
+
switch_parser
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.define(label, chain: [], &block)
|
84
|
+
c = CommandDefinition.define(label, chain: chain, &block)
|
85
|
+
|
86
|
+
parent_command = if chain.any?
|
87
|
+
Ergane.active_tool.dig(*chain)
|
88
|
+
else
|
89
|
+
Ergane.active_tool
|
90
|
+
end
|
91
|
+
|
92
|
+
parent_command[label] = c
|
93
|
+
end
|
94
|
+
|
95
|
+
def load_commands(paths)
|
96
|
+
activate_tool do
|
97
|
+
Ergane.logger.debug "Loading paths:"
|
98
|
+
Array.wrap(paths).each do |path|
|
99
|
+
Ergane.logger.debug " - #{path}"
|
100
|
+
Dir[path].each do |path|
|
101
|
+
file = path.split('/').last
|
102
|
+
Ergane.logger.debug " - loading #{path.split('/').last(4).join('/')}"
|
103
|
+
instance_eval(File.read(path), file)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def activate_tool(&block)
|
112
|
+
Ergane.activate_tool(self, &block)
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
data/lib/ergane/util.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
module Ergane
|
2
|
+
module Util
|
3
|
+
|
4
|
+
def self.colors
|
5
|
+
# @colors = [:red, :yellow, :green, :blue, :cyan, :magenta].cycle
|
6
|
+
@colors ||= [:light_red, :light_yellow, :light_green, :light_blue, :light_cyan, :light_magenta].cycle
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.color_array(array)
|
10
|
+
array.collect do |a|
|
11
|
+
a.to_s.send(colors.next.to_sym).underline
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.color_array!(array)
|
16
|
+
colors.rewind
|
17
|
+
array.collect do |a|
|
18
|
+
a.to_s.send(colors.next.to_sym).underline
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.rainbow(string, delimeter=' ')
|
23
|
+
rainbow_a(string.split(delimeter)).join(delimeter)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.rainbow!(string, delimeter=' ')
|
27
|
+
rainbow_a!(string.split(delimeter)).join(delimeter)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.rainbow_a(array)
|
31
|
+
array.collect do |a|
|
32
|
+
a.to_s.send(next_color).underline
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.rainbow_a!(array)
|
37
|
+
rainbow_colors.rewind
|
38
|
+
array.collect do |a|
|
39
|
+
a.to_s.send(next_color).underline
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.next_color
|
44
|
+
colors.next
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.rainbow_colors
|
48
|
+
@rainbow_colors = [:light_red, :light_yellow, :light_green, :light_blue, :light_cyan, :light_magenta].cycle
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.next_rainbow_color
|
52
|
+
rainbow_colors.cycle
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.full_name
|
56
|
+
@full_name ||= `finger \`whoami\` | grep Name | awk -F 'Name: ' '{print $2}'`.chomp
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.last_name
|
60
|
+
@last_name || begin
|
61
|
+
split_names.last
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.first_name
|
66
|
+
@first_name || begin
|
67
|
+
split_names.first
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def self.split_names
|
74
|
+
@first_name, @last_name = ful_name.split(' ')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
data/lib/ergane.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'active_support/core_ext'
|
3
|
+
require 'active_support/dependencies'
|
4
|
+
require 'optparse'
|
5
|
+
require 'colorize'
|
6
|
+
require 'notifier'
|
7
|
+
require 'pry-byebug'
|
8
|
+
|
9
|
+
$LOAD_PATH.unshift File.expand_path("#{__dir__}/ergane")
|
10
|
+
|
11
|
+
require 'ergane/version'
|
12
|
+
require 'ergane/command_definition'
|
13
|
+
require 'ergane/tool'
|
14
|
+
|
15
|
+
module Ergane
|
16
|
+
|
17
|
+
Help = Class.new(StandardError)
|
18
|
+
Interrupt = Class.new(StandardError)
|
19
|
+
|
20
|
+
class << self
|
21
|
+
attr_reader :logger
|
22
|
+
|
23
|
+
def root(*args)
|
24
|
+
(@root ||= Pathname.new(File.expand_path('../', __dir__))).join(*args)
|
25
|
+
end
|
26
|
+
|
27
|
+
def logger
|
28
|
+
@logger ||= Logger.new($stdout).tap do |log|
|
29
|
+
log.progname = @@active_tool&.title || self.name
|
30
|
+
log.level = :warn
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# def debug(string)
|
35
|
+
# old_level = logger.level
|
36
|
+
# logger.level = :debug if $debug
|
37
|
+
# logger.debug(string)
|
38
|
+
# ensure
|
39
|
+
# logger.level = old_level
|
40
|
+
# end
|
41
|
+
|
42
|
+
@@active_tool = nil
|
43
|
+
|
44
|
+
def active_tool
|
45
|
+
@@active_tool
|
46
|
+
end
|
47
|
+
|
48
|
+
def activate_tool(tool)
|
49
|
+
previous_tool = @@active_tool
|
50
|
+
@@active_tool = tool
|
51
|
+
yield if block_given?
|
52
|
+
ensure
|
53
|
+
@@active_tool = previous_tool
|
54
|
+
end
|
55
|
+
|
56
|
+
def notify(message, title: nil, sound: nil, icon: nil, group: nil)
|
57
|
+
cmd = ['terminal-notifier']
|
58
|
+
cmd << "-group #{Process.pid}#{group}"
|
59
|
+
cmd << "-title #{active_tool.label}"
|
60
|
+
cmd << "-subtitle #{title}" if title
|
61
|
+
cmd << "-appIcon 'assets/athena md-light-shadow.png'" if icon
|
62
|
+
cmd << "-message '#{message}'"
|
63
|
+
cmd << "-activate 'com.apple.Terminal'" # if macos
|
64
|
+
cmd << "-sound #{sound}" if sound
|
65
|
+
Thread.new { `afplay /System/Library/Sounds/#{sound == true ? 'Blow' : sound}.aiff` } unless sound == false
|
66
|
+
|
67
|
+
`#{cmd.join(' ')}`
|
68
|
+
# Notifier.notify(title: title, message: message, image: 'assets/athena md-light-shadow.png')
|
69
|
+
end
|
70
|
+
|
71
|
+
# Show a list of available extensions to use
|
72
|
+
def extensions
|
73
|
+
Extension.library
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
if ARGV.include?('--debug')
|
79
|
+
$debug = true
|
80
|
+
Ergane.logger.level = :debug
|
81
|
+
end
|
82
|
+
|
83
|
+
Dir[Ergane.root('lib', 'core_ext', "*.rb")].each do |path|
|
84
|
+
require path
|
85
|
+
end
|
metadata
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ergane
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dale Stevens
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-05-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: colorize
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: terminal-notifier
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: notifier
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry-byebug
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: bundler
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: ruby-prof
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rspec
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: pry-byebug
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rake
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
description: |
|
154
|
+
Library for creating lightweight, yet powerful CLI tools in Ruby.
|
155
|
+
Emphasis and priority on load speed and flexibility.
|
156
|
+
email:
|
157
|
+
- dale@twilightcoders.net
|
158
|
+
executables:
|
159
|
+
- ergane
|
160
|
+
extensions: []
|
161
|
+
extra_rdoc_files: []
|
162
|
+
files:
|
163
|
+
- CHANGELOG.md
|
164
|
+
- LICENSE
|
165
|
+
- README.md
|
166
|
+
- app/commands/ergane/console.rb
|
167
|
+
- bin/ergane
|
168
|
+
- lib/core_ext/option_parser.rb
|
169
|
+
- lib/ergane.rb
|
170
|
+
- lib/ergane/command_definition.rb
|
171
|
+
- lib/ergane/debug.rb
|
172
|
+
- lib/ergane/helpers/hashall.rb
|
173
|
+
- lib/ergane/switch_definition.rb
|
174
|
+
- lib/ergane/tool.rb
|
175
|
+
- lib/ergane/util.rb
|
176
|
+
- lib/ergane/version.rb
|
177
|
+
homepage: https://github.com/TwilightCoders/ergane
|
178
|
+
licenses:
|
179
|
+
- MIT
|
180
|
+
metadata:
|
181
|
+
allowed_push_host: https://rubygems.org
|
182
|
+
post_install_message:
|
183
|
+
rdoc_options: []
|
184
|
+
require_paths:
|
185
|
+
- lib
|
186
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
187
|
+
requirements:
|
188
|
+
- - ">="
|
189
|
+
- !ruby/object:Gem::Version
|
190
|
+
version: '2.7'
|
191
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
192
|
+
requirements:
|
193
|
+
- - ">="
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
version: '0'
|
196
|
+
requirements: []
|
197
|
+
rubygems_version: 3.1.6
|
198
|
+
signing_key:
|
199
|
+
specification_version: 4
|
200
|
+
summary: Ergane — The patron goddess of craftsmen and artisans
|
201
|
+
test_files: []
|