lightning 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +9 -0
- data/README.rdoc +53 -125
- data/Rakefile +14 -40
- data/bin/lightning +4 -0
- data/bin/lightning-complete +1 -10
- data/bin/lightning-translate +4 -0
- data/lib/lightning.rb +36 -50
- data/lib/lightning/bolt.rb +53 -26
- data/lib/lightning/builder.rb +87 -0
- data/lib/lightning/commands.rb +92 -69
- data/lib/lightning/commands/bolt.rb +63 -0
- data/lib/lightning/commands/core.rb +57 -0
- data/lib/lightning/commands/function.rb +76 -0
- data/lib/lightning/commands/shell_command.rb +38 -0
- data/lib/lightning/commands_util.rb +75 -0
- data/lib/lightning/completion.rb +72 -28
- data/lib/lightning/completion_map.rb +42 -39
- data/lib/lightning/config.rb +92 -57
- data/lib/lightning/function.rb +70 -0
- data/lib/lightning/generator.rb +77 -43
- data/lib/lightning/generators.rb +53 -0
- data/lib/lightning/generators/misc.rb +12 -0
- data/lib/lightning/generators/ruby.rb +32 -0
- data/lib/lightning/util.rb +70 -0
- data/lib/lightning/version.rb +3 -0
- data/test/bolt_test.rb +16 -28
- data/test/builder_test.rb +54 -0
- data/test/commands_test.rb +98 -0
- data/test/completion_map_test.rb +31 -54
- data/test/completion_test.rb +106 -36
- data/test/config_test.rb +22 -56
- data/test/function_test.rb +90 -0
- data/test/generator_test.rb +73 -0
- data/test/lightning.yml +26 -34
- data/test/test_helper.rb +80 -15
- metadata +42 -20
- data/VERSION.yml +0 -4
- data/bin/lightning-full_path +0 -18
- data/bin/lightning-install +0 -7
- data/lib/lightning/bolts.rb +0 -12
- data/lightning.yml.example +0 -87
- data/lightning_completions.example +0 -147
- data/test/lightning_test.rb +0 -58
@@ -0,0 +1,87 @@
|
|
1
|
+
module Lightning
|
2
|
+
# Builds shell file ~/.lightning/functions.sh from the config file. This file
|
3
|
+
# is built and sourced into a user's shell using `lightning-reload`.
|
4
|
+
# Currently supports bash and zsh shells. Defaults to building for bash.
|
5
|
+
module Builder
|
6
|
+
extend self
|
7
|
+
|
8
|
+
# @private
|
9
|
+
HEADER = <<-INIT.gsub(/^\s{4}/,'')
|
10
|
+
#### This file was built by lightning. ####
|
11
|
+
#LBIN_PATH="$PWD/bin/" #only use for development
|
12
|
+
LBIN_PATH=""
|
13
|
+
|
14
|
+
lightning-reload() {
|
15
|
+
lightning install $@
|
16
|
+
source_file=$(lightning source_file)
|
17
|
+
source $source_file
|
18
|
+
echo Loaded $source_file
|
19
|
+
}
|
20
|
+
INIT
|
21
|
+
|
22
|
+
# @return [Boolean] Determines if Builder can build a file for its current shell
|
23
|
+
def can_build?
|
24
|
+
respond_to? "#{shell}_builder"
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [String] Current shell, defaults to 'bash'
|
28
|
+
def shell
|
29
|
+
Lightning.config[:shell] || 'bash'
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [String] Builds shell file
|
33
|
+
def run
|
34
|
+
return puts("No builder exists for #{Builder.shell} shell") unless Builder.can_build?
|
35
|
+
functions = Lightning.functions.values
|
36
|
+
check_for_existing_commands(functions)
|
37
|
+
output = build(functions)
|
38
|
+
File.open(Lightning.config.source_file, 'w') {|f| f.write(output) }
|
39
|
+
output
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param [Array] Function objects
|
43
|
+
# @return [String] Shell file string to be saved and sourced
|
44
|
+
def build(args)
|
45
|
+
HEADER + "\n\n" + send("#{shell}_builder", *[args])
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
def bash_builder(functions)
|
50
|
+
functions.map do |e|
|
51
|
+
<<-EOS.gsub(/^\s{10}/,'')
|
52
|
+
#{e.name} () {
|
53
|
+
local IFS=$'\\n'
|
54
|
+
local arr=( $(${LBIN_PATH}lightning-translate #{e.name} $@) )
|
55
|
+
#{e.shell_command} "${arr[@]}"
|
56
|
+
}
|
57
|
+
complete -o default -C "${LBIN_PATH}lightning-complete #{e.name}" #{e.name}
|
58
|
+
EOS
|
59
|
+
end.join("\n")
|
60
|
+
end
|
61
|
+
|
62
|
+
def zsh_builder(functions)
|
63
|
+
functions.map do |e|
|
64
|
+
<<-EOS.gsub(/^\s{10}/,'')
|
65
|
+
#{e.name} () {
|
66
|
+
local IFS=$'\\n'
|
67
|
+
local arr
|
68
|
+
arr=( $(${LBIN_PATH}lightning-translate #{e.name} $@) )
|
69
|
+
#{e.shell_command} "${arr[@]}"
|
70
|
+
}
|
71
|
+
_#{e.name} () {
|
72
|
+
local IFS=$'\\n'
|
73
|
+
reply=( $(${LBIN_PATH}lightning-complete #{e.name} "${@}") )
|
74
|
+
}
|
75
|
+
compctl -QK _#{e.name} #{e.name}
|
76
|
+
EOS
|
77
|
+
end.join("\n")
|
78
|
+
end
|
79
|
+
|
80
|
+
def check_for_existing_commands(functions)
|
81
|
+
if !(existing_commands = functions.select {|e| Util.shell_command_exists?(e.name) }).empty?
|
82
|
+
puts "The following commands already exist in $PATH and are being generated: "+
|
83
|
+
"#{existing_commands.map {|e| e.name}.join(', ')}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/lightning/commands.rb
CHANGED
@@ -1,92 +1,115 @@
|
|
1
|
-
|
2
|
-
#
|
1
|
+
module Lightning
|
2
|
+
# Runs lightning commands which are methods in this namespace.
|
3
|
+
#
|
4
|
+
# == Command Basics
|
5
|
+
# To get a list of commands and their description: +lightning -h+. To get usage and description on
|
6
|
+
# a command +lightning COMMAND -h+ i.e +lightning bolt -h+. Any command and subcommand can be abbreviated.
|
7
|
+
# For example, +lightning b c gem path1+ is short for +lightning bolt create gem path1+.
|
8
|
+
#
|
9
|
+
# == Command Plugins
|
10
|
+
# Command plugins are a way for users to define their own lightning commands.
|
11
|
+
# A command plugin is a .rb file in ~/.lightning/commands/. Each plugin can have multiple
|
12
|
+
# commands since a command is just a method in Lightning::Commands.
|
13
|
+
#
|
14
|
+
# A sample command plugin looks like this:
|
15
|
+
# module Lightning::Commands
|
16
|
+
# desc 'COMMAND', 'Prints hello'
|
17
|
+
# def hello(argv)
|
18
|
+
# puts "Hello with #{argv.size} arguments"
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# To register a command, {Commands#desc desc} must be placed before a method, describing the command's
|
23
|
+
# usage and description. Note that a command receives commandline arguments as an array. See
|
24
|
+
# {CommandsUtil} for helper methods to be used inside a command.
|
25
|
+
#
|
26
|
+
# For command plugin examples
|
27
|
+
# {read the source}[http://github.com/cldwalker/lightning/tree/master/lib/lightning/commands/].
|
3
28
|
module Commands
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
29
|
+
@desc = {}
|
30
|
+
extend self
|
31
|
+
extend CommandsUtil
|
32
|
+
|
33
|
+
# Called by `lightning` to call proper lightning command, print help or print version
|
34
|
+
def run(argv=ARGV)
|
35
|
+
if (command = argv.shift) && (actual_command = unalias_command(command))
|
36
|
+
run_command(actual_command, argv)
|
37
|
+
elsif command && respond_to?(command)
|
38
|
+
run_command(command, argv)
|
39
|
+
elsif %w{-v --version}.include?(command)
|
40
|
+
puts "lightning #{VERSION}"
|
9
41
|
else
|
10
|
-
|
42
|
+
load_user_commands
|
43
|
+
puts "Command '#{command}' not found.","\n" if command && !%w{-h --help}.include?(command)
|
44
|
+
print_help
|
11
45
|
end
|
12
|
-
puts lightning_commands.map {|e| e['name'] }.join("\n")
|
13
46
|
end
|
14
47
|
|
15
|
-
|
16
|
-
def
|
17
|
-
|
18
|
-
if options['delete']
|
19
|
-
return puts("Path needed") if args[0].nil?
|
20
|
-
delete_path(command_name, args[0])
|
21
|
-
elsif options['add']
|
22
|
-
return puts("Path needed") if args[0].nil?
|
23
|
-
add_path(command_name, args[0])
|
24
|
-
else
|
25
|
-
puts "Paths for command '#{command_name}'"
|
26
|
-
puts config_command_paths(command_name).join("\n")
|
27
|
-
end
|
48
|
+
# @return [Array] Available lightning commands
|
49
|
+
def commands
|
50
|
+
@desc.keys
|
28
51
|
end
|
29
52
|
|
30
|
-
|
31
|
-
def
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
add_command_alias(*args.slice(0,3))
|
53
|
+
# Calls proper lightning command with remaining commandline arguments
|
54
|
+
def run_command(command, args)
|
55
|
+
@command = command.to_s
|
56
|
+
if %w{-h --help}.include?(args[0])
|
57
|
+
print_command_help
|
36
58
|
else
|
37
|
-
|
38
|
-
(Lightning.config[:aliases] || []).each do |path_alias, path|
|
39
|
-
puts "#{path_alias} : #{path}"
|
40
|
-
end
|
59
|
+
send(command, args)
|
41
60
|
end
|
61
|
+
rescue StandardError
|
62
|
+
$stderr.puts "Error: "+ $!.message
|
63
|
+
end
|
64
|
+
|
65
|
+
# @return [String] Command usage for current command
|
66
|
+
def command_usage
|
67
|
+
"Usage: lightning #{@command} #{desc_array[0]}"
|
68
|
+
end
|
69
|
+
|
70
|
+
# Place before a command method to set its usage and description
|
71
|
+
def desc(*args)
|
72
|
+
@next_desc = args
|
42
73
|
end
|
43
74
|
|
44
75
|
private
|
45
|
-
def
|
46
|
-
|
47
|
-
if command['paths'].is_a?(String)
|
48
|
-
Lightning.config[:paths][command['paths']]
|
49
|
-
else
|
50
|
-
command['paths']
|
51
|
-
end
|
52
|
-
else
|
53
|
-
[]
|
54
|
-
end
|
76
|
+
def print_command_help
|
77
|
+
puts [command_usage, '', desc_array[1]]
|
55
78
|
end
|
56
79
|
|
57
|
-
def
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
80
|
+
def print_help
|
81
|
+
puts "lightning COMMAND [arguments]", ""
|
82
|
+
puts "Available commands:"
|
83
|
+
print_sorted_hash @desc.inject({}) {|a,(k,v)| a[k] = v[1]; a }, true
|
84
|
+
puts "", "For more information on a command use:"
|
85
|
+
puts " lightning COMMAND -h", ""
|
86
|
+
puts "Options: "
|
87
|
+
puts " -h, --help Show this help and exit"
|
88
|
+
puts " -v, --version Print current version and exit"
|
89
|
+
puts "", "Commands and subcommands can be abbreviated."
|
90
|
+
puts "For example, 'lightning b c gem path1' is short for 'lightning bolt create gem path1'."
|
64
91
|
end
|
65
92
|
|
66
|
-
def
|
67
|
-
|
68
|
-
command['aliases'].delete(path_alias)
|
69
|
-
Lightning.config.save
|
70
|
-
puts "Path alias '#{path_alias}' deleted"
|
71
|
-
end
|
93
|
+
def desc_array
|
94
|
+
Array(@desc[@command])
|
72
95
|
end
|
73
96
|
|
74
|
-
def
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
puts "Path '#{path}' added"
|
80
|
-
end
|
97
|
+
def unalias_command(command)
|
98
|
+
actual_command = commands.sort.find {|e| e[/^#{command}/] }
|
99
|
+
# don't load plugin commands for faster completion/translation
|
100
|
+
load_user_commands unless %w{translate complete}.include?(actual_command)
|
101
|
+
actual_command || commands.sort.find {|e| e[/^#{command}/] }
|
81
102
|
end
|
82
103
|
|
83
|
-
def
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
104
|
+
def load_user_commands
|
105
|
+
@load_user_commands ||= Util.load_plugins(Lightning.dir, 'commands') || true
|
106
|
+
end
|
107
|
+
|
108
|
+
def method_added(meth)
|
109
|
+
@desc[meth.to_s] = @next_desc if @next_desc
|
110
|
+
@next_desc = nil
|
90
111
|
end
|
91
112
|
end
|
92
113
|
end
|
114
|
+
|
115
|
+
Lightning::Util.load_plugins File.dirname(__FILE__), 'commands'
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Lightning::Commands
|
2
|
+
protected
|
3
|
+
desc "(list [-a|--alias] | alias BOLT ALIAS | create BOLT GLOBS | delete BOLT |\n#{' '*22} "+
|
4
|
+
"generate BOLT [generator] [-t|--test] | global ON_OR_OFF BOLTS | show BOLT)",
|
5
|
+
"Commands for managing bolts. Defaults to listing them."
|
6
|
+
def bolt(argv)
|
7
|
+
subcommand = argv.shift || 'list'
|
8
|
+
subcommand = %w{alias create delete generate global list show}.find {|e| e[/^#{subcommand}/]} || subcommand
|
9
|
+
bolt_subcommand(subcommand, argv) if subcommand_has_required_args(subcommand, argv)
|
10
|
+
end
|
11
|
+
|
12
|
+
def bolt_subcommand(subcommand, argv)
|
13
|
+
case subcommand
|
14
|
+
when 'list' then list_subcommand(:bolts, argv)
|
15
|
+
when 'create' then create_bolt(argv.shift, argv)
|
16
|
+
when 'alias' then alias_bolt(argv[0], argv[1])
|
17
|
+
when 'delete' then delete_bolt(argv[0])
|
18
|
+
when 'show' then show_bolt(argv[0])
|
19
|
+
when 'global' then globalize_bolts(argv.shift, argv)
|
20
|
+
when 'generate' then generate_bolt(argv)
|
21
|
+
else puts "Invalid subcommand '#{subcommand}'", command_usage
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def generate_bolt(argv)
|
26
|
+
args, options = parse_args argv
|
27
|
+
Lightning::Generator.run(args[0], :once=>args[1], :test=>options[:test] || options[:t])
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_bolt(bolt, globs)
|
31
|
+
config.bolts[bolt] = Lightning::Config.bolt(globs)
|
32
|
+
save_and_say "Created bolt '#{bolt}'"
|
33
|
+
end
|
34
|
+
|
35
|
+
def alias_bolt(bolt, bolt_alias)
|
36
|
+
if_bolt_found(bolt) do |bolt|
|
37
|
+
config.bolts[bolt]['alias'] = bolt_alias
|
38
|
+
save_and_say "Aliased bolt '#{bolt}' to '#{bolt_alias}'"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def delete_bolt(bolt)
|
43
|
+
if_bolt_found(bolt) do |bolt|
|
44
|
+
config.bolts.delete(bolt)
|
45
|
+
save_and_say "Deleted bolt '#{bolt}' and its functions"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def show_bolt(bolt)
|
50
|
+
if_bolt_found(bolt) {|b| puts config.bolts[b].to_yaml.sub("--- \n", '') }
|
51
|
+
end
|
52
|
+
|
53
|
+
def globalize_bolts(boolean, arr)
|
54
|
+
return puts("First argument must be 'on' or 'off'") unless %w{on off}.include?(boolean)
|
55
|
+
if boolean == 'on'
|
56
|
+
valid = arr.select {|b| if_bolt_found(b) {|bolt| config.bolts[bolt]['global'] = true } }
|
57
|
+
save_and_say "Global on for bolts #{valid.join(', ')}" unless valid.empty?
|
58
|
+
else
|
59
|
+
valid = arr.select {|b| if_bolt_found(b) {|bolt| config.bolts[bolt].delete('global') ; true } }
|
60
|
+
save_and_say "Global off for bolts #{valid.join(', ')}" unless valid.empty?
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Lightning
|
2
|
+
module Commands
|
3
|
+
# Silent command used by `lightning-reload` which prints {Builder}'s shell file
|
4
|
+
def source_file(argv)
|
5
|
+
puts config.source_file
|
6
|
+
end
|
7
|
+
|
8
|
+
protected
|
9
|
+
desc "FUNCTION [arguments]", "Prints a function's completions based on the last argument."
|
10
|
+
def complete(argv)
|
11
|
+
return unless command_has_required_args(argv, 1)
|
12
|
+
# this arg is needed by zsh in Complete
|
13
|
+
function = argv[0]
|
14
|
+
# bash hack: read ENV here because passing $COMP_LINE from the shell
|
15
|
+
# is a different incorrect buffer
|
16
|
+
buffer = ENV["COMP_LINE"] || argv.join(' ')
|
17
|
+
# zsh hack: when tabbing on blank space $@ is empty
|
18
|
+
# this ensures all completions
|
19
|
+
buffer += " " if argv.size == 1
|
20
|
+
|
21
|
+
puts Completion.complete(buffer, Lightning.functions[function])
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "FUNCTION ARGUMENTS", "Translates each argument and prints it on a separate line."
|
25
|
+
def translate(argv)
|
26
|
+
return unless command_has_required_args(argv, 2)
|
27
|
+
translations = (fn = Lightning.functions[argv.shift]) ?
|
28
|
+
fn.translate(argv).join("\n") : '#Error-no_function_found_to_translate'
|
29
|
+
puts translations
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "[--file=FILE] [--shell=SHELL]",
|
33
|
+
"Builds shell functions and installs them into FILE to be sourced by shell."
|
34
|
+
def install(argv)
|
35
|
+
args, options = parse_args(argv)
|
36
|
+
config[:shell] = options[:shell] if options[:shell]
|
37
|
+
config.source_file = options[:file] if options[:file]
|
38
|
+
|
39
|
+
if first_install?
|
40
|
+
Generator.run
|
41
|
+
puts "Created ~/.lightningrc"
|
42
|
+
config.bolts.each {|k,v| v['global'] = true }
|
43
|
+
config.save
|
44
|
+
end
|
45
|
+
|
46
|
+
Builder.run
|
47
|
+
puts "Created #{config.source_file}"+ (first_install? ? " for #{Builder.shell}" : '')
|
48
|
+
end
|
49
|
+
|
50
|
+
def first_install?; !File.exists?(Config.config_file); end
|
51
|
+
|
52
|
+
desc '', 'Lists available generators.'
|
53
|
+
def generator(argv)
|
54
|
+
print_sorted_hash Generator.generators
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Lightning::Commands
|
2
|
+
protected
|
3
|
+
desc '(list [--command=SHELL_COMMAND] [--bolt=BOLT] | create SHELL_COMMAND BOLT [function] | delete FUNCTION)',
|
4
|
+
'Commands for managing functions. Defaults to listing them.'
|
5
|
+
def function(argv)
|
6
|
+
subcommand = argv.shift || 'list'
|
7
|
+
subcommand = %w{create delete list}.find {|e| e[/^#{subcommand}/]} || subcommand
|
8
|
+
function_subcommand(subcommand, argv) if subcommand_has_required_args(subcommand, argv)
|
9
|
+
end
|
10
|
+
|
11
|
+
def function_subcommand(subcommand, argv)
|
12
|
+
case subcommand
|
13
|
+
when 'list' then list_function(argv)
|
14
|
+
when 'create' then create_function_and_bolt(argv)
|
15
|
+
when 'delete' then delete_function argv.shift
|
16
|
+
else puts "Invalid subcommand '#{subcommand}'", command_usage
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def list_function(argv)
|
21
|
+
args, options = parse_args argv
|
22
|
+
functions = if options[:bolt]
|
23
|
+
Lightning.functions.values.select {|e| e.bolt.name == options[:bolt] }.map {|e| e.name}
|
24
|
+
elsif options[:command]
|
25
|
+
Lightning.functions.values.select {|e| e.shell_command == options[:command] }.map {|e| e.name}
|
26
|
+
else
|
27
|
+
Lightning.functions.keys
|
28
|
+
end
|
29
|
+
puts functions.sort
|
30
|
+
end
|
31
|
+
|
32
|
+
def create_function_and_bolt(argv)
|
33
|
+
bolt = config.unalias_bolt(argv[1])
|
34
|
+
(config.bolts[bolt] || Lightning::Generator.run(bolt, :once=>bolt)) &&
|
35
|
+
create_function(argv[0], bolt, :name=>argv[2])
|
36
|
+
end
|
37
|
+
|
38
|
+
def create_function(scmd, bolt, options={})
|
39
|
+
options[:name] ||= Lightning.bolts[bolt].function_name(scmd) unless global_commands.include?(scmd)
|
40
|
+
function = options[:name] || Lightning.bolts[bolt].function_name(scmd)
|
41
|
+
|
42
|
+
if find_function(config.bolts[bolt]['functions'], 'shell_command', scmd)
|
43
|
+
puts("Function '#{function}' already exists")
|
44
|
+
else
|
45
|
+
if_bolt_found(bolt) do |bolt|
|
46
|
+
fn = options[:name] ? {'name'=>options[:name],'shell_command'=>scmd} : scmd
|
47
|
+
(config.bolts[bolt]['functions'] ||= []) << fn
|
48
|
+
save_and_say "Created function '#{function}'"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_function(functions, attribute, query)
|
54
|
+
Array(functions).find {|e|
|
55
|
+
attribute != 'shell_command' ? (e.is_a?(Hash) && e[attribute] == query) :
|
56
|
+
config.only_command(e.is_a?(Hash) ? e['shell_command'] : e) == query
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
def delete_function(fn)
|
61
|
+
if (function = Lightning.functions[fn])
|
62
|
+
cmd = config.unaliased_command(fn.split('-')[0])
|
63
|
+
if_bolt_found(function.bolt.name) {|bolt|
|
64
|
+
if (element = find_function(config.bolts[bolt]['functions'], 'name', fn) ||
|
65
|
+
find_function(config.bolts[bolt]['functions'], 'shell_command', cmd) )
|
66
|
+
config.bolts[bolt]['functions'].delete(element)
|
67
|
+
return save_and_say("Deleted function '#{fn}'")
|
68
|
+
elsif function.bolt.global && config.global_commands.include?(cmd)
|
69
|
+
return puts("Can't delete function '#{fn}' which is generated by a shell command '#{cmd}'.",
|
70
|
+
"Can only delete by deleting shell command: shell_command delete #{cmd}")
|
71
|
+
end
|
72
|
+
}
|
73
|
+
end
|
74
|
+
puts "Can't find function '#{fn}'"
|
75
|
+
end
|
76
|
+
end
|