vop 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +50 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +66 -0
- data/README.md +2 -0
- data/Rakefile +6 -0
- data/bin/vop.rb +28 -0
- data/bin/vop.sh +4 -0
- data/exe/vop +28 -0
- data/lib/vop.rb +242 -0
- data/lib/vop/command.rb +168 -0
- data/lib/vop/command_loader.rb +47 -0
- data/lib/vop/entity.rb +61 -0
- data/lib/vop/loader.rb +35 -0
- data/lib/vop/plugin.rb +141 -0
- data/lib/vop/plugin_loader.rb +88 -0
- data/lib/vop/plugins/core/commands/clear_context.rb +3 -0
- data/lib/vop/plugins/core/commands/collect_contributions.rb +31 -0
- data/lib/vop/plugins/core/commands/edit.rb +12 -0
- data/lib/vop/plugins/core/commands/help.rb +38 -0
- data/lib/vop/plugins/core/commands/identity.rb +4 -0
- data/lib/vop/plugins/core/commands/list_contributors.rb +8 -0
- data/lib/vop/plugins/core/commands/list_entities.rb +3 -0
- data/lib/vop/plugins/core/commands/pry.rb +9 -0
- data/lib/vop/plugins/core/commands/reset.rb +5 -0
- data/lib/vop/plugins/core/commands/show_context.rb +3 -0
- data/lib/vop/plugins/core/commands/source.rb +5 -0
- data/lib/vop/plugins/core/commands/system_call.rb +5 -0
- data/lib/vop/plugins/core/core.plugin +4 -0
- data/lib/vop/plugins/core/helpers/command_loader/command_syntax.rb +45 -0
- data/lib/vop/plugins/core/helpers/command_loader/contributions.rb +28 -0
- data/lib/vop/plugins/core/helpers/command_loader/entities.rb +57 -0
- data/lib/vop/plugins/core/helpers/helper.rb +3 -0
- data/lib/vop/plugins/core/helpers/plugin_loader/plugin_syntax.rb +0 -0
- data/lib/vop/plugins/meta/commands/add_search_path.rb +6 -0
- data/lib/vop/plugins/meta/commands/delete_plugin.rb +13 -0
- data/lib/vop/plugins/meta/commands/list_commands.rb +17 -0
- data/lib/vop/plugins/meta/commands/list_plugins.rb +8 -0
- data/lib/vop/plugins/meta/commands/new_command.rb +14 -0
- data/lib/vop/plugins/meta/commands/new_plugin.rb +25 -0
- data/lib/vop/plugins/meta/commands/show_search_path.rb +3 -0
- data/lib/vop/plugins/meta/commands/who_provides.rb +5 -0
- data/lib/vop/plugins/meta/meta.plugin +1 -0
- data/lib/vop/plugins/ssh/commands/scp.rb +11 -0
- data/lib/vop/plugins/ssh/commands/ssh.rb +19 -0
- data/lib/vop/plugins/ssh/ssh.plugin +1 -0
- data/lib/vop/shell.rb +52 -0
- data/lib/vop/shell/backend.rb +28 -0
- data/lib/vop/shell/base_shell.rb +112 -0
- data/lib/vop/shell/formatter.rb +46 -0
- data/lib/vop/shell/vop_shell_backend.rb +257 -0
- data/lib/vop/version.rb +3 -0
- data/vop.gemspec +31 -0
- metadata +223 -0
@@ -0,0 +1,88 @@
|
|
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
|
@@ -0,0 +1,31 @@
|
|
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
|
@@ -0,0 +1,12 @@
|
|
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
|
@@ -0,0 +1,38 @@
|
|
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
|
@@ -0,0 +1,45 @@
|
|
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
|
@@ -0,0 +1,28 @@
|
|
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
|
@@ -0,0 +1,57 @@
|
|
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
|
File without changes
|
@@ -0,0 +1,17 @@
|
|
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
|
@@ -0,0 +1,14 @@
|
|
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
|