vop 0.3.1 → 0.3.4
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.
- 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
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
param! "command_name", "command for which a contributor should be disabled"
|
|
2
|
+
param! "contributor", "name of the contributor to disable"
|
|
3
|
+
|
|
4
|
+
run do |plugin, command_name, contributor|
|
|
5
|
+
registry = plugin.state[:contributions] || {}
|
|
6
|
+
unless registry.has_key? command_name
|
|
7
|
+
raise "no contributors found for command #{command_name}"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
contributors = registry[command_name]
|
|
11
|
+
unless contributors.include? contributor
|
|
12
|
+
raise "no contributor found with name #{contributor} for command #{command_name}"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
contributors.delete(contributor)
|
|
16
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
description "each entity gets a command called <entities> automagically"
|
|
2
|
+
|
|
3
|
+
run do
|
|
4
|
+
result = []
|
|
5
|
+
|
|
6
|
+
@op.entities.each do |entity_name, definition|
|
|
7
|
+
list_command_name = definition.name.carefully_pluralize
|
|
8
|
+
$logger.debug "generating entity list command #{list_command_name} (#{definition.plugin.name})"
|
|
9
|
+
|
|
10
|
+
plugin = definition.plugin
|
|
11
|
+
|
|
12
|
+
list_command = Command.new(plugin, list_command_name)
|
|
13
|
+
# TODO list_command.read_only = true
|
|
14
|
+
|
|
15
|
+
if definition.on
|
|
16
|
+
list_command.add_param(definition.on.to_s, mandatory: true)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
list_command.block = lambda do |params, request, context, plugin|
|
|
20
|
+
ex = Executor.new(@op)
|
|
21
|
+
block_param_names = definition.block.parameters.map { |x| x.last }
|
|
22
|
+
payload = ex.prepare_payload(request, context, block_param_names)
|
|
23
|
+
|
|
24
|
+
hash_array = definition.block.call(*payload)
|
|
25
|
+
if hash_array.nil?
|
|
26
|
+
$logger.warn "list block of entity '#{definition.name}' returned nil (?!)"
|
|
27
|
+
else
|
|
28
|
+
unless hash_array.is_a? Array
|
|
29
|
+
raise "unexpected data type : found #{hash_array.class}, expected Array"
|
|
30
|
+
end
|
|
31
|
+
entity_array = []
|
|
32
|
+
unless hash_array.empty?
|
|
33
|
+
first = hash_array.first
|
|
34
|
+
unless first.is_a? Hash
|
|
35
|
+
raise "entity '#{definition.name}' returned unexpected data type : found #{first.class}, expected Hash"
|
|
36
|
+
end
|
|
37
|
+
entity_array = hash_array.map do |row|
|
|
38
|
+
Entity.new(@op, definition.short_name, definition.key, row)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# wrap the resulting array of entities to add syntactic sugar
|
|
43
|
+
::Vop::Entities.new(entity_array)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
result << list_command
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
@op << result
|
|
50
|
+
|
|
51
|
+
result
|
|
52
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
description "all read-only commands get <command>! commands that invalidate automagically"
|
|
2
|
+
|
|
3
|
+
run do
|
|
4
|
+
count = 0
|
|
5
|
+
@op.commands.values.each do |command|
|
|
6
|
+
if command.read_only
|
|
7
|
+
invalidation_command_name = "#{command.short_name}!"
|
|
8
|
+
$logger.debug "generating invalidation command #{invalidation_command_name}"
|
|
9
|
+
|
|
10
|
+
invalidation_command = Command.new(command.plugin, invalidation_command_name)
|
|
11
|
+
invalidation_command.params = command.params
|
|
12
|
+
|
|
13
|
+
invalidation_command.block = lambda do |params|
|
|
14
|
+
@op.invalidate_cache(
|
|
15
|
+
"command" => command.short_name,
|
|
16
|
+
"raw_params" => params
|
|
17
|
+
)
|
|
18
|
+
@op.execute(command.short_name, params)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
@op << invalidation_command
|
|
22
|
+
count += 1
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
count
|
|
26
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
param "plugin_filter", description: "name of a plugin by which commands should be filtered"
|
|
2
|
+
|
|
3
|
+
run do |plugin_filter|
|
|
4
|
+
result = @op.commands.values
|
|
5
|
+
|
|
6
|
+
unless plugin_filter.nil?
|
|
7
|
+
result.delete_if { |command| command.plugin.name != plugin_filter }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
result.map { |command| command.name }.sort
|
|
11
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
param! "command_name", description: "the target of the contribution"
|
|
2
|
+
param! "contributor", description: "the command that contributes"
|
|
3
|
+
|
|
4
|
+
run do |command_name, contributor, plugin|
|
|
5
|
+
# TODO verify that +command+ exists - cannot do it here because of
|
|
6
|
+
# 'machines.rails_machines' tries to contribute to unknown command 'machine'
|
|
7
|
+
# unless @op.commands.keys.include? command
|
|
8
|
+
# raise "'#{contributor}' tries to contribute to unknown command '#{command}'"
|
|
9
|
+
# end
|
|
10
|
+
|
|
11
|
+
# TODO initialize in init_hook (not here and in list_contributors)
|
|
12
|
+
registry = plugin.state[:contributions] ||= Hash.new { |h,k| h[k] = [] }
|
|
13
|
+
registry[command_name] << contributor
|
|
14
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Vop
|
|
2
|
+
|
|
3
|
+
class Chain
|
|
4
|
+
|
|
5
|
+
attr_reader :links
|
|
6
|
+
|
|
7
|
+
def initialize(op, links)
|
|
8
|
+
@op = op
|
|
9
|
+
@links = links
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def next
|
|
13
|
+
@links.shift
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def execute(request)
|
|
17
|
+
next_link = self.next
|
|
18
|
+
response = nil
|
|
19
|
+
response = next_link.execute(request) if next_link
|
|
20
|
+
response
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
require_relative "command_param"
|
|
2
|
+
|
|
3
|
+
module Vop
|
|
4
|
+
|
|
5
|
+
class Command
|
|
6
|
+
|
|
7
|
+
attr_accessor :plugin
|
|
8
|
+
attr_accessor :name
|
|
9
|
+
|
|
10
|
+
attr_accessor :block
|
|
11
|
+
attr_accessor :params
|
|
12
|
+
attr_accessor :description
|
|
13
|
+
|
|
14
|
+
attr_accessor :show_options
|
|
15
|
+
attr_accessor :dont_register
|
|
16
|
+
attr_accessor :read_only
|
|
17
|
+
attr_accessor :allows_extra
|
|
18
|
+
|
|
19
|
+
def initialize(plugin, name)
|
|
20
|
+
@plugin = plugin
|
|
21
|
+
@name = name
|
|
22
|
+
@description = nil
|
|
23
|
+
|
|
24
|
+
@block = lambda { |params| $logger.warn "#{name} not yet implemented!" }
|
|
25
|
+
|
|
26
|
+
@params = []
|
|
27
|
+
@show_options = {}
|
|
28
|
+
|
|
29
|
+
@dont_register = false
|
|
30
|
+
@read_only = false
|
|
31
|
+
@allows_extra = false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def short_name
|
|
35
|
+
@name.split(".").last
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def source
|
|
39
|
+
plugin.sources[:commands][name]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def param(name)
|
|
43
|
+
@params.select { |x| x.name == name }.first
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def add_param(name, options = {})
|
|
47
|
+
@params << CommandParam.new(name, options)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def params_with(&filter)
|
|
51
|
+
params.select do |param|
|
|
52
|
+
filter.call(param)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def mandatory_params
|
|
57
|
+
params_with { |x| x.options[:mandatory] == true }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# The default param is the one used when a command is called with a single "scalar" param only, like
|
|
61
|
+
# @op.foo("zaphod")
|
|
62
|
+
# If a parameter is marked as default, it will be assigned the value "zaphod" in this case.
|
|
63
|
+
# If there is only a single param, it is the default param by default
|
|
64
|
+
# Also, if there is only one mandatory param, it is considered to be the default param
|
|
65
|
+
def default_param
|
|
66
|
+
if params.size == 1
|
|
67
|
+
params.first
|
|
68
|
+
else
|
|
69
|
+
result = params_with { |x| x.options[:default_param] == true }.first
|
|
70
|
+
if result.nil?
|
|
71
|
+
mandatory = mandatory_params
|
|
72
|
+
if mandatory_params.size == 1
|
|
73
|
+
result = mandatory.first
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
result
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def execute(payload)
|
|
81
|
+
self.block.call(*payload)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module Vop
|
|
2
|
+
|
|
3
|
+
class CommandParam
|
|
4
|
+
|
|
5
|
+
attr_reader :name, :options
|
|
6
|
+
|
|
7
|
+
def initialize(name, options = {})
|
|
8
|
+
@name = name
|
|
9
|
+
|
|
10
|
+
unless options.is_a? Hash
|
|
11
|
+
raise "[CommandParam] sanity check failed: unexpected options object class #{options.class}, expected Hash"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# auto-detect boolean parameters
|
|
15
|
+
if options.has_key? :default
|
|
16
|
+
if options[:default] == true || options[:default] == false
|
|
17
|
+
options[:boolean] = true
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
defaults = {
|
|
22
|
+
multi: false,
|
|
23
|
+
mandatory: false,
|
|
24
|
+
default_param: false
|
|
25
|
+
}
|
|
26
|
+
@options = defaults.merge(options)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# some params do not want to prefilled from the context
|
|
30
|
+
def wants_context
|
|
31
|
+
!(
|
|
32
|
+
options.has_key?(:use_context) &&
|
|
33
|
+
options[:use_context] == false
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Vop
|
|
2
|
+
|
|
3
|
+
class Entities < Array
|
|
4
|
+
|
|
5
|
+
def [](key)
|
|
6
|
+
# if key.is_a? Numeric
|
|
7
|
+
# super(key)
|
|
8
|
+
# else
|
|
9
|
+
$logger.debug "accessing entity with key '#{key}'"
|
|
10
|
+
found = select { |x| x.id == key }.first
|
|
11
|
+
if found
|
|
12
|
+
found
|
|
13
|
+
else
|
|
14
|
+
raise "no element with key '#{key}'"
|
|
15
|
+
end
|
|
16
|
+
# end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
module Vop
|
|
2
|
+
|
|
3
|
+
class Entity
|
|
4
|
+
|
|
5
|
+
attr_reader :type, :data, :key
|
|
6
|
+
|
|
7
|
+
def initialize(op, type, key, data)
|
|
8
|
+
@op = op
|
|
9
|
+
@type = type
|
|
10
|
+
@key = key
|
|
11
|
+
@data = data
|
|
12
|
+
|
|
13
|
+
unless @data.has_key? @key
|
|
14
|
+
raise "key #{key} not found in data : #{data.keys.sort}"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
make_methods_for_commands
|
|
18
|
+
make_method_for_id
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def id
|
|
22
|
+
@data[@key]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def [](key)
|
|
26
|
+
@data[key]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def plugin
|
|
30
|
+
if @data.has_key? "plugin_name"
|
|
31
|
+
@op.plugin(@data["plugin_name"])
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# all commands that have a parameter with the same name as the entity
|
|
36
|
+
# are considered eligible for this entity (TODO that's too broad, isn't it?)
|
|
37
|
+
def entity_commands
|
|
38
|
+
result = @op.commands.values.select do |command|
|
|
39
|
+
command.params.select do |param|
|
|
40
|
+
param.name == @type
|
|
41
|
+
end.count > 0
|
|
42
|
+
end
|
|
43
|
+
@command_count = result.count
|
|
44
|
+
result
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def make_methods_for_commands
|
|
48
|
+
entity_commands.each do |command|
|
|
49
|
+
# TODO this is very similar to code in Vop.<<
|
|
50
|
+
self.class.send(:define_method, command.short_name) do |*args, &block|
|
|
51
|
+
$logger.debug "[#{@type}:#{id}] #{command.short_name} (#{args.pretty_inspect}, block? #{block_given?})"
|
|
52
|
+
ruby_args = args.length > 0 ? args[0] : {}
|
|
53
|
+
# TODO we might want to do this only if there's a block param defined
|
|
54
|
+
# TODO this does not work if *args comes with a scalar default param
|
|
55
|
+
if block
|
|
56
|
+
ruby_args["block"] = block
|
|
57
|
+
end
|
|
58
|
+
@op.execute(command.short_name, ruby_args, { @type.to_s => id })
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def make_method_for_id
|
|
64
|
+
self.class.send(:define_method, @key) do |*args|
|
|
65
|
+
id
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def to_s
|
|
70
|
+
"Vop::Entity (#{@type})"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Vop
|
|
2
|
+
|
|
3
|
+
class EntityDefinition
|
|
4
|
+
|
|
5
|
+
attr_reader :plugin, :name
|
|
6
|
+
attr_accessor :key
|
|
7
|
+
attr_accessor :block
|
|
8
|
+
attr_accessor :on
|
|
9
|
+
attr_accessor :show_options
|
|
10
|
+
|
|
11
|
+
def initialize(plugin, name)
|
|
12
|
+
@plugin = plugin
|
|
13
|
+
@name = name
|
|
14
|
+
@key = "name"
|
|
15
|
+
@data = {}
|
|
16
|
+
|
|
17
|
+
@block = lambda { |params| $logger.warn "entity #{name} does not have a run block" }
|
|
18
|
+
|
|
19
|
+
@on = nil
|
|
20
|
+
@show_options = {}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def short_name
|
|
24
|
+
@name.split(".").last
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def source
|
|
28
|
+
plugin.sources[:entities][name]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module Vop
|
|
2
|
+
|
|
3
|
+
class Filter
|
|
4
|
+
|
|
5
|
+
attr_reader :name, :plugin
|
|
6
|
+
|
|
7
|
+
attr_accessor :description
|
|
8
|
+
attr_accessor :block
|
|
9
|
+
|
|
10
|
+
def initialize(plugin, name)
|
|
11
|
+
@plugin = plugin
|
|
12
|
+
@name = name
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def short_name
|
|
16
|
+
@name.split(".").last
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def execute(request)
|
|
20
|
+
# this executor is just to prepare payload
|
|
21
|
+
ex = Executor.new(@plugin.op)
|
|
22
|
+
context = {}
|
|
23
|
+
block_param_names = self.block.parameters.map { |x| x.last }
|
|
24
|
+
payload = ex.prepare_payload(request, context, block_param_names)
|
|
25
|
+
|
|
26
|
+
response = nil
|
|
27
|
+
begin
|
|
28
|
+
response = self.block.call(*payload)
|
|
29
|
+
rescue InterruptChain => ic
|
|
30
|
+
response = ic.response
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
response
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class InterruptChain < Exception
|
|
39
|
+
|
|
40
|
+
attr_reader :response
|
|
41
|
+
|
|
42
|
+
def initialize(response)
|
|
43
|
+
@response = response
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
end
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require_relative "../parts/entity_loader"
|
|
3
|
+
require_relative "../parts/command_loader"
|
|
4
|
+
require_relative "../parts/filter_loader"
|
|
5
|
+
require_relative "thing_with_params"
|
|
6
|
+
|
|
7
|
+
module Vop
|
|
8
|
+
|
|
9
|
+
class Plugin < ThingWithParams
|
|
10
|
+
|
|
11
|
+
attr_reader :op
|
|
12
|
+
attr_reader :name
|
|
13
|
+
attr_accessor :description
|
|
14
|
+
attr_reader :options
|
|
15
|
+
attr_reader :commands
|
|
16
|
+
|
|
17
|
+
attr_reader :sources
|
|
18
|
+
attr_reader :state
|
|
19
|
+
attr_reader :config
|
|
20
|
+
attr_accessor :dependencies
|
|
21
|
+
|
|
22
|
+
def initialize(op, plugin_name, plugin_path, options = {})
|
|
23
|
+
super()
|
|
24
|
+
|
|
25
|
+
@op = op
|
|
26
|
+
@name = plugin_name
|
|
27
|
+
@path = plugin_path
|
|
28
|
+
|
|
29
|
+
defaults = {
|
|
30
|
+
auto_load: true
|
|
31
|
+
}
|
|
32
|
+
@options = defaults.merge(options)
|
|
33
|
+
|
|
34
|
+
@description = nil
|
|
35
|
+
|
|
36
|
+
@state = {}
|
|
37
|
+
|
|
38
|
+
@config_file_name = File.join(op.plugin_config_path, plugin_name + ".json")
|
|
39
|
+
@config = {}
|
|
40
|
+
|
|
41
|
+
@dependencies = []
|
|
42
|
+
|
|
43
|
+
@hooks = {}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def to_s
|
|
47
|
+
"Vop::Plugin #{name}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def init
|
|
51
|
+
$logger.debug "plugin init : #{@name}"
|
|
52
|
+
|
|
53
|
+
@sources = Hash.new { |h, k| h[k] = {} }
|
|
54
|
+
|
|
55
|
+
@config = {}
|
|
56
|
+
|
|
57
|
+
# call_hook :preload ?
|
|
58
|
+
load_helpers
|
|
59
|
+
load_default_config
|
|
60
|
+
load_config
|
|
61
|
+
|
|
62
|
+
# TODO proceed only if auto_load
|
|
63
|
+
call_hook :init
|
|
64
|
+
load_entities
|
|
65
|
+
load_commands
|
|
66
|
+
load_filters
|
|
67
|
+
|
|
68
|
+
#@op.call_global_hook :plugin_loaded, self
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def plugin_dir(name)
|
|
72
|
+
File.join(@path, name.to_s)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def load_default_config
|
|
76
|
+
params.each do |param|
|
|
77
|
+
if param.options.has_key?(:default)
|
|
78
|
+
@config[param.name] = param.options[:default]
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def load_config
|
|
84
|
+
$logger.debug "looking for config at #{@config_file_name}"
|
|
85
|
+
if File.exists? @config_file_name
|
|
86
|
+
raw = nil
|
|
87
|
+
begin
|
|
88
|
+
raw = IO.read(@config_file_name)
|
|
89
|
+
config_from_file = JSON.parse(raw)
|
|
90
|
+
@config.merge! config_from_file
|
|
91
|
+
$logger.debug "plugin config loaded from #{@config_file_name}"
|
|
92
|
+
rescue => e
|
|
93
|
+
$logger.error "could not read JSON config from #{@config_file_name} (#{e.message}), ignoring:\n#{raw}"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def write_config
|
|
99
|
+
$logger.info "writing config into #{@op.plugin_config_path}"
|
|
100
|
+
unless Dir.exists?(@op.plugin_config_path)
|
|
101
|
+
FileUtils.mkdir_p @op.plugin_config_path
|
|
102
|
+
end
|
|
103
|
+
File.open(@config_file_name, "w") do |file|
|
|
104
|
+
file.write @config.to_json()
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def load_code_from_dir(type_name)
|
|
109
|
+
dir = plugin_dir(type_name)
|
|
110
|
+
|
|
111
|
+
if File.exists?(dir)
|
|
112
|
+
Dir.glob(File.join(dir, "*.rb")).each do |file_name|
|
|
113
|
+
name_from_file = /#{dir}\/(.+).rb$/.match(file_name).captures.first
|
|
114
|
+
full_name = [@name, name_from_file].join(".")
|
|
115
|
+
$logger.debug(" #{type_name} << #{full_name}")
|
|
116
|
+
|
|
117
|
+
@sources[type_name][full_name] = {
|
|
118
|
+
:file_name => file_name,
|
|
119
|
+
:code => File.read(file_name)
|
|
120
|
+
}
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def load_entities
|
|
126
|
+
loader = EntityLoader.new(self)
|
|
127
|
+
|
|
128
|
+
load_code_from_dir :entities
|
|
129
|
+
@entities = loader.read_sources @sources[:entities]
|
|
130
|
+
@op << @entities unless @entities.empty?
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def load_commands
|
|
134
|
+
loader = CommandLoader.new(self)
|
|
135
|
+
|
|
136
|
+
load_code_from_dir :commands
|
|
137
|
+
@commands = loader.read_sources @sources[:commands]
|
|
138
|
+
@op << @commands unless @commands.empty?
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def load_filters
|
|
142
|
+
loader = FilterLoader.new(self)
|
|
143
|
+
|
|
144
|
+
load_code_from_dir :filters
|
|
145
|
+
@filters = loader.read_sources @sources[:filters]
|
|
146
|
+
@op << @filters unless @filters.empty?
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def load_helpers
|
|
150
|
+
#load_code_from_dir :helpers
|
|
151
|
+
load_code_from_dir("helpers") # TODO unify (symbol vs. string) with load_commands above
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def inject_helpers(target, sub_type_name = nil)
|
|
155
|
+
type_name = 'helpers'
|
|
156
|
+
|
|
157
|
+
plugins_to_load_helpers_from = [ self ]
|
|
158
|
+
|
|
159
|
+
self.dependencies.each do |name|
|
|
160
|
+
other = @op.plugin(name)
|
|
161
|
+
raise "can not resolve plugin dependency #{name}" unless other
|
|
162
|
+
plugins_to_load_helpers_from << other
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
plugins_to_load_helpers_from.each do |other_plugin|
|
|
166
|
+
helper_sources = other_plugin.sources[type_name]
|
|
167
|
+
|
|
168
|
+
next if helper_sources.size == 0
|
|
169
|
+
|
|
170
|
+
helper_module = Module.new()
|
|
171
|
+
|
|
172
|
+
helper_sources.each do |name, helper|
|
|
173
|
+
begin
|
|
174
|
+
helper_module.class_eval helper[:code]
|
|
175
|
+
rescue Exception => e
|
|
176
|
+
$stderr.puts("could not read helper #{name} : #{e.message}")
|
|
177
|
+
raise e
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
target.extend helper_module
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def hook(name, &block)
|
|
186
|
+
@hooks[name.to_sym] = block
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def call_hook(name, *args)
|
|
190
|
+
result = nil
|
|
191
|
+
if @hooks.has_key? name
|
|
192
|
+
result = @hooks[name].call(self, *args)
|
|
193
|
+
end
|
|
194
|
+
result
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def template_path(name)
|
|
198
|
+
name += ".erb" unless name.end_with? ".erb"
|
|
199
|
+
File.join(plugin_dir(:templates), name)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def template(name)
|
|
203
|
+
@op.read_template(template_path(name))
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
end
|