vop 0.3.1 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/Gemfile +2 -1
  4. data/Gemfile.lock +34 -33
  5. data/README.md +184 -2
  6. data/bin/console +17 -0
  7. data/bin/setup +8 -0
  8. data/bin/vop.sh +6 -2
  9. data/exe/vop +3 -25
  10. data/lib/boot.rb +3 -0
  11. data/lib/core/meta/commands/new_command.rb +1 -0
  12. data/lib/core/meta/commands/new_plugin.rb +47 -0
  13. data/lib/core/meta/commands/search_gems_for_plugins.rb +38 -0
  14. data/lib/core/meta/commands/search_path.rb +6 -0
  15. data/lib/core/meta/commands/set.rb +12 -0
  16. data/lib/core/meta/commands/show.rb +6 -0
  17. data/lib/core/meta/commands/show_config.rb +9 -0
  18. data/lib/core/meta/commands/who_provides.rb +6 -0
  19. data/lib/core/meta/meta.plugin +1 -0
  20. data/lib/core/shell/commands/change_loglevel.rb +6 -0
  21. data/lib/{vop/plugins/core → core/shell}/commands/clear_context.rb +0 -0
  22. data/lib/core/shell/commands/edit.rb +12 -0
  23. data/lib/core/shell/commands/help.rb +49 -0
  24. data/lib/core/shell/commands/reset.rb +4 -0
  25. data/lib/{vop/plugins/core → core/shell}/commands/show_context.rb +0 -0
  26. data/lib/core/shell/commands/source.rb +21 -0
  27. data/lib/{vop/plugins/core/helpers/plugin_loader/plugin_syntax.rb → core/shell/shell.plugin} +0 -0
  28. data/lib/core/structure/commands/collect_contributions.rb +46 -0
  29. data/lib/core/structure/commands/disable_contributor.rb +16 -0
  30. data/lib/core/structure/commands/generate_entity_commands.rb +52 -0
  31. data/lib/core/structure/commands/generate_invalidation_commands.rb +26 -0
  32. data/lib/core/structure/commands/list_commands.rb +11 -0
  33. data/lib/core/structure/commands/list_contributors.rb +10 -0
  34. data/lib/core/structure/commands/list_entities.rb +3 -0
  35. data/lib/core/structure/commands/list_plugins.rb +3 -0
  36. data/lib/core/structure/commands/register_contributor.rb +14 -0
  37. data/lib/core/structure/structure.plugin +4 -0
  38. data/lib/vop/objects/chain.rb +25 -0
  39. data/lib/vop/objects/command.rb +86 -0
  40. data/lib/vop/objects/command_param.rb +39 -0
  41. data/lib/vop/objects/entities.rb +21 -0
  42. data/lib/vop/objects/entity.rb +75 -0
  43. data/lib/vop/objects/entity_definition.rb +33 -0
  44. data/lib/vop/objects/filter.rb +48 -0
  45. data/lib/vop/objects/plugin.rb +208 -0
  46. data/lib/vop/objects/request.rb +73 -0
  47. data/lib/vop/objects/response.rb +17 -0
  48. data/lib/vop/objects/thing_with_params.rb +17 -0
  49. data/lib/vop/{command_loader.rb → parts/command_loader.rb} +8 -12
  50. data/lib/vop/parts/dependency_resolver.rb +56 -0
  51. data/lib/vop/parts/entity_loader.rb +46 -0
  52. data/lib/vop/parts/executor.rb +155 -0
  53. data/lib/vop/parts/filter_loader.rb +41 -0
  54. data/lib/vop/parts/plugin_finder.rb +46 -0
  55. data/lib/vop/parts/plugin_loader.rb +72 -0
  56. data/lib/vop/shell/shell.rb +221 -0
  57. data/lib/vop/shell/shell_formatter.rb +110 -0
  58. data/lib/vop/shell/shell_input.rb +14 -0
  59. data/lib/vop/shell/shell_input_readline.rb +20 -0
  60. data/lib/vop/shell/shell_input_testable.rb +27 -0
  61. data/lib/vop/syntax/command_syntax.rb +90 -0
  62. data/lib/vop/syntax/entity_syntax.rb +35 -0
  63. data/lib/vop/syntax/filter_syntax.rb +11 -0
  64. data/lib/vop/syntax/plugin_syntax.rb +55 -0
  65. data/lib/vop/util/errors.rb +45 -0
  66. data/lib/vop/util/pluralizer.rb +26 -0
  67. data/lib/vop/util/worker.rb +24 -0
  68. data/lib/vop/version.rb +1 -1
  69. data/lib/vop/vop.rb +216 -0
  70. data/lib/vop.rb +16 -229
  71. data/vop.gemspec +18 -15
  72. metadata +95 -63
  73. data/bin/vop.rb +0 -28
  74. data/lib/vop/command.rb +0 -168
  75. data/lib/vop/entity.rb +0 -61
  76. data/lib/vop/loader.rb +0 -35
  77. data/lib/vop/plugin.rb +0 -141
  78. data/lib/vop/plugin_loader.rb +0 -88
  79. data/lib/vop/plugins/core/commands/collect_contributions.rb +0 -31
  80. data/lib/vop/plugins/core/commands/edit.rb +0 -12
  81. data/lib/vop/plugins/core/commands/help.rb +0 -38
  82. data/lib/vop/plugins/core/commands/identity.rb +0 -4
  83. data/lib/vop/plugins/core/commands/list_contributors.rb +0 -8
  84. data/lib/vop/plugins/core/commands/list_entities.rb +0 -3
  85. data/lib/vop/plugins/core/commands/pry.rb +0 -9
  86. data/lib/vop/plugins/core/commands/reset.rb +0 -5
  87. data/lib/vop/plugins/core/commands/source.rb +0 -5
  88. data/lib/vop/plugins/core/commands/system_call.rb +0 -5
  89. data/lib/vop/plugins/core/core.plugin +0 -4
  90. data/lib/vop/plugins/core/helpers/command_loader/command_syntax.rb +0 -45
  91. data/lib/vop/plugins/core/helpers/command_loader/contributions.rb +0 -28
  92. data/lib/vop/plugins/core/helpers/command_loader/entities.rb +0 -57
  93. data/lib/vop/plugins/core/helpers/helper.rb +0 -3
  94. data/lib/vop/plugins/meta/commands/add_search_path.rb +0 -6
  95. data/lib/vop/plugins/meta/commands/delete_plugin.rb +0 -13
  96. data/lib/vop/plugins/meta/commands/list_commands.rb +0 -17
  97. data/lib/vop/plugins/meta/commands/list_plugins.rb +0 -8
  98. data/lib/vop/plugins/meta/commands/new_command.rb +0 -14
  99. data/lib/vop/plugins/meta/commands/new_plugin.rb +0 -25
  100. data/lib/vop/plugins/meta/commands/show_search_path.rb +0 -3
  101. data/lib/vop/plugins/meta/commands/who_provides.rb +0 -5
  102. data/lib/vop/plugins/meta/meta.plugin +0 -1
  103. data/lib/vop/plugins/ssh/commands/scp.rb +0 -11
  104. data/lib/vop/plugins/ssh/commands/ssh.rb +0 -19
  105. data/lib/vop/plugins/ssh/ssh.plugin +0 -1
  106. data/lib/vop/shell/backend.rb +0 -28
  107. data/lib/vop/shell/base_shell.rb +0 -112
  108. data/lib/vop/shell/formatter.rb +0 -46
  109. data/lib/vop/shell/vop_shell_backend.rb +0 -257
  110. data/lib/vop/shell.rb +0 -52
@@ -0,0 +1,73 @@
1
+ require_relative "response"
2
+ require_relative "chain"
3
+ require_relative "../parts/executor"
4
+
5
+ module Vop
6
+
7
+ class Request
8
+
9
+ attr_reader :command_name, :param_values, :extra
10
+ attr_accessor :shell
11
+
12
+ def initialize(op, command_name, param_values = {}, extra = {})
13
+ @op = op
14
+ @command_name = command_name
15
+ raise "unknown command '#{command_name}'" if command.nil?
16
+ @param_values = param_values
17
+ @extra = extra
18
+ @shell = nil
19
+
20
+ @current_filter = nil
21
+ @filter_chain = @op.filter_chain.clone
22
+ end
23
+
24
+ def command
25
+ @op.commands[@command_name]
26
+ end
27
+
28
+ def cache_key
29
+ blacklist = %w|shell raw_params|
30
+
31
+ ex = Executor.new(@op)
32
+ prepared = ex.prepare_params(self)
33
+
34
+ param_string = prepared.map { |k,v|
35
+ next if blacklist.include? k
36
+ [k.to_s,v].join("=")
37
+ }.sort.compact.join(":")
38
+ "vop/request:#{command.name}:" + param_string + ":v1"
39
+ end
40
+
41
+ def self.from_json(op, json)
42
+ hash = JSON.parse(json)
43
+ self.new(op, hash["command"], hash["params"], hash["extra"])
44
+ end
45
+
46
+ def to_json
47
+ {
48
+ command: @command_name,
49
+ params: @param_values,
50
+ extra: @extra
51
+ }.to_json
52
+ end
53
+
54
+ def next_filter
55
+ @chain.next()
56
+ end
57
+
58
+ def execute
59
+ result = nil
60
+ context = nil
61
+
62
+ # build a chain out of all filters + the command itself
63
+ filter_chain = @op.filter_chain.clone.map {
64
+ |filter_name| @op.filters[filter_name.split(".").first]
65
+ }
66
+ filter_chain << Executor.new(@op)
67
+ @chain = Chain.new(@op, filter_chain)
68
+ @chain.execute(self)
69
+ end
70
+
71
+ end
72
+
73
+ end
@@ -0,0 +1,17 @@
1
+ module Vop
2
+
3
+ class Response
4
+
5
+ attr_reader :result, :context, :timestamp
6
+ attr_accessor :status
7
+
8
+ def initialize(result, context, timestamp = nil)
9
+ @result = result
10
+ @context = context
11
+ @timestamp = timestamp || Time.now
12
+ @status = "ok"
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,17 @@
1
+ module Vop
2
+
3
+ class ThingWithParams
4
+
5
+ attr_reader :params
6
+
7
+ def initialize
8
+ @params = []
9
+ end
10
+
11
+ def param(name)
12
+ @params.select { |x| x.name == name }.first
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -1,4 +1,5 @@
1
- require 'vop/command'
1
+ require_relative "../syntax/command_syntax"
2
+ require_relative "../objects/command"
2
3
 
3
4
  module Vop
4
5
 
@@ -8,24 +9,19 @@ module Vop
8
9
  @plugin = plugin
9
10
  @op = plugin.op
10
11
 
11
- @commands = {}
12
+ @commands = []
12
13
 
13
- # we need to load both the general helpers of the plugin because they are
14
- # used inside commands as well as the helpers specific to the command_loader
15
14
  @plugin.inject_helpers(self)
16
- @plugin.inject_helpers(self, 'command_loader')
15
+
16
+ extend CommandSyntax
17
17
  end
18
18
 
19
19
  def new_command(name)
20
20
  @command = Command.new(@plugin, name)
21
- @commands[@command.name] = @command
21
+ @commands << @command
22
22
  @command
23
23
  end
24
24
 
25
- def run(&block)
26
- @command.block = block
27
- end
28
-
29
25
  def read_sources(named_commands)
30
26
  # reads a hash of <command_name> => <source string>
31
27
  named_commands.each do |name, source|
@@ -34,8 +30,8 @@ module Vop
34
30
 
35
31
  begin
36
32
  self.instance_eval(source[:code], source[:file_name])
37
- rescue => detail
38
- raise "problem loading plugin #{name} : #{detail.message}\n#{detail.backtrace.join("\n")}"
33
+ rescue Exception
34
+ raise Errors::CommandLoadError.new("problem loading command #{name}")
39
35
  end
40
36
  end
41
37
 
@@ -0,0 +1,56 @@
1
+ module Vop
2
+
3
+ class DependencyResolver
4
+
5
+ def initialize(op)
6
+ @op = op
7
+ end
8
+
9
+ def sort(plugins)
10
+ @plugins = {}
11
+ plugins.each do |plugin|
12
+ @plugins[plugin.name] = plugin
13
+ end
14
+
15
+ resolved = []
16
+ unresolved = []
17
+
18
+ root_plugin = Plugin.new(@op, "__root__", nil)
19
+ root_plugin.dependencies = @plugins.keys
20
+
21
+ $logger.debug "root dummy : #{root_plugin}"
22
+
23
+ resolve(root_plugin, resolved, unresolved)
24
+ resolved.delete_if { |x| x == root_plugin.name }
25
+
26
+ resolved.map { |x| @plugins[x] }
27
+ end
28
+
29
+ def resolve(plugin, resolved, unresolved, level = 0)
30
+ $logger.debug "#{' ' * level}checking dependencies for #{plugin.name}"
31
+ unresolved << plugin.name
32
+
33
+ plugin.dependencies.each do |dep|
34
+ $logger.debug "#{' ' * level}resolving #{dep}"
35
+ already_loaded = @op.plugins.map(&:name).include? dep
36
+ unless already_loaded
37
+ unless resolved.include? dep
38
+ if unresolved.include? dep
39
+ raise ::Vop::Errors::RunningInCircles, "circular dependency #{plugin.name} -> #{dep}"
40
+ else
41
+ unless @plugins.has_key? dep
42
+ raise ::Vop::Errors::MissingPlugin, "dependency not met: #{plugin.name} depends on #{dep}"
43
+ end
44
+ dependency = @plugins[dep]
45
+ resolve(dependency, resolved, unresolved, level + 1)
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ resolved << plugin.name
52
+ end
53
+
54
+ end
55
+
56
+ end
@@ -0,0 +1,46 @@
1
+ require_relative "../syntax/entity_syntax"
2
+ require_relative "../objects/entity_definition"
3
+
4
+ module Vop
5
+
6
+ class EntityLoader
7
+
8
+ def initialize(plugin)
9
+ @plugin = plugin
10
+ @op = plugin.op
11
+
12
+ @loaded = []
13
+
14
+ @plugin.inject_helpers(self)
15
+
16
+ #@command = @entity
17
+ #extend CommandSyntax
18
+ extend EntitySyntax
19
+ end
20
+
21
+ def prepare(name)
22
+ @entity = EntityDefinition.new(@plugin, name)
23
+ @command = @entity
24
+ @loaded << @entity
25
+ @entity
26
+ end
27
+
28
+ def read_sources(named_sources)
29
+ # reads a hash of <name> => <source string>
30
+ named_sources.each do |name, source|
31
+
32
+ prepare(name)
33
+
34
+ begin
35
+ self.instance_eval(source[:code], source[:file_name])
36
+ rescue SyntaxError => detail
37
+ raise Errors::EntityLoadError.new("problem loading entity #{name} : #{detail.message}")
38
+ end
39
+ end
40
+
41
+ @loaded
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -0,0 +1,155 @@
1
+ module Vop
2
+
3
+ class Executor
4
+
5
+ def initialize(vop)
6
+ @op = vop
7
+ end
8
+
9
+ # accepts arguments as handed in by :define_method and prepares them
10
+ # into the +params+ structure expected by command blocks
11
+ def prepare_params(request)
12
+ (ruby_args, extra) = request.param_values, request.extra
13
+ result = {}
14
+ if ruby_args
15
+ if ruby_args.is_a? Hash
16
+ result = ruby_args
17
+ else
18
+ # if there is a default param, it can be passed to execute as "scalar"
19
+ # param, but it will be converted into a "normal" named param
20
+ dp = request.command.default_param
21
+ if dp
22
+ result = {
23
+ dp.name => ruby_args
24
+ }
25
+ end
26
+ end
27
+ end
28
+
29
+ if extra.keys.size > 0
30
+ extra.each do |k,v|
31
+ param = request.command.param(k)
32
+ # TODO actually, this is not always context - it's used from the entities as well
33
+ if param && param.wants_context
34
+ result[k] = extra[k]
35
+ end
36
+ end
37
+ end
38
+
39
+ # add in defaults (for all params that have not been specified)
40
+ request.command.params.each do |p|
41
+ param_name = p.name.to_sym
42
+ unless result.has_key? param_name
43
+ if p.options.has_key? :default
44
+ result[param_name] = p.options[:default]
45
+ end
46
+ end
47
+ end
48
+
49
+ result.each do |k,v|
50
+ param = request.command.param(k.to_s)
51
+ if param.nil? && ! request.command.allows_extra
52
+ raise "no such param #{k.to_s} (in #{request.command.name})"
53
+ end
54
+ p = param && param.options
55
+ if p
56
+ # values are auto-boxed into an array if the param expects multiple values
57
+ if p[:multi] && ! v.is_a?(Array) then
58
+ v = [ v ]
59
+ # array values are auto-unboxed if the param does not want multi
60
+ elsif ! p[:multi] && v.is_a?(Array) && v.length == 1
61
+ v = v.first
62
+ end
63
+
64
+ # convert booleans
65
+ if p[:boolean] && ! v.nil?
66
+ unless [true, false].include? v
67
+ #$logger.debug("converting #{param.name} (#{v}) into boolean")
68
+ v = !! /[tT]rue|[yY]es|[oO]n/.match(v)
69
+ end
70
+ end
71
+ end
72
+ result[k] = v
73
+ end
74
+
75
+ result
76
+ end
77
+
78
+ def prepare_payload(request, context, block_param_names)
79
+ payload = []
80
+
81
+ prepared = prepare_params(request)
82
+ param_names = request.command.params.map { |x| x.name }
83
+
84
+ block_param_names.each do |name|
85
+ param = nil
86
+
87
+ case name.to_s
88
+ when "params"
89
+ param = prepared
90
+ when "plugin"
91
+ param = request.command.plugin
92
+ when "command"
93
+ param = request.command
94
+ when "request"
95
+ param = request
96
+ when "context"
97
+ param = context
98
+ when "shell"
99
+ raise "shell not supported" if request.shell.nil?
100
+ param = request.shell
101
+ else
102
+ if prepared.has_key? name.to_s
103
+ param = prepared[name.to_s]
104
+ elsif prepared.has_key? name
105
+ param = prepared[name]
106
+ else
107
+ unless param_names.include? name.to_s
108
+ raise "unknown block param name : >>#{name}<<"
109
+ end
110
+ end
111
+ end
112
+
113
+ unless param.nil?
114
+ command_param = request.command.param(name.to_s)
115
+ if command_param && command_param.options[:entity]
116
+ # auto-inflate entities
117
+ entity_list = @op.entities.values
118
+ entity = entity_list.select { |x| x.short_name == name.to_s }.first
119
+
120
+ unless entity.nil?
121
+ #$logger.debug "auto-inflating entity #{name.to_s} (#{param})"
122
+
123
+ list_command_name = entity.short_name.carefully_pluralize
124
+ the_list = @op.execute(list_command_name, {})
125
+ #$logger.debug "inflated entity list : #{the_list.size} entities"
126
+ param = the_list.select { |x| x[entity.key] == param }.first
127
+ end
128
+ end
129
+
130
+ payload << param
131
+ end
132
+ end
133
+
134
+ payload
135
+ end
136
+
137
+ def execute(request)
138
+ blacklist = %w|list_contributors collect_contributions machines rails_machines|
139
+ unless blacklist.include? request.command.short_name
140
+ $logger.debug "+++ #{request.command.short_name} (#{request.param_values}) +++"
141
+ end
142
+ command = request.command
143
+
144
+ context = {}
145
+ block_param_names = request.command.block.parameters.map { |x| x.last }
146
+ payload = prepare_payload(request, context, block_param_names)
147
+ result = command.execute(payload)
148
+
149
+ Response.new(result, context)
150
+ end
151
+
152
+ end
153
+
154
+
155
+ end
@@ -0,0 +1,41 @@
1
+ require_relative "../syntax/filter_syntax"
2
+ require_relative "../objects/filter"
3
+
4
+ module Vop
5
+
6
+ class FilterLoader
7
+
8
+ def initialize(plugin)
9
+ @plugin = plugin
10
+ @op = @plugin.op
11
+
12
+ @filters = []
13
+
14
+ @plugin.inject_helpers(self)
15
+ extend FilterSyntax
16
+ end
17
+
18
+ def new_filter(name)
19
+ @filter = Filter.new(@plugin, name)
20
+ @filters << @filter
21
+ @filter
22
+ end
23
+
24
+ def read_sources(named_sources)
25
+ # reads a hash of <name> => <source string>
26
+ named_sources.each do |name, source|
27
+ new_filter(name)
28
+
29
+ begin
30
+ self.instance_eval(source[:code], source[:file_name])
31
+ rescue => detail
32
+ raise Errors::LoadError.new("problem loading filter #{name}", detail)
33
+ end
34
+ end
35
+
36
+ @filters
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1,46 @@
1
+ require "pathname"
2
+
3
+ module Vop
4
+
5
+ class PluginFinder
6
+
7
+ attr_reader :plugins, :templates
8
+
9
+ def initialize
10
+ reset
11
+ end
12
+
13
+ def reset
14
+ @plugins = []
15
+ @templates = []
16
+ end
17
+
18
+ def find(paths)
19
+ reset
20
+
21
+ $logger.debug "scanning #{paths} for plugins..."
22
+ paths = [ paths ] unless paths.is_a? Array
23
+
24
+ if paths.size > 0
25
+ paths.each do |path|
26
+ begin
27
+ next unless File.exists? path
28
+ rescue => e
29
+ if e.message =~ /Fixnum/
30
+ $logger.warn "unexpected Fixnum path : #{path}"
31
+ end
32
+ raise e
33
+ end
34
+
35
+
36
+ @plugins += Dir.glob("#{path}/**/*.plugin").map { |x| Pathname.new(File.dirname(x)).realpath.to_s }
37
+ @templates += Dir.glob("#{path}/**/plugin.vop").map { |x| Pathname.new(x).realpath.to_s }
38
+ end
39
+ end
40
+
41
+ self
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -0,0 +1,72 @@
1
+ require_relative "../objects/plugin"
2
+ require_relative "../syntax/plugin_syntax"
3
+ require_relative "../util/errors"
4
+
5
+ module Vop
6
+
7
+ class PluginLoader
8
+
9
+ attr_reader :loaded
10
+
11
+ def initialize(op)
12
+ @op = op
13
+
14
+ extend PluginSyntax
15
+ end
16
+
17
+ def reset
18
+ @loaded = []
19
+ end
20
+
21
+ def new_plugin(plugin_name, plugin_path, plugin_options = {})
22
+ @plugin = Plugin.new(@op, plugin_name, plugin_path, plugin_options)
23
+ @plugin
24
+ end
25
+
26
+ def read_plugin(code, source_file = nil)
27
+ begin
28
+ instance_eval(code, source_file)
29
+ rescue => detail
30
+ $logger.warn "problem loading plugin #{@plugin.name} : #{detail.message}\n#{detail.backtrace.join("\n")}"
31
+ raise Errors::PluginLoadError.new(detail)
32
+ end
33
+ end
34
+
35
+ def load(found, plugin_options = {})
36
+ reset
37
+
38
+ (plugins, templates) = [found.plugins, found.templates]
39
+
40
+ plugins.each do |plugin_path|
41
+ name = File.basename(plugin_path)
42
+
43
+ $logger.debug "loading #{name} from #{plugin_path}"
44
+ plugin = new_plugin(name, plugin_path, plugin_options)
45
+
46
+ templates.each do |template|
47
+ template_path = File.dirname(template)
48
+ if plugin_path.start_with? template_path
49
+ $logger.debug " (applying template #{template_path})"
50
+ template_file = File.join(template_path, "plugin.vop")
51
+ code = File.read(template_file)
52
+ read_plugin(code, template_file)
53
+ end
54
+ end
55
+
56
+ plugin_file = File.join(plugin_path, "#{name}.plugin")
57
+ next unless File.exists?(plugin_file)
58
+ $logger.debug "reading plugin '#{name}' from '#{plugin_file}'"
59
+
60
+ code = File.read(plugin_file)
61
+ read_plugin(code, plugin_file)
62
+ @loaded << @plugin
63
+
64
+ $logger.debug "loaded plugin #{@plugin.name}"
65
+ end
66
+
67
+ self
68
+ end
69
+
70
+ end
71
+
72
+ end