vop 0.3.1 → 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|