envo 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/envo-install +18 -0
- data/bin/envo_gen_tmp_helper.rb +50 -0
- data/bin/envo_run +30 -0
- data/lib/envo.rb +45 -0
- data/lib/envo/cli/envo.bat +11 -0
- data/lib/envo/cli/envo.sh +6 -0
- data/lib/envo/cli/help.rb +20 -0
- data/lib/envo/cli/installer_bash.rb +131 -0
- data/lib/envo/cli/installer_win_cmd.rb +96 -0
- data/lib/envo/cli/runner.rb +129 -0
- data/lib/envo/cli_parser.rb +54 -0
- data/lib/envo/cmd_clean.rb +53 -0
- data/lib/envo/cmd_copy.rb +56 -0
- data/lib/envo/cmd_list.rb +50 -0
- data/lib/envo/cmd_list_add.rb +102 -0
- data/lib/envo/cmd_list_del.rb +78 -0
- data/lib/envo/cmd_path.rb +28 -0
- data/lib/envo/cmd_reset.rb +66 -0
- data/lib/envo/cmd_run.rb +68 -0
- data/lib/envo/cmd_set.rb +76 -0
- data/lib/envo/cmd_show.rb +82 -0
- data/lib/envo/cmd_swap.rb +49 -0
- data/lib/envo/cmd_unset.rb +45 -0
- data/lib/envo/context.rb +156 -0
- data/lib/envo/error.rb +7 -0
- data/lib/envo/host.rb +24 -0
- data/lib/envo/host_shell.rb +11 -0
- data/lib/envo/logger.rb +27 -0
- data/lib/envo/parse_result.rb +9 -0
- data/lib/envo/parsed_cmd.rb +10 -0
- data/lib/envo/script_parser.rb +58 -0
- data/lib/envo/shell/bash.rb +63 -0
- data/lib/envo/shell/win_cmd.rb +47 -0
- data/lib/envo/state.rb +71 -0
- data/lib/envo/val/list_val.rb +107 -0
- data/lib/envo/val/no_val.rb +35 -0
- data/lib/envo/val/path_list_val.rb +24 -0
- data/lib/envo/val/path_val.rb +41 -0
- data/lib/envo/val/string_val.rb +40 -0
- data/lib/envo/val/val_builder.rb +59 -0
- data/lib/envo/version.rb +4 -0
- metadata +89 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
module Envo
|
2
|
+
class CliParser
|
3
|
+
def self.opt?(opt)
|
4
|
+
opt =~ /^-/
|
5
|
+
end
|
6
|
+
def self.filter_opts_front(args)
|
7
|
+
front_opts = args.take_while { |a| opt?(a) }
|
8
|
+
args.shift(front_opts.size)
|
9
|
+
front_opts
|
10
|
+
end
|
11
|
+
def self.filter_opts_back(args)
|
12
|
+
back_opts = args.reverse.take_while { |a| opt?(a) }.reverse
|
13
|
+
args.pop(back_opts.size)
|
14
|
+
back_opts
|
15
|
+
end
|
16
|
+
def self.filter_opts(args)
|
17
|
+
filter_opts_front(args) + filter_opts_back(args)
|
18
|
+
end
|
19
|
+
def initialize(opts)
|
20
|
+
@known_cmds = {}
|
21
|
+
@known_opts = opts
|
22
|
+
end
|
23
|
+
def add_cmd(name, parse_func)
|
24
|
+
raise Envo::Error "cmd #{name} is already added to parser" if @known_cmds[name]
|
25
|
+
@known_cmds[name] = parse_func
|
26
|
+
end
|
27
|
+
def parse(argv)
|
28
|
+
result = ParseResult.new
|
29
|
+
cmd = nil
|
30
|
+
while !argv.empty?
|
31
|
+
arg = argv.shift
|
32
|
+
if CliParser.opt?(arg)
|
33
|
+
result.opts.merge! @known_opts.parse_cli(arg)
|
34
|
+
else
|
35
|
+
break cmd = arg
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
raise Envo::Error.new 'missing command' if !cmd
|
40
|
+
raise Envo::Error.new "unknown command '#{cmd}'" if !@known_cmds[cmd]
|
41
|
+
|
42
|
+
parsed_cmd = @known_cmds[cmd].(cmd, argv)
|
43
|
+
|
44
|
+
cmd_opts = {}
|
45
|
+
parsed_cmd.opts.each do |opt|
|
46
|
+
cmd_opts.merge! @known_opts.parse_cli(opt)
|
47
|
+
end
|
48
|
+
parsed_cmd.opts = cmd_opts
|
49
|
+
|
50
|
+
result.cmds << parsed_cmd
|
51
|
+
result
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Envo
|
2
|
+
class CmdClean
|
3
|
+
Name = 'clean'
|
4
|
+
def self.register_help(help)
|
5
|
+
help.add_cmd "clean <name> ...", <<~EOF
|
6
|
+
cleans environent variables
|
7
|
+
unsets empty strings, non-existing paths, empty lists
|
8
|
+
removes duplicates from lists and non-existing paths from path lists
|
9
|
+
EOF
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.register_cli_parser(parser)
|
13
|
+
parser.add_cmd(Name, ->(cmd, args) { parse_cli(args) })
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.register_script_parser(parser)
|
17
|
+
parser.add_cmd(Name, ->(cmd, tokens, opts) { parse_tokens(tokens, opts) })
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.parse_cli(args)
|
21
|
+
opts = CliParser.filter_opts(args)
|
22
|
+
parse_tokens(args, opts)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.parse_tokens(tokens, opts)
|
26
|
+
ParsedCmd.new(CmdClean.new(tokens), opts)
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(names)
|
30
|
+
raise Error.new 'clean: no names provided' if names.empty?
|
31
|
+
@names = names
|
32
|
+
end
|
33
|
+
|
34
|
+
attr_reader :names, :show_names
|
35
|
+
|
36
|
+
def execute(ctx)
|
37
|
+
@names.each do |name|
|
38
|
+
ename = ctx.expand_name(name)
|
39
|
+
|
40
|
+
if ctx.raw?
|
41
|
+
val = ctx.raw_get(ename)
|
42
|
+
ctx.unset(ename) if val && val.empty?
|
43
|
+
else
|
44
|
+
val = ctx.smart_get(ename)
|
45
|
+
if val.type != :empty
|
46
|
+
val.clean!
|
47
|
+
ctx.smart_set(ename, val)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Envo
|
2
|
+
class CmdCopy
|
3
|
+
Name = 'copy'
|
4
|
+
def self.register_help(help)
|
5
|
+
help.add_cmd 'copy <source-name> <target-name>', <<~EOF
|
6
|
+
copy value of source to target
|
7
|
+
shorthand: 'cp'
|
8
|
+
EOF
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.register_cli_parser(parser)
|
12
|
+
parser.add_cmd(Name, ->(cmd, args) { parse_cli(args) })
|
13
|
+
parser.add_cmd('cp', ->(cmd, args) { parse_cli(args) })
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.register_script_parser(parser)
|
17
|
+
parser.add_cmd(Name, ->(cmd, tokens, opts) { parse_tokens(tokens, opts) })
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.parse_cli(args)
|
21
|
+
opts = CliParser.filter_opts(args)
|
22
|
+
parse_tokens(args, opts)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.parse_tokens(tokens, opts)
|
26
|
+
raise Envo::Error.new "copy: provide two names to copy. Use 'copy <source-name> <target-name>'" if tokens.size != 2
|
27
|
+
ParsedCmd.new(CmdCopy.new(tokens[0], tokens[1]), opts)
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(source, target)
|
31
|
+
@source = source
|
32
|
+
@target = target
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_accessor :source, :target
|
36
|
+
|
37
|
+
def execute(ctx)
|
38
|
+
esrc = ctx.expand_name(@source)
|
39
|
+
raw_src = ctx.raw_get(esrc)
|
40
|
+
raise Envo::Error.new "copy: no such var '#{esrc}'" if !raw_src && !ctx.force?
|
41
|
+
return if !raw_src
|
42
|
+
|
43
|
+
etarget = ctx.expand_name(@target)
|
44
|
+
raw_target = ctx.raw_get(etarget)
|
45
|
+
|
46
|
+
ok = !raw_target
|
47
|
+
ok ||= ctx.ask("'#{etarget}' already exists. Overwrite?")
|
48
|
+
raise Envo::Error.new "'#{etarget}' exists" if !ok
|
49
|
+
|
50
|
+
return if esrc == etarget # copy something over itself...
|
51
|
+
return if !raw_src && !raw_target # no point in doing anything if they don't exist
|
52
|
+
|
53
|
+
ctx.raw_set(etarget, raw_src)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Envo
|
2
|
+
class CmdList
|
3
|
+
Name = 'list'
|
4
|
+
def self.register_help(help)
|
5
|
+
help.add_cmd "list <name>", "show value of list environment variable which is a list"
|
6
|
+
help.add_cmd "list <name> add <val>", <<~EOF
|
7
|
+
add value to list if it's not already inside
|
8
|
+
--front - adds value to front of list (moves it to front if it's already inside)
|
9
|
+
--back - adds value to back of list (moves it to back if it's already inside)
|
10
|
+
EOF
|
11
|
+
help.add_cmd "list <name> del <val|index>", <<~EOF
|
12
|
+
remove a value from a list
|
13
|
+
if the provided value is an integer, it's interpreted as an index in the list
|
14
|
+
EOF
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.register_cli_parser(parser)
|
18
|
+
parser.add_cmd(Name, ->(cmd, args) { parse_cli(args) })
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.parse_cli(args)
|
22
|
+
opts = CliParser.filter_opts_front(args)
|
23
|
+
raise Envo::Error.new "list: missing name. Use 'list <name> <cmd> <args>'" if args.empty?
|
24
|
+
name = args.shift
|
25
|
+
return ParsedCmd.new(CmdShow.new([name], true), opts) if args.empty? # just list <name>
|
26
|
+
|
27
|
+
cmd = args.shift
|
28
|
+
return CmdListAdd.parse_cli_args(name, args, opts) if cmd == 'add'
|
29
|
+
return CmdListDel.parse_cli_args(name, args, opts) if cmd == 'del'
|
30
|
+
|
31
|
+
raise Envo::Error.new "list: unkonwn subcommand #{cmd}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.register_script_parser(parser)
|
35
|
+
parser.add_cmd(Name, ->(cmd, tokens, opts) { parse_script(tokens, opts) })
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.parse_script(tokens, opts)
|
39
|
+
raise Envo::Error.new "list: missing name. Use 'list <name> <cmd> <args>'" if tokens.empty?
|
40
|
+
name = tokens.shift
|
41
|
+
return ParsedCmd.new(CmdShow.new([name], true), opts) if tokens.empty? # just list <name>
|
42
|
+
|
43
|
+
cmd = tokens.shift
|
44
|
+
return CmdListAdd.parse_script(name, tokens, opts) if cmd == 'add'
|
45
|
+
return CmdListDel.parse_tokens(name, tokens, opts) if cmd == 'del'
|
46
|
+
|
47
|
+
raise Envo::Error.new "list: unkonwn subcommand #{cmd}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module Envo
|
2
|
+
class CmdListAdd
|
3
|
+
def self.register_help(help)
|
4
|
+
help.add_cmd 'la <name> <val>', "shorthand for 'list <name> add <val>'"
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.register_cli_parser(parser)
|
8
|
+
parser.add_cmd('la', ->(cmd, args) { parse_cli_all(args) })
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.register_script_parser(parser)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.parse_cli_all(args)
|
15
|
+
opts = CliParser.filter_opts_front(args)
|
16
|
+
raise Envo::Error.new "list-add: missing name. Use 'la <name> <val>'" if args.empty?
|
17
|
+
parse_cli_args(args[0], args[1..], opts)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.parse_script(name, tokens, opts)
|
21
|
+
pos = nil
|
22
|
+
opts.filter! do |opt|
|
23
|
+
case opt
|
24
|
+
when 'front', 'top'
|
25
|
+
pos = :front
|
26
|
+
false
|
27
|
+
when 'back', 'bottom'
|
28
|
+
pos = :back
|
29
|
+
false
|
30
|
+
else
|
31
|
+
true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
ParsedCmd.new(CmdListAdd.new(name, tokens, pos), opts)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.parse_cli_args(name, args, opts)
|
39
|
+
opts += CliParser.filter_opts(args)
|
40
|
+
pos = nil
|
41
|
+
opts.filter! do |opt|
|
42
|
+
case opt
|
43
|
+
when '--front', '--top'
|
44
|
+
pos = :front
|
45
|
+
false
|
46
|
+
when '--back', '--bottom'
|
47
|
+
pos = :back
|
48
|
+
false
|
49
|
+
else
|
50
|
+
true
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
ParsedCmd.new(CmdListAdd.new(name, args, pos), opts)
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize(name, values, pos)
|
58
|
+
raise Envo::Error.new 'list-add: no values to add provided' if values.empty?
|
59
|
+
@name = name
|
60
|
+
@values = values
|
61
|
+
@pos = pos
|
62
|
+
end
|
63
|
+
|
64
|
+
attr_accessor :name, :values, :pos
|
65
|
+
|
66
|
+
def execute(ctx)
|
67
|
+
ename = ctx.expand_name(@name)
|
68
|
+
|
69
|
+
list = ctx.smart_get(ename)
|
70
|
+
|
71
|
+
ok = list.list?
|
72
|
+
if !ok
|
73
|
+
if list.type == :empty
|
74
|
+
ok ||= ctx.ask("#{ename} doesn't exist. Create?")
|
75
|
+
else
|
76
|
+
ok ||= ctx.ask("#{ename} is not a list, but a #{list.type}. Convert?")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
raise Envo::Error.new "list-add: adding list item to a non-list" if !ok
|
80
|
+
|
81
|
+
list = list.to_list
|
82
|
+
|
83
|
+
ordered = @pos == :front ? values.reverse : values
|
84
|
+
ordered.each do |val|
|
85
|
+
val = ctx.expand_value(val)
|
86
|
+
|
87
|
+
ok = list.accept_item?(val)
|
88
|
+
ok ||= ctx.ask("Add #{val.type} to #{list.type}?")
|
89
|
+
raise Envo::Error.new "list-add: adding #{val.type} to #{list.type}" if !ok
|
90
|
+
|
91
|
+
idesc = val.invalid_description
|
92
|
+
ok = !idesc
|
93
|
+
ok ||= ctx.ask("Add #{idesc} to #{ename}?")
|
94
|
+
raise Envo::Error.new "list-add: adding #{idesc} to #{ename}" if !ok
|
95
|
+
|
96
|
+
list.insert(val.to_s, @pos)
|
97
|
+
end
|
98
|
+
|
99
|
+
ctx.smart_set(ename, list)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Envo
|
2
|
+
class CmdListDel
|
3
|
+
def self.register_help(help)
|
4
|
+
help.add_cmd 'ld <name> <val|index>', "shorthand for 'list <name> del <val|index>'"
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.register_cli_parser(parser)
|
8
|
+
parser.add_cmd('ld', ->(cmd, args) { parse_cli_all(args) })
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.register_script_parser(parser)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.parse_cli_all(args)
|
15
|
+
opts = CliParser.filter_opts_front(args)
|
16
|
+
raise Envo::Error.new "list-del: missing name. Use 'ld <name> <val|index>'" if args.empty?
|
17
|
+
parse_cli_args(args[0], args[1..], opts)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.parse_cli_args(name, args, opts)
|
21
|
+
opts += CliParser.filter_opts(args)
|
22
|
+
parse_tokens(name, args, opts)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.parse_tokens(name, tokens, opts)
|
26
|
+
raise Envo::Error.new "list-del: provide one value or index to delete. Use 'list <name> del <val|index>'" if tokens.size != 1
|
27
|
+
ParsedCmd.new(CmdListDel.new(name, tokens[0]), opts)
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(name, value)
|
31
|
+
@name = name
|
32
|
+
@value = value
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_accessor :name, :value
|
36
|
+
|
37
|
+
def execute(ctx)
|
38
|
+
ename = ctx.expand_name(@name)
|
39
|
+
|
40
|
+
list = ctx.smart_get(ename)
|
41
|
+
|
42
|
+
if list.type == :empty
|
43
|
+
return if ctx.force?
|
44
|
+
raise Envo::Error.new "list-del: deleting an item from a non-exiting value"
|
45
|
+
end
|
46
|
+
|
47
|
+
ok = list.list?
|
48
|
+
ok ||= ctx.ask("#{ename} is not a list, but a #{list.type}. Convert?")
|
49
|
+
raise Envo::Error.new "list-del: deleting an item from a non-list" if !ok
|
50
|
+
|
51
|
+
list = list.to_list
|
52
|
+
|
53
|
+
val = ctx.expand_value(@value)
|
54
|
+
if val.list?
|
55
|
+
raise Envo::Error.new "list-del: can't delete a list from a list" if !ctx.force?
|
56
|
+
return
|
57
|
+
end
|
58
|
+
val = val.to_s
|
59
|
+
|
60
|
+
index = val.to_i
|
61
|
+
if !ctx.raw? && index.to_s == val
|
62
|
+
deleted = list.delete_at(index)
|
63
|
+
if !deleted
|
64
|
+
raise Envo::Error.new "list-del: no index #{val} in #{ename}" if !ctx.force?
|
65
|
+
return
|
66
|
+
end
|
67
|
+
else
|
68
|
+
deleted = list.delete(val)
|
69
|
+
if !deleted
|
70
|
+
raise Envo::Error.new "list-del: no item '#{val}' in #{ename}" if !deleted
|
71
|
+
return
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
ctx.smart_set(ename, list)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Envo
|
2
|
+
class CmdPath
|
3
|
+
Name = 'path'
|
4
|
+
def self.register_help(help)
|
5
|
+
help.add_cmd "path <args>", <<~EOF
|
6
|
+
shorthand for 'list @path <args>'
|
7
|
+
shorthand: 'p'
|
8
|
+
EOF
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.register_cli_parser(parser)
|
12
|
+
parser.add_cmd(Name, ->(cmd, args) { parse_cli(args) })
|
13
|
+
parser.add_cmd('p', ->(cmd, args) { parse_cli(args) })
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.register_script_parser(parser)
|
17
|
+
parser.add_cmd(Name, ->(cmd, tokens, opts) { parse_script(tokens, opts) })
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.parse_cli(args)
|
21
|
+
CmdList.parse_cli(args.unshift('@path'))
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.parse_script(tokens, opts)
|
25
|
+
CmdList.parse_script(tokens.unshift('@path'), opts)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Envo
|
2
|
+
class CmdReset
|
3
|
+
Name = 'reset'
|
4
|
+
def self.register_help(help)
|
5
|
+
help.add_cmd 'reset <name>[=[<val>]]', <<~EOF
|
6
|
+
set or unset value of an existing environment variable
|
7
|
+
produce an error if the variable doesn't exist
|
8
|
+
EOF
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.register_cli_parser(parser)
|
12
|
+
parser.add_cmd(Name, ->(cmd, args) { parse_cli(args) })
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.register_script_parser(parser)
|
16
|
+
parser.add_cmd(Name, ->(cmd, tokens, opts) { parse_tokens(tokens, opts) })
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.parse_cli(args)
|
20
|
+
opts = CliParser.filter_opts(args)
|
21
|
+
parse_tokens(args, opts)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.parse_tokens(args, opts)
|
25
|
+
raise Envo::Error.new "reset: missing name. Use 'reset <name> [= [<val>]]'" if args.empty? || args[0][0] == '='
|
26
|
+
if args.size == 1
|
27
|
+
arg = args[0]
|
28
|
+
cnt = arg.count('=')
|
29
|
+
return ParsedCmd.new(CmdReset.new(arg, nil), opts) if cnt == 0
|
30
|
+
if cnt == 1 && arg[-1] == '='
|
31
|
+
split = arg.split('=')
|
32
|
+
return ParsedCmd.new(CmdReset.new(split[0], nil), opts) if split.size == 1
|
33
|
+
end
|
34
|
+
elsif args.size == 2
|
35
|
+
return ParsedCmd.new(CmdReset.new(args[0], nil), opts) if args[1] == '='
|
36
|
+
end
|
37
|
+
|
38
|
+
helper = CmdSet.parse_cli(args).cmd
|
39
|
+
ParsedCmd.new(CmdReset.new(helper.name, helper), opts)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.parse_script(args)
|
43
|
+
puts "#{Name} parse_script"
|
44
|
+
end
|
45
|
+
|
46
|
+
def initialize(name, helper)
|
47
|
+
@name = name
|
48
|
+
@helper = helper
|
49
|
+
end
|
50
|
+
|
51
|
+
attr_accessor :name, :helper
|
52
|
+
|
53
|
+
def execute(ctx)
|
54
|
+
ename = ctx.expand_name(@name)
|
55
|
+
|
56
|
+
raise Envo::Error.new "reset: no such var '#{ename}'" if !ctx.raw_get(ename)
|
57
|
+
|
58
|
+
if !@helper
|
59
|
+
ctx.unset(ename)
|
60
|
+
else
|
61
|
+
@helper.name = @name
|
62
|
+
@helper.execute(ctx)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|