vop 0.3.1 → 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/Gemfile +2 -1
- data/Gemfile.lock +34 -33
- data/README.md +184 -2
- data/bin/console +17 -0
- data/bin/setup +8 -0
- data/bin/vop.sh +6 -2
- data/exe/vop +3 -25
- data/lib/boot.rb +3 -0
- data/lib/core/meta/commands/new_command.rb +1 -0
- data/lib/core/meta/commands/new_plugin.rb +47 -0
- data/lib/core/meta/commands/search_gems_for_plugins.rb +38 -0
- data/lib/core/meta/commands/search_path.rb +6 -0
- data/lib/core/meta/commands/set.rb +12 -0
- data/lib/core/meta/commands/show.rb +6 -0
- data/lib/core/meta/commands/show_config.rb +9 -0
- data/lib/core/meta/commands/who_provides.rb +6 -0
- data/lib/core/meta/meta.plugin +1 -0
- data/lib/core/shell/commands/change_loglevel.rb +6 -0
- data/lib/{vop/plugins/core → core/shell}/commands/clear_context.rb +0 -0
- data/lib/core/shell/commands/edit.rb +12 -0
- data/lib/core/shell/commands/help.rb +49 -0
- data/lib/core/shell/commands/reset.rb +4 -0
- data/lib/{vop/plugins/core → core/shell}/commands/show_context.rb +0 -0
- data/lib/core/shell/commands/source.rb +21 -0
- data/lib/{vop/plugins/core/helpers/plugin_loader/plugin_syntax.rb → core/shell/shell.plugin} +0 -0
- data/lib/core/structure/commands/collect_contributions.rb +46 -0
- data/lib/core/structure/commands/disable_contributor.rb +16 -0
- data/lib/core/structure/commands/generate_entity_commands.rb +52 -0
- data/lib/core/structure/commands/generate_invalidation_commands.rb +26 -0
- data/lib/core/structure/commands/list_commands.rb +11 -0
- data/lib/core/structure/commands/list_contributors.rb +10 -0
- data/lib/core/structure/commands/list_entities.rb +3 -0
- data/lib/core/structure/commands/list_plugins.rb +3 -0
- data/lib/core/structure/commands/register_contributor.rb +14 -0
- data/lib/core/structure/structure.plugin +4 -0
- data/lib/vop/objects/chain.rb +25 -0
- data/lib/vop/objects/command.rb +86 -0
- data/lib/vop/objects/command_param.rb +39 -0
- data/lib/vop/objects/entities.rb +21 -0
- data/lib/vop/objects/entity.rb +75 -0
- data/lib/vop/objects/entity_definition.rb +33 -0
- data/lib/vop/objects/filter.rb +48 -0
- data/lib/vop/objects/plugin.rb +208 -0
- data/lib/vop/objects/request.rb +73 -0
- data/lib/vop/objects/response.rb +17 -0
- data/lib/vop/objects/thing_with_params.rb +17 -0
- data/lib/vop/{command_loader.rb → parts/command_loader.rb} +8 -12
- data/lib/vop/parts/dependency_resolver.rb +56 -0
- data/lib/vop/parts/entity_loader.rb +46 -0
- data/lib/vop/parts/executor.rb +155 -0
- data/lib/vop/parts/filter_loader.rb +41 -0
- data/lib/vop/parts/plugin_finder.rb +46 -0
- data/lib/vop/parts/plugin_loader.rb +72 -0
- data/lib/vop/shell/shell.rb +221 -0
- data/lib/vop/shell/shell_formatter.rb +110 -0
- data/lib/vop/shell/shell_input.rb +14 -0
- data/lib/vop/shell/shell_input_readline.rb +20 -0
- data/lib/vop/shell/shell_input_testable.rb +27 -0
- data/lib/vop/syntax/command_syntax.rb +90 -0
- data/lib/vop/syntax/entity_syntax.rb +35 -0
- data/lib/vop/syntax/filter_syntax.rb +11 -0
- data/lib/vop/syntax/plugin_syntax.rb +55 -0
- data/lib/vop/util/errors.rb +45 -0
- data/lib/vop/util/pluralizer.rb +26 -0
- data/lib/vop/util/worker.rb +24 -0
- data/lib/vop/version.rb +1 -1
- data/lib/vop/vop.rb +216 -0
- data/lib/vop.rb +16 -229
- data/vop.gemspec +18 -15
- metadata +95 -63
- data/bin/vop.rb +0 -28
- data/lib/vop/command.rb +0 -168
- data/lib/vop/entity.rb +0 -61
- data/lib/vop/loader.rb +0 -35
- data/lib/vop/plugin.rb +0 -141
- data/lib/vop/plugin_loader.rb +0 -88
- data/lib/vop/plugins/core/commands/collect_contributions.rb +0 -31
- data/lib/vop/plugins/core/commands/edit.rb +0 -12
- data/lib/vop/plugins/core/commands/help.rb +0 -38
- data/lib/vop/plugins/core/commands/identity.rb +0 -4
- data/lib/vop/plugins/core/commands/list_contributors.rb +0 -8
- data/lib/vop/plugins/core/commands/list_entities.rb +0 -3
- data/lib/vop/plugins/core/commands/pry.rb +0 -9
- data/lib/vop/plugins/core/commands/reset.rb +0 -5
- data/lib/vop/plugins/core/commands/source.rb +0 -5
- data/lib/vop/plugins/core/commands/system_call.rb +0 -5
- data/lib/vop/plugins/core/core.plugin +0 -4
- data/lib/vop/plugins/core/helpers/command_loader/command_syntax.rb +0 -45
- data/lib/vop/plugins/core/helpers/command_loader/contributions.rb +0 -28
- data/lib/vop/plugins/core/helpers/command_loader/entities.rb +0 -57
- data/lib/vop/plugins/core/helpers/helper.rb +0 -3
- data/lib/vop/plugins/meta/commands/add_search_path.rb +0 -6
- data/lib/vop/plugins/meta/commands/delete_plugin.rb +0 -13
- data/lib/vop/plugins/meta/commands/list_commands.rb +0 -17
- data/lib/vop/plugins/meta/commands/list_plugins.rb +0 -8
- data/lib/vop/plugins/meta/commands/new_command.rb +0 -14
- data/lib/vop/plugins/meta/commands/new_plugin.rb +0 -25
- data/lib/vop/plugins/meta/commands/show_search_path.rb +0 -3
- data/lib/vop/plugins/meta/commands/who_provides.rb +0 -5
- data/lib/vop/plugins/meta/meta.plugin +0 -1
- data/lib/vop/plugins/ssh/commands/scp.rb +0 -11
- data/lib/vop/plugins/ssh/commands/ssh.rb +0 -19
- data/lib/vop/plugins/ssh/ssh.plugin +0 -1
- data/lib/vop/shell/backend.rb +0 -28
- data/lib/vop/shell/base_shell.rb +0 -112
- data/lib/vop/shell/formatter.rb +0 -46
- data/lib/vop/shell/vop_shell_backend.rb +0 -257
- data/lib/vop/shell.rb +0 -52
data/lib/vop/plugin_loader.rb
DELETED
@@ -1,88 +0,0 @@
|
|
1
|
-
require 'vop/plugin'
|
2
|
-
require 'pp'
|
3
|
-
|
4
|
-
module Vop
|
5
|
-
|
6
|
-
class PluginLoader
|
7
|
-
|
8
|
-
attr_reader :op
|
9
|
-
attr_reader :dir
|
10
|
-
|
11
|
-
def initialize(op, dir)
|
12
|
-
@op = op
|
13
|
-
@dir = dir
|
14
|
-
end
|
15
|
-
|
16
|
-
def new_plugin(plugin_name, plugin_path)
|
17
|
-
@plugin = Plugin.new(@op, plugin_name, plugin_path)
|
18
|
-
|
19
|
-
# TODO activate plugin_loader helpers?
|
20
|
-
# (this will allow plugins to define helpers that can be used in plugins)
|
21
|
-
#@plugin.inject_helpers(self)
|
22
|
-
#@plugin.inject_helpers(self, 'plugin_loader')
|
23
|
-
|
24
|
-
@op.plugins[plugin_name] = @plugin
|
25
|
-
@plugin
|
26
|
-
end
|
27
|
-
|
28
|
-
def on(hook_sym, &block)
|
29
|
-
@plugin.hook(hook_sym, &block)
|
30
|
-
|
31
|
-
core_hooks = %i|before_execute after_execute|
|
32
|
-
if core_hooks.include? hook_sym
|
33
|
-
@op.hook(hook_sym, @plugin.name)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def dependency(sym)
|
38
|
-
@plugin.dependencies << sym.to_s
|
39
|
-
end
|
40
|
-
alias_method :depends, :dependency
|
41
|
-
|
42
|
-
def read_plugin_template
|
43
|
-
template_path = File.join(@dir, 'plugin.vop')
|
44
|
-
plugin_template = nil
|
45
|
-
if File.exists?(template_path)
|
46
|
-
plugin_template = IO.read(template_path)
|
47
|
-
end
|
48
|
-
if plugin_template
|
49
|
-
begin
|
50
|
-
self.instance_eval plugin_template
|
51
|
-
rescue => e
|
52
|
-
$stderr.puts "could not load plugin #{template_path} : " + e.message
|
53
|
-
$stderr.puts e.backtrace.join("\n")
|
54
|
-
raise e
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def self.read(op, dir)
|
60
|
-
$logger.debug("scanning dir #{dir} for plugins...")
|
61
|
-
|
62
|
-
loader = new(op, dir)
|
63
|
-
loader.read_plugin_template
|
64
|
-
|
65
|
-
Dir.new(dir).each do |plugin_name|
|
66
|
-
next if /^\./.match(plugin_name)
|
67
|
-
|
68
|
-
plugin_path = File.join(dir, plugin_name)
|
69
|
-
file_name = File.join(plugin_path, "#{plugin_name}.plugin")
|
70
|
-
next unless File.exists?(file_name)
|
71
|
-
$logger.debug("reading plugin '#{plugin_name}' from '#{file_name}'")
|
72
|
-
|
73
|
-
plugin = loader.new_plugin(plugin_name, plugin_path)
|
74
|
-
|
75
|
-
code = File.read(file_name)
|
76
|
-
begin
|
77
|
-
loader.instance_eval(code, file_name)
|
78
|
-
rescue => detail
|
79
|
-
raise "problem loading plugin #{plugin_name} : #{detail.message}\n#{detail.backtrace.join("\n")}"
|
80
|
-
end
|
81
|
-
|
82
|
-
$logger.debug "loaded plugin #{plugin_name}"
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
end
|
87
|
-
|
88
|
-
end
|
@@ -1,31 +0,0 @@
|
|
1
|
-
param! "name"
|
2
|
-
param "except", :multi => true
|
3
|
-
param! "raw_params"
|
4
|
-
|
5
|
-
run do |name, raw_params, params|
|
6
|
-
result = []
|
7
|
-
|
8
|
-
registry = @op.plugins["core"].state[:contributions]
|
9
|
-
if registry.has_key? name
|
10
|
-
contributors = registry[name]
|
11
|
-
contributors.each do |contributor|
|
12
|
-
except = params["except"] || []
|
13
|
-
if except.include? contributor
|
14
|
-
puts "skipping contributor #{contributor} because of except"
|
15
|
-
next
|
16
|
-
end
|
17
|
-
|
18
|
-
$logger.debug "calling #{contributor} for contribution to #{name}"
|
19
|
-
#puts caller[0..10]
|
20
|
-
if caller.grep(/eval.*block in entity/).size > 2
|
21
|
-
$logger.debug "skipping contribution from #{contributor} because of loop"
|
22
|
-
else
|
23
|
-
$logger.debug "raw params: " + raw_params.inspect
|
24
|
-
short_name = contributor.to_s.split(".").last
|
25
|
-
result += @op.send(short_name.to_sym, raw_params)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
result
|
31
|
-
end
|
@@ -1,12 +0,0 @@
|
|
1
|
-
param! "name", :lookup => lambda { @op.list_commands.map { |x| x[:name] } }
|
2
|
-
|
3
|
-
run do |params, name|
|
4
|
-
editor = ENV["EDITOR"]
|
5
|
-
editor = "vim" # TODO if "config.default_editor"
|
6
|
-
raise "please set the EDITOR environment variable" unless editor
|
7
|
-
|
8
|
-
command_file = @op.command(name).source[:file_name]
|
9
|
-
system("#{editor} #{command_file}")
|
10
|
-
|
11
|
-
@op.reset
|
12
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
param! "name", :lookup => lambda { @op.list_commands.map { |x| x[:name] } }
|
2
|
-
|
3
|
-
run do |params|
|
4
|
-
puts
|
5
|
-
puts params["name"]
|
6
|
-
command = @op.commands[params["name"]]
|
7
|
-
if command.description
|
8
|
-
puts " #{command.description}"
|
9
|
-
end
|
10
|
-
|
11
|
-
puts
|
12
|
-
puts "syntax:"
|
13
|
-
|
14
|
-
minimal_syntax = command.short_name
|
15
|
-
maximal_syntax = command.short_name
|
16
|
-
command.params.each do |p|
|
17
|
-
example = if p == command.default_param
|
18
|
-
"<#{p[:name]}>"
|
19
|
-
else
|
20
|
-
"#{p[:name]}=<#{p[:name]}>"
|
21
|
-
end
|
22
|
-
minimal_syntax << " #{example}" if p[:mandatory]
|
23
|
-
maximal_syntax << " #{example}"
|
24
|
-
end
|
25
|
-
puts " #{minimal_syntax}"
|
26
|
-
puts " #{maximal_syntax}" unless minimal_syntax == maximal_syntax
|
27
|
-
|
28
|
-
if command.params.size > 0
|
29
|
-
puts
|
30
|
-
puts "parameters:"
|
31
|
-
max_param_length = command.params.map { |p| p[:name].length }.max
|
32
|
-
command.params.each do |p|
|
33
|
-
puts " %-#{max_param_length}s\t%s" % [p[:name], p[:description]]
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
nil
|
38
|
-
end
|
@@ -1,45 +0,0 @@
|
|
1
|
-
def config(sym)
|
2
|
-
@plugin.config[sym]
|
3
|
-
end
|
4
|
-
|
5
|
-
def description(s)
|
6
|
-
@command.description = s
|
7
|
-
end
|
8
|
-
|
9
|
-
def param(name, options = {})
|
10
|
-
p = {
|
11
|
-
:name => name.to_s,
|
12
|
-
:multi => false,
|
13
|
-
:mandatory => false,
|
14
|
-
:default_param => false
|
15
|
-
}
|
16
|
-
|
17
|
-
if name.is_a? Symbol
|
18
|
-
# parameters whose names are symbols are resolved into entities
|
19
|
-
op = @plugin.op
|
20
|
-
|
21
|
-
entity_names = op.core.state[:entities].map { |entity| entity[:name] }
|
22
|
-
if entity_names.include? name.to_s
|
23
|
-
list_command_name = "list_#{name.to_s.pluralize(42)}"
|
24
|
-
p[:lookup] = lambda do |params|
|
25
|
-
# TODO :name is probably specific to the entity (key?)
|
26
|
-
op.send(list_command_name.to_sym).map { |x| x[:name] }
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
@command.params << p.merge(options)
|
32
|
-
end
|
33
|
-
|
34
|
-
def param!(name, options = {})
|
35
|
-
options.merge! :mandatory => true
|
36
|
-
param(name, options)
|
37
|
-
end
|
38
|
-
|
39
|
-
def show(options = {})
|
40
|
-
column_options = options.delete(:columns)
|
41
|
-
|
42
|
-
raise "unknown keyword #{options.keys.first}" if options.keys.length > 0
|
43
|
-
|
44
|
-
@command.show_options[:columns] = column_options
|
45
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
# TODO remove?
|
2
|
-
def accept_contributions
|
3
|
-
#puts "#{@command.name} accepts contributions"
|
4
|
-
# TODO this init should happen earlier
|
5
|
-
#@op.plugins['core'].state[:contributions] ||= Hash.new { |h,k| h[k] = [] }
|
6
|
-
end
|
7
|
-
|
8
|
-
def contribute(options = {}, &block)
|
9
|
-
target = options[:to] || @command.short_name
|
10
|
-
#puts "#{@command.name} contributes to #{target}"
|
11
|
-
# TODO check that the command exists
|
12
|
-
@op.plugins['core'].state[:contributions][target] << @command.name
|
13
|
-
@command.block = block
|
14
|
-
end
|
15
|
-
|
16
|
-
def with_contributions(options = {}, &block)
|
17
|
-
raise "untested"
|
18
|
-
collected = @op.collect_contributions('name' => command_name, 'raw_params' => params)
|
19
|
-
params_with_contributions = params.merge(:contributions => collected)
|
20
|
-
list = block.call(params_with_contributions)
|
21
|
-
|
22
|
-
found = list.select { |x| x[key.to_sym] == params[key] }
|
23
|
-
if found && found.size > 0
|
24
|
-
Entity.new(@op, command_name, key, found.first)
|
25
|
-
else
|
26
|
-
raise "no such entity : #{params[key]} [#{command_name}]"
|
27
|
-
end
|
28
|
-
end
|
@@ -1,57 +0,0 @@
|
|
1
|
-
require 'vop/entity'
|
2
|
-
require 'pp'
|
3
|
-
|
4
|
-
# if an entity doesn't define a block, by default
|
5
|
-
# it just passes through all contributions
|
6
|
-
def default_entity_block(params)
|
7
|
-
params['contributions']
|
8
|
-
end
|
9
|
-
|
10
|
-
def define_entity(name, key, options)
|
11
|
-
@op.plugins['core'].state[:entities] << {
|
12
|
-
name: name,
|
13
|
-
key: key,
|
14
|
-
options: options
|
15
|
-
}
|
16
|
-
end
|
17
|
-
|
18
|
-
def entity(key, options = {}, &block)
|
19
|
-
command_name = @command.short_name
|
20
|
-
|
21
|
-
# the entity command gets a mandatory param automatically, using the
|
22
|
-
# block defined by the entity as lookup
|
23
|
-
# TODO make the default block work
|
24
|
-
#list_block = block || default_entity_block
|
25
|
-
list_block = block
|
26
|
-
|
27
|
-
if options[:on]
|
28
|
-
param! options[:on]
|
29
|
-
end
|
30
|
-
|
31
|
-
param! key, :lookup => lambda { |params|
|
32
|
-
collected = @op.collect_contributions('name' => command_name, 'raw_params' => params)
|
33
|
-
params_with_contributions = params.merge(:contributions => collected)
|
34
|
-
the_list = list_block.call(params_with_contributions)
|
35
|
-
the_list
|
36
|
-
}
|
37
|
-
|
38
|
-
define_entity(command_name, key, options)
|
39
|
-
|
40
|
-
# entities generally accept contributions...
|
41
|
-
accept_contributions
|
42
|
-
|
43
|
-
# ...and they have a special run block that filters the correct row from
|
44
|
-
# the lookup list and returns an entity populated from that row
|
45
|
-
@command.block = lambda do |params|
|
46
|
-
collected = @op.collect_contributions('name' => command_name, 'raw_params' => params)
|
47
|
-
params_with_contributions = params.merge(:contributions => collected)
|
48
|
-
list = list_block.call(params_with_contributions)
|
49
|
-
|
50
|
-
found = list.select { |x| x[key.to_sym] == params[key] }
|
51
|
-
if found && found.size > 0
|
52
|
-
Entity.new(@op, command_name, key, found.first)
|
53
|
-
else
|
54
|
-
raise "no such entity : #{params[key]} [#{command_name}]"
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
description "returns a list of commands with descriptions"
|
2
|
-
|
3
|
-
param 'plugin', :description => 'list of plugins to filter by',
|
4
|
-
:multi => true,
|
5
|
-
:lookup => lambda { |params| @op.list_plugins.map { |x| x[:name] } },
|
6
|
-
:default_param => true
|
7
|
-
|
8
|
-
run do |params|
|
9
|
-
@op.commands.select do |name, command|
|
10
|
-
not params.has_key?('plugin') or params['plugin'].include?(command.plugin.name)
|
11
|
-
end.map do |name, command|
|
12
|
-
{
|
13
|
-
:name => name,
|
14
|
-
:description => command.description || ''
|
15
|
-
}
|
16
|
-
end
|
17
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
param! 'name'
|
2
|
-
param! 'plugin', :lookup => lambda { @op.list_plugins.map { |x| x[:name] } }
|
3
|
-
|
4
|
-
run do |params|
|
5
|
-
pp params
|
6
|
-
plugin = @op.list_plugins.select { |x| x[:name] == params['plugin'] }.first
|
7
|
-
|
8
|
-
command_file = File.join(plugin[:path], 'commands', "#{params['name']}.rb")
|
9
|
-
File.write(command_file, "run { 42 }")
|
10
|
-
|
11
|
-
@op.reset
|
12
|
-
|
13
|
-
nil
|
14
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
description "initializes a new plugin (folder)"
|
2
|
-
|
3
|
-
param! 'name'
|
4
|
-
param! 'path', :description => 'the path in which to create the new plugin.'
|
5
|
-
|
6
|
-
require 'fileutils'
|
7
|
-
|
8
|
-
run do |params|
|
9
|
-
raise "no such path: #{params['path']}" unless File.exists? params['path']
|
10
|
-
|
11
|
-
# a plugin is a directory
|
12
|
-
plugin_path = File.join(params['path'], params['name'])
|
13
|
-
Dir.mkdir(plugin_path)
|
14
|
-
|
15
|
-
# with subfolders for commands and helpers
|
16
|
-
%w|commands helpers|.each do |thing|
|
17
|
-
Dir.mkdir(File.join(plugin_path, thing))
|
18
|
-
end
|
19
|
-
|
20
|
-
# and a metadata file called '<name>.plugin'
|
21
|
-
plugin_file = params['name'] + '.plugin'
|
22
|
-
FileUtils.touch(File.join(plugin_path, plugin_file))
|
23
|
-
|
24
|
-
@op.reset
|
25
|
-
end
|
@@ -1 +0,0 @@
|
|
1
|
-
dependency :core
|
@@ -1,11 +0,0 @@
|
|
1
|
-
require 'net/scp'
|
2
|
-
|
3
|
-
param! 'machine'
|
4
|
-
param 'user'
|
5
|
-
param! 'local_path'
|
6
|
-
param! 'remote_path'
|
7
|
-
|
8
|
-
run do |machine, local_path, remote_path, params|
|
9
|
-
user = params.has_key?('user') ? params['user'] : ENV['USER']
|
10
|
-
Net::SCP.upload!(params['machine'], user, local_path, remote_path)
|
11
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
require 'net/ssh'
|
2
|
-
|
3
|
-
param! 'machine'
|
4
|
-
param! 'command', :default_param => true
|
5
|
-
param 'user' #, :default => ENV['USER']
|
6
|
-
param 'key_file', :multi => true
|
7
|
-
|
8
|
-
run do |params|
|
9
|
-
machine_name = params['machine']
|
10
|
-
user = params.has_key?('user') ? params['user'] : ENV['USER']
|
11
|
-
|
12
|
-
options = {}
|
13
|
-
if params.has_key? 'key_file'
|
14
|
-
options[:keys] = params['key_file']
|
15
|
-
end
|
16
|
-
Net::SSH.start(machine_name, user, options) do |ssh|
|
17
|
-
ssh.exec! params['command']
|
18
|
-
end
|
19
|
-
end
|
@@ -1 +0,0 @@
|
|
1
|
-
depends :core
|
data/lib/vop/shell/backend.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
# responsible for what should happen when the user interacts with the shell
|
2
|
-
# provides tab completion options and processes the user's input
|
3
|
-
class Backend
|
4
|
-
|
5
|
-
# is called whenever the user submits a command (hitting enter)
|
6
|
-
def process_input(command_line)
|
7
|
-
raise "not implemented in abstract Backend!"
|
8
|
-
end
|
9
|
-
|
10
|
-
# is called whenever the user requests tab completion
|
11
|
-
# should return an array of completion proposals
|
12
|
-
def complete(word)
|
13
|
-
raise "not implemented in abstract Backend!"
|
14
|
-
end
|
15
|
-
|
16
|
-
def prompt
|
17
|
-
">"
|
18
|
-
end
|
19
|
-
|
20
|
-
def process_ctrl_c
|
21
|
-
|
22
|
-
end
|
23
|
-
|
24
|
-
def show_banner
|
25
|
-
|
26
|
-
end
|
27
|
-
|
28
|
-
end
|
data/lib/vop/shell/base_shell.rb
DELETED
@@ -1,112 +0,0 @@
|
|
1
|
-
require 'readline'
|
2
|
-
|
3
|
-
# This class is an abstract implementation of a command shell
|
4
|
-
# It handles command completion and history functions.
|
5
|
-
# For the actual business logic, you need to pass it an implementation of ShellBackend
|
6
|
-
class BaseShell
|
7
|
-
attr_reader :backend
|
8
|
-
|
9
|
-
def initialize(backend)
|
10
|
-
@logger = $logger
|
11
|
-
@backend = backend
|
12
|
-
|
13
|
-
at_exit { console.close }
|
14
|
-
|
15
|
-
trap("INT") {
|
16
|
-
Thread.kill(@thread)
|
17
|
-
@backend.process_ctrl_c
|
18
|
-
}
|
19
|
-
end
|
20
|
-
|
21
|
-
class SimpleConsole
|
22
|
-
def initialize(input = $stdin)
|
23
|
-
@input = input
|
24
|
-
end
|
25
|
-
|
26
|
-
def readline
|
27
|
-
begin
|
28
|
-
line = @input.readline
|
29
|
-
line.chomp! if line
|
30
|
-
line
|
31
|
-
rescue EOFError
|
32
|
-
nil
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def close
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
class ReadlineConsole
|
41
|
-
HISTORY_FILE = ".vop_history"
|
42
|
-
MAX_HISTORY = 200
|
43
|
-
|
44
|
-
def history_path
|
45
|
-
File.join(ENV['HOME'] || ENV['USERPROFILE'], HISTORY_FILE)
|
46
|
-
end
|
47
|
-
|
48
|
-
def initialize(shell)
|
49
|
-
@shell = shell
|
50
|
-
|
51
|
-
if File.exist?(history_path)
|
52
|
-
hist = File.readlines(history_path).map{|line| line.chomp}
|
53
|
-
Readline::HISTORY.push(*hist)
|
54
|
-
end
|
55
|
-
|
56
|
-
#Readline.basic_word_break_characters = " \t\n\\`@><=;|&{([+*%"
|
57
|
-
|
58
|
-
# see http://stackoverflow.com/questions/13876024/how-to-write-a-ruby-command-line-app-that-supports-tab-completion#13876556
|
59
|
-
Readline.completer_word_break_characters = ""
|
60
|
-
|
61
|
-
Readline.completion_append_character = nil
|
62
|
-
Readline.completion_proc = @shell.backend.method(:complete).to_proc
|
63
|
-
end
|
64
|
-
|
65
|
-
def close
|
66
|
-
open(history_path, "wb") do |f|
|
67
|
-
history = Readline::HISTORY.to_a
|
68
|
-
if history.size > MAX_HISTORY
|
69
|
-
history = history[history.size - MAX_HISTORY, MAX_HISTORY]
|
70
|
-
end
|
71
|
-
history.each{|line| f.puts(line)}
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def readline
|
76
|
-
line = Readline.readline(@shell.backend.prompt, true)
|
77
|
-
Readline::HISTORY.pop if /^\s*$/ =~ line
|
78
|
-
line
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def console
|
83
|
-
@console ||= $stdin.tty? ? ReadlineConsole.new(self) : SimpleConsole.new
|
84
|
-
end
|
85
|
-
|
86
|
-
def run
|
87
|
-
backend.show_banner
|
88
|
-
loop do
|
89
|
-
@thread = Thread.new {
|
90
|
-
line = console.readline
|
91
|
-
|
92
|
-
if line
|
93
|
-
backend.process_input line
|
94
|
-
end
|
95
|
-
}
|
96
|
-
begin
|
97
|
-
@thread.join
|
98
|
-
rescue
|
99
|
-
error = $!
|
100
|
-
if error == "exit"
|
101
|
-
Kernel.exit
|
102
|
-
else
|
103
|
-
$stderr.puts "error: >>#{error}<<"
|
104
|
-
$stderr.puts error.backtrace.join("\n")
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
end
|
109
|
-
$stderr.puts "Exiting shell..."
|
110
|
-
end
|
111
|
-
|
112
|
-
end
|