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.
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
@@ -1,88 +0,0 @@
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
@@ -1,31 +0,0 @@
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
@@ -1,12 +0,0 @@
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
@@ -1,38 +0,0 @@
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
@@ -1,4 +0,0 @@
1
- run do
2
- foo()
3
- config(:identity) || 'localhost'
4
- end
@@ -1,8 +0,0 @@
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
@@ -1,3 +0,0 @@
1
- run do
2
- @plugin.state[:entities].map { |x| x[:name] }
3
- end
@@ -1,9 +0,0 @@
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
@@ -1,5 +0,0 @@
1
- description "reloads plugins and commands"
2
-
3
- run do
4
- @op._reset
5
- end
@@ -1,5 +0,0 @@
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
@@ -1,5 +0,0 @@
1
- param! "command"
2
-
3
- run do |command|
4
- `#{command}`
5
- end
@@ -1,4 +0,0 @@
1
- on :init do |plugin|
2
- plugin.state[:entities] ||= []
3
- plugin.state[:contributions] ||= Hash.new { |h,k| h[k] = [] }
4
- end
@@ -1,45 +0,0 @@
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
@@ -1,28 +0,0 @@
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
@@ -1,57 +0,0 @@
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
@@ -1,3 +0,0 @@
1
- def foo
2
- puts "call for mr. beeblebrox!"
3
- end
@@ -1,6 +0,0 @@
1
- param! 'path'
2
-
3
- run do |params|
4
- @op.config[:search_path] << params['path']
5
- @op.reset
6
- end
@@ -1,13 +0,0 @@
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
@@ -1,17 +0,0 @@
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
@@ -1,8 +0,0 @@
1
- run do
2
- @op.plugins.map do |name, plugin|
3
- {
4
- :name => name,
5
- :path => plugin.path
6
- }
7
- end
8
- end
@@ -1,14 +0,0 @@
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
@@ -1,25 +0,0 @@
1
- description "initializes a new plugin (folder)"
2
-
3
- param! 'name'
4
- param! 'path', :description => 'the path in which to create the new plugin.'
5
-
6
- require 'fileutils'
7
-
8
- run do |params|
9
- raise "no such path: #{params['path']}" unless File.exists? params['path']
10
-
11
- # a plugin is a directory
12
- plugin_path = File.join(params['path'], params['name'])
13
- Dir.mkdir(plugin_path)
14
-
15
- # with subfolders for commands and helpers
16
- %w|commands helpers|.each do |thing|
17
- Dir.mkdir(File.join(plugin_path, thing))
18
- end
19
-
20
- # and a metadata file called '<name>.plugin'
21
- plugin_file = params['name'] + '.plugin'
22
- FileUtils.touch(File.join(plugin_path, plugin_file))
23
-
24
- @op.reset
25
- end
@@ -1,3 +0,0 @@
1
- run do
2
- @op._search_path
3
- end
@@ -1,5 +0,0 @@
1
- param! 'name', :lookup => lambda { |params| @op.list_commands.map { |x| x[:name] } }
2
-
3
- run do |name|
4
- @op.command(name).plugin.name
5
- end
@@ -1 +0,0 @@
1
- dependency :core
@@ -1,11 +0,0 @@
1
- require 'net/scp'
2
-
3
- param! 'machine'
4
- param 'user'
5
- param! 'local_path'
6
- param! 'remote_path'
7
-
8
- run do |machine, local_path, remote_path, params|
9
- user = params.has_key?('user') ? params['user'] : ENV['USER']
10
- Net::SCP.upload!(params['machine'], user, local_path, remote_path)
11
- end
@@ -1,19 +0,0 @@
1
- require 'net/ssh'
2
-
3
- param! 'machine'
4
- param! 'command', :default_param => true
5
- param 'user' #, :default => ENV['USER']
6
- param 'key_file', :multi => true
7
-
8
- run do |params|
9
- machine_name = params['machine']
10
- user = params.has_key?('user') ? params['user'] : ENV['USER']
11
-
12
- options = {}
13
- if params.has_key? 'key_file'
14
- options[:keys] = params['key_file']
15
- end
16
- Net::SSH.start(machine_name, user, options) do |ssh|
17
- ssh.exec! params['command']
18
- end
19
- end
@@ -1 +0,0 @@
1
- depends :core
@@ -1,28 +0,0 @@
1
- # responsible for what should happen when the user interacts with the shell
2
- # provides tab completion options and processes the user's input
3
- class Backend
4
-
5
- # is called whenever the user submits a command (hitting enter)
6
- def process_input(command_line)
7
- raise "not implemented in abstract Backend!"
8
- end
9
-
10
- # is called whenever the user requests tab completion
11
- # should return an array of completion proposals
12
- def complete(word)
13
- raise "not implemented in abstract Backend!"
14
- end
15
-
16
- def prompt
17
- ">"
18
- end
19
-
20
- def process_ctrl_c
21
-
22
- end
23
-
24
- def show_banner
25
-
26
- end
27
-
28
- end
@@ -1,112 +0,0 @@
1
- require 'readline'
2
-
3
- # This class is an abstract implementation of a command shell
4
- # It handles command completion and history functions.
5
- # For the actual business logic, you need to pass it an implementation of ShellBackend
6
- class BaseShell
7
- attr_reader :backend
8
-
9
- def initialize(backend)
10
- @logger = $logger
11
- @backend = backend
12
-
13
- at_exit { console.close }
14
-
15
- trap("INT") {
16
- Thread.kill(@thread)
17
- @backend.process_ctrl_c
18
- }
19
- end
20
-
21
- class SimpleConsole
22
- def initialize(input = $stdin)
23
- @input = input
24
- end
25
-
26
- def readline
27
- begin
28
- line = @input.readline
29
- line.chomp! if line
30
- line
31
- rescue EOFError
32
- nil
33
- end
34
- end
35
-
36
- def close
37
- end
38
- end
39
-
40
- class ReadlineConsole
41
- HISTORY_FILE = ".vop_history"
42
- MAX_HISTORY = 200
43
-
44
- def history_path
45
- File.join(ENV['HOME'] || ENV['USERPROFILE'], HISTORY_FILE)
46
- end
47
-
48
- def initialize(shell)
49
- @shell = shell
50
-
51
- if File.exist?(history_path)
52
- hist = File.readlines(history_path).map{|line| line.chomp}
53
- Readline::HISTORY.push(*hist)
54
- end
55
-
56
- #Readline.basic_word_break_characters = " \t\n\\`@><=;|&{([+*%"
57
-
58
- # see http://stackoverflow.com/questions/13876024/how-to-write-a-ruby-command-line-app-that-supports-tab-completion#13876556
59
- Readline.completer_word_break_characters = ""
60
-
61
- Readline.completion_append_character = nil
62
- Readline.completion_proc = @shell.backend.method(:complete).to_proc
63
- end
64
-
65
- def close
66
- open(history_path, "wb") do |f|
67
- history = Readline::HISTORY.to_a
68
- if history.size > MAX_HISTORY
69
- history = history[history.size - MAX_HISTORY, MAX_HISTORY]
70
- end
71
- history.each{|line| f.puts(line)}
72
- end
73
- end
74
-
75
- def readline
76
- line = Readline.readline(@shell.backend.prompt, true)
77
- Readline::HISTORY.pop if /^\s*$/ =~ line
78
- line
79
- end
80
- end
81
-
82
- def console
83
- @console ||= $stdin.tty? ? ReadlineConsole.new(self) : SimpleConsole.new
84
- end
85
-
86
- def run
87
- backend.show_banner
88
- loop do
89
- @thread = Thread.new {
90
- line = console.readline
91
-
92
- if line
93
- backend.process_input line
94
- end
95
- }
96
- begin
97
- @thread.join
98
- rescue
99
- error = $!
100
- if error == "exit"
101
- Kernel.exit
102
- else
103
- $stderr.puts "error: >>#{error}<<"
104
- $stderr.puts error.backtrace.join("\n")
105
- end
106
- end
107
-
108
- end
109
- $stderr.puts "Exiting shell..."
110
- end
111
-
112
- end