vop 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +50 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +66 -0
  5. data/README.md +2 -0
  6. data/Rakefile +6 -0
  7. data/bin/vop.rb +28 -0
  8. data/bin/vop.sh +4 -0
  9. data/exe/vop +28 -0
  10. data/lib/vop.rb +242 -0
  11. data/lib/vop/command.rb +168 -0
  12. data/lib/vop/command_loader.rb +47 -0
  13. data/lib/vop/entity.rb +61 -0
  14. data/lib/vop/loader.rb +35 -0
  15. data/lib/vop/plugin.rb +141 -0
  16. data/lib/vop/plugin_loader.rb +88 -0
  17. data/lib/vop/plugins/core/commands/clear_context.rb +3 -0
  18. data/lib/vop/plugins/core/commands/collect_contributions.rb +31 -0
  19. data/lib/vop/plugins/core/commands/edit.rb +12 -0
  20. data/lib/vop/plugins/core/commands/help.rb +38 -0
  21. data/lib/vop/plugins/core/commands/identity.rb +4 -0
  22. data/lib/vop/plugins/core/commands/list_contributors.rb +8 -0
  23. data/lib/vop/plugins/core/commands/list_entities.rb +3 -0
  24. data/lib/vop/plugins/core/commands/pry.rb +9 -0
  25. data/lib/vop/plugins/core/commands/reset.rb +5 -0
  26. data/lib/vop/plugins/core/commands/show_context.rb +3 -0
  27. data/lib/vop/plugins/core/commands/source.rb +5 -0
  28. data/lib/vop/plugins/core/commands/system_call.rb +5 -0
  29. data/lib/vop/plugins/core/core.plugin +4 -0
  30. data/lib/vop/plugins/core/helpers/command_loader/command_syntax.rb +45 -0
  31. data/lib/vop/plugins/core/helpers/command_loader/contributions.rb +28 -0
  32. data/lib/vop/plugins/core/helpers/command_loader/entities.rb +57 -0
  33. data/lib/vop/plugins/core/helpers/helper.rb +3 -0
  34. data/lib/vop/plugins/core/helpers/plugin_loader/plugin_syntax.rb +0 -0
  35. data/lib/vop/plugins/meta/commands/add_search_path.rb +6 -0
  36. data/lib/vop/plugins/meta/commands/delete_plugin.rb +13 -0
  37. data/lib/vop/plugins/meta/commands/list_commands.rb +17 -0
  38. data/lib/vop/plugins/meta/commands/list_plugins.rb +8 -0
  39. data/lib/vop/plugins/meta/commands/new_command.rb +14 -0
  40. data/lib/vop/plugins/meta/commands/new_plugin.rb +25 -0
  41. data/lib/vop/plugins/meta/commands/show_search_path.rb +3 -0
  42. data/lib/vop/plugins/meta/commands/who_provides.rb +5 -0
  43. data/lib/vop/plugins/meta/meta.plugin +1 -0
  44. data/lib/vop/plugins/ssh/commands/scp.rb +11 -0
  45. data/lib/vop/plugins/ssh/commands/ssh.rb +19 -0
  46. data/lib/vop/plugins/ssh/ssh.plugin +1 -0
  47. data/lib/vop/shell.rb +52 -0
  48. data/lib/vop/shell/backend.rb +28 -0
  49. data/lib/vop/shell/base_shell.rb +112 -0
  50. data/lib/vop/shell/formatter.rb +46 -0
  51. data/lib/vop/shell/vop_shell_backend.rb +257 -0
  52. data/lib/vop/version.rb +3 -0
  53. data/vop.gemspec +31 -0
  54. 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,3 @@
1
+ run do |shell|
2
+ shell.context.clear
3
+ 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,4 @@
1
+ run do
2
+ foo()
3
+ config(:identity) || 'localhost'
4
+ end
@@ -0,0 +1,8 @@
1
+ param! "name"
2
+
3
+ run do |name|
4
+ registry = @op.plugins["core"].state[:contributions]
5
+ if registry.has_key? name
6
+ registry[name]
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ run do
2
+ @plugin.state[:entities].map { |x| x[:name] }
3
+ end
@@ -0,0 +1,9 @@
1
+ require "pry"
2
+
3
+ run do |params|
4
+ @op._pry
5
+ # pry messes up the terminal completion setup by the vop shell backend,
6
+ # so currently this is a one-call command:
7
+ @op.system_call("bin/vop.sh")
8
+ Kernel.exit(42)
9
+ end
@@ -0,0 +1,5 @@
1
+ description "reloads plugins and commands"
2
+
3
+ run do
4
+ @op._reset
5
+ end
@@ -0,0 +1,3 @@
1
+ run do |shell|
2
+ shell.context
3
+ end
@@ -0,0 +1,5 @@
1
+ param! "name", :lookup => lambda { |_| @op.list_commands.map { |x| x[:name] } }
2
+
3
+ run do |params, name|
4
+ @op.command(name).source[:code]
5
+ end
@@ -0,0 +1,5 @@
1
+ param! "command"
2
+
3
+ run do |command|
4
+ `#{command}`
5
+ end
@@ -0,0 +1,4 @@
1
+ on :init do |plugin|
2
+ plugin.state[:entities] ||= []
3
+ plugin.state[:contributions] ||= Hash.new { |h,k| h[k] = [] }
4
+ 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
@@ -0,0 +1,3 @@
1
+ def foo
2
+ puts "call for mr. beeblebrox!"
3
+ end
@@ -0,0 +1,6 @@
1
+ param! 'path'
2
+
3
+ run do |params|
4
+ @op.config[:search_path] << params['path']
5
+ @op.reset
6
+ end
@@ -0,0 +1,13 @@
1
+ require 'fileutils'
2
+
3
+ param! 'name'
4
+
5
+ run do |params|
6
+ name = params['name']
7
+
8
+ plugin = @op.plugins[name]
9
+ raise "no such plugin : #{name}" unless plugin
10
+
11
+ FileUtils.rm_r plugin.path
12
+ @op.reset
13
+ end
@@ -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,8 @@
1
+ run do
2
+ @op.plugins.map do |name, plugin|
3
+ {
4
+ :name => name,
5
+ :path => plugin.path
6
+ }
7
+ end
8
+ 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