vop 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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