shelldon 0.0.1 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +11 -0
  3. data/.rubocop.yml +60 -0
  4. data/Gemfile +0 -5
  5. data/README.md +0 -1
  6. data/lib/auto_complete.rb +34 -0
  7. data/lib/command/command.rb +63 -11
  8. data/lib/command/command_list.rb +16 -8
  9. data/lib/command/script.rb +33 -0
  10. data/lib/config/config.rb +13 -4
  11. data/lib/config/config_factory.rb +4 -0
  12. data/lib/config/param.rb +17 -11
  13. data/lib/config/param/boolean_param.rb +1 -1
  14. data/lib/config/param_factory.rb +6 -1
  15. data/lib/dsl.rb +17 -0
  16. data/lib/{Exceptions → exceptions}/error_factory.rb +10 -0
  17. data/lib/exceptions/exceptions.rb +60 -0
  18. data/lib/file_management/config_file_manager.rb +3 -7
  19. data/lib/file_management/history_file.rb +0 -2
  20. data/lib/file_management/yaml_manager.rb +10 -1
  21. data/lib/helpers/confirmation.rb +20 -0
  22. data/lib/helpers/timer.rb +5 -0
  23. data/lib/opts/opt_factory.rb +4 -3
  24. data/lib/opts/opts.rb +1 -1
  25. data/lib/shell/shell.rb +82 -19
  26. data/lib/shell/shell_factory.rb +87 -14
  27. data/lib/shell/shell_index.rb +11 -1
  28. data/lib/shelldon.rb +8 -3
  29. data/lib/shelldon/version.rb +1 -1
  30. data/shelldon.gemspec +15 -8
  31. data/test_shell/Gemfile +1 -2
  32. data/test_shell/Gemfile.lock +7 -6
  33. data/test_shell/dependency_test/Gemfile +2 -0
  34. data/test_shell/dependency_test/Gemfile.lock +21 -0
  35. data/test_shell/dependency_test/dependency_test.rb +20 -0
  36. data/test_shell/dependency_test/dt_commands.rb +66 -0
  37. data/test_shell/dependency_test/dt_config.rb +31 -0
  38. data/test_shell/dependency_test/dt_opts.rb +11 -0
  39. data/test_shell/dependency_test/dt_runner.rb +11 -0
  40. data/test_shell/simple_shell.rb +6 -6
  41. data/test_shell/test_shell.rb +46 -3
  42. data/test_shell/useful_commands.rb +1 -1
  43. metadata +80 -41
  44. data/Gemfile.lock +0 -43
@@ -10,7 +10,7 @@ module Shelldon
10
10
  v
11
11
  elsif v.is_a?(String)
12
12
  return true if v == true || v =~ (/(true|t|yes|y|1)$/i)
13
- return false if v == false || v.blank? || v =~ (/(false|f|no|n|0)$/i)
13
+ return false if v == false || v =~ (/(false|f|no|n|0)$/i)
14
14
  fail(ArgumentError, "invalid value for Boolean: \"#{v}\"")
15
15
  else
16
16
  v ? true : false
@@ -21,6 +21,10 @@ module Shelldon
21
21
  @default = default_value
22
22
  end
23
23
 
24
+ def override(value)
25
+ @override = value
26
+ end
27
+
24
28
  def opt(opt)
25
29
  @opt = opt
26
30
  end
@@ -30,13 +34,14 @@ module Shelldon
30
34
  if @param
31
35
  @param.pretty = @pretty_proc if @pretty_proc
32
36
  @param.default = @default unless @default.nil?
37
+ @param.override = @override if @override
33
38
  @param.opt = @opt if @opt
34
39
  @param.error = @error if @error
35
40
  @param.validator = @validator if @validator
36
41
  @param.adjustor = @adjustor if @adjustor
37
42
  @config.register(@param)
38
43
  else
39
- fail StandardError
44
+ fail Shelldon::RegisterNilParamError
40
45
  end
41
46
  end
42
47
 
data/lib/dsl.rb CHANGED
@@ -7,6 +7,10 @@ module Shelldon
7
7
  ShellIndex.instance << shell if shell.is_a?(Shelldon::Shell)
8
8
  end
9
9
 
10
+ def self.first?(sym)
11
+ ShellIndex.first?(sym)
12
+ end
13
+
10
14
  def self.shell(name = (:default), &block)
11
15
  ShellFactory.new(name.to_sym, &block)
12
16
  end
@@ -18,4 +22,17 @@ module Shelldon
18
22
  def self.opts
19
23
  Shelldon::Opts.instance.opts
20
24
  end
25
+
26
+ def self.method_missing(meth, *args, &block)
27
+ ShellIndex.instance[:default].send(meth, *args, &block)
28
+ end
29
+
30
+ def self.confirm(*args)
31
+ Shelldon::Confirmation.ask(*args)
32
+ end
33
+
34
+ def self.has_shell?(sym)
35
+ sym = sym.to_sym unless sym
36
+ ShellIndex.instance.key?(sym)
37
+ end
21
38
  end
@@ -1,5 +1,7 @@
1
1
  module Shelldon
2
2
  class ErrorFactory
3
+ attr_reader :on_accept_error, :on_reject_error
4
+
3
5
  def initialize(&block)
4
6
  @accept_errors = {}
5
7
  @reject_errors = {}
@@ -19,6 +21,14 @@ module Shelldon
19
21
  @reject_errors[e] = (block_given? ? block : nil)
20
22
  end
21
23
 
24
+ def on_accept(&block)
25
+ @on_accept_error = block
26
+ end
27
+
28
+ def on_reject(&block)
29
+ @on_reject_error = block
30
+ end
31
+
22
32
  def get
23
33
  [@accept_errors, @reject_errors]
24
34
  end
@@ -0,0 +1,60 @@
1
+ module Shelldon
2
+ class Error < StandardError
3
+ end
4
+
5
+ class ConfirmationError < Error
6
+ define_method(:message) { 'User did not provide a valid confirmation.' }
7
+ end
8
+
9
+ class NotImplementedError < Error
10
+ define_method(:message) { 'This command has not been implemented, or is a placeholder' }
11
+ end
12
+
13
+ class NoSuchCommandError < Error
14
+ define_method(:message) { 'No such command exists' }
15
+ end
16
+
17
+ class NotBooleanError < Error
18
+ define_method(:message) { 'The specified config param is not a boolean' }
19
+ end
20
+
21
+ class NotProcError < Error
22
+ define_method(:message) { 'Autocomplete must be a Proc or an Array' }
23
+ end
24
+
25
+ class DuplicateParamError < Error
26
+ define_method(:message) { 'Cannot create two params with the same name' }
27
+ end
28
+
29
+ class NoSuchParamError < Error
30
+ define_method(:message) { 'No such param exists' }
31
+ end
32
+
33
+ class InvalidParamValueError < Error
34
+ define_method(:message) { 'Param failed to pass validation' }
35
+ end
36
+
37
+ class RegisterNilParamError < Error
38
+ define_method(:message) { 'The Param Factory is attempting to register a nil.' }
39
+ end
40
+
41
+ class NoSuchOptTypeError < Error
42
+ define_method(:message) { 'The specified opt type was not :boolean or :required' }
43
+ end
44
+
45
+ class RedefineOptsError < Error
46
+ define_method(:message) { 'Cannot re-define opts' }
47
+ end
48
+
49
+ class NoSuchShellError < Error
50
+ define_method(:message) { 'The shell index does not contain that shell' }
51
+ end
52
+
53
+ class NotAShellError < Error
54
+ define_method(:message) { 'Cannot add non-shells to the shell index' }
55
+ end
56
+
57
+ class TimeoutError < Error
58
+ define_method(:message) { 'Operation timed out.' }
59
+ end
60
+ end
@@ -4,24 +4,20 @@ module Shelldon
4
4
  class ConfigFileManager < YamlManager
5
5
  def initialize(shell, config_file)
6
6
  @shell = shell
7
- @config_file = config_file
7
+ @file = config_file
8
8
  end
9
9
 
10
10
  def ensure_dir(dir)
11
11
  FileUtils.mkdir_p(dir)
12
12
  end
13
13
 
14
- def ensure_file(file)
15
- File.open(file, 'w') { |f| f.write({}.to_yaml) } unless File.exist?(file)
16
- end
17
-
18
14
  def export
19
15
  super(@shell.config)
20
16
  end
21
17
 
22
18
  def setup
23
- @file = @config_file.expand_path
24
- @file_dir = @config_file.dirname.expand_path
19
+ @file = @file.expand_path
20
+ @file_dir = @file.dirname.expand_path
25
21
  ensure_dir(@file_dir)
26
22
  ensure_file(@file)
27
23
  end
@@ -10,8 +10,6 @@ module Shelldon
10
10
  @file = Pathname.new(@file).expand_path
11
11
  hist = File.open(@file, 'r') { |f| f.read }.split("\n")
12
12
  hist.each do |line|
13
- # puts line
14
- # pp Readline::HISTORY.to_a
15
13
  Readline::HISTORY.push(line)
16
14
  end
17
15
  end
@@ -1,4 +1,3 @@
1
-
2
1
  module Shelldon
3
2
  class YamlManager < FileManager
4
3
  def ensure_file(file)
@@ -10,7 +9,17 @@ module Shelldon
10
9
  end
11
10
 
12
11
  def import(_yaml)
12
+ reset unless valid?
13
13
  YAML.load_file(@file)
14
14
  end
15
+
16
+ def valid?
17
+ File.exist?(@file) && !File.zero?(@file)
18
+ end
19
+
20
+ def reset
21
+ FileUtils.mv(@file, "#{@file}-#{Time.now.strftime('%y%m%d-%k%M%S')}.invalid")
22
+ ensure_file(@file)
23
+ end
15
24
  end
16
25
  end
@@ -0,0 +1,20 @@
1
+ module Shelldon
2
+ class Confirmation
3
+ def self.ask(*args)
4
+ new(*args).ask
5
+ end
6
+
7
+ def initialize(msg = 'Confirm? (y/n)',
8
+ ok = %w(y Y yes Yes YES))
9
+ @msg = msg
10
+ @ok = ok
11
+ end
12
+
13
+ def ask
14
+ puts @msg
15
+ print '> '
16
+ response = $stdin.gets.chomp
17
+ @ok.include?(response) ? true : fail(Shelldon::ConfirmationError)
18
+ end
19
+ end
20
+ end
@@ -1,5 +1,10 @@
1
1
  module Shelldon
2
2
  class Timer
3
+ def self.start
4
+ t = new
5
+ t.start
6
+ t
7
+ end
3
8
  def initialize(strftime = '%s')
4
9
  @strftime = strftime
5
10
  end
@@ -1,9 +1,10 @@
1
1
  module Shelldon
2
2
  class OptFactory
3
- def initialize(&block)
3
+ def initialize(name, &block)
4
+ @name = name
4
5
  @opt_arr = []
5
6
  instance_eval(&block)
6
- Shelldon.opts = get_opts
7
+ Shelldon.opts = get_opts if Shelldon.first?(@name)
7
8
  end
8
9
 
9
10
  def get_opts
@@ -23,7 +24,7 @@ module Shelldon
23
24
  when :required
24
25
  Getopt::REQUIRED
25
26
  else
26
- fail StandardError
27
+ fail Shelldon::NoSuchOptTypeError
27
28
  end
28
29
  end
29
30
  end
@@ -6,7 +6,7 @@ module Shelldon
6
6
  end
7
7
 
8
8
  def set(opts_arr)
9
- fail StandardError unless @opts.nil?
9
+ fail Shelldon::RedefineOptsError unless @opts.nil?
10
10
  @opts ||= opts_arr
11
11
  end
12
12
  end
@@ -1,11 +1,11 @@
1
- require 'byebug'
2
1
  require 'readline'
2
+ require_relative '../auto_complete'
3
+ # require 'byebug'
3
4
 
4
5
  module Shelldon
5
6
  class Shell
6
- attr_accessor :command_list, :config,
7
- :accept_errors, :reject_errors
8
- attr_reader :home, :name
7
+ attr_accessor :command_list, :config, :accept_errors, :reject_errors, :on_opts, :on_pipe
8
+ attr_reader :home, :name, :config, :logger
9
9
  attr_writer :history, :history_file
10
10
 
11
11
  def initialize(name, &block)
@@ -15,25 +15,39 @@ module Shelldon
15
15
  @errors = {}
16
16
  @history = true
17
17
  @command_list = CommandList.new(self)
18
- @config = Config.new(self)
18
+ @config = Shelldon::Config.new(self)
19
+ @autocomplete = Shelldon::AutoComplete.new(self) # if defined?(Shelldon::AutoComplete)
20
+ @on_opts = {}
19
21
  setup(&block) if block_given?
20
22
  end
21
23
 
24
+ def shell
25
+ self
26
+ end
27
+
22
28
  def self.method_missing(meth_name, *args, &block)
23
29
  Shelldon::ShellIndex[:default].send(meth_name, *args, &block)
24
30
  end
25
31
 
32
+ def self.[](key)
33
+ fail Shelldon::NoSuchShellError unless Shelldon::ShellIndex[key]
34
+ Shelldon::ShellIndex[key]
35
+ end
36
+
26
37
  def setup(&block)
27
38
  instance_eval(&block)
28
- FileUtils.mkdir_p(@home.to_s) unless File.exist?(@home)
39
+ FileUtils.mkdir_p(@home.to_s) unless File.exist?(@home) if @home
29
40
  Dir.chdir(@home) if @home
30
- Readline.completion_proc = proc { [] }
41
+ if @auto_complete_proc
42
+ Readline.completion_proc = @auto_complete_proc
43
+ else
44
+ @autocomplete.set_proc if @autocomplete
45
+ end
46
+
31
47
  if @history_path && @history
32
48
  @history_helper = Shelldon::HistoryFile.new(@history_path)
33
- @history_helper.load if @history_helper
34
49
  end
35
50
  @config.load_config_file
36
- run
37
51
  end
38
52
 
39
53
  def quit
@@ -42,35 +56,65 @@ module Shelldon
42
56
  exit 0
43
57
  end
44
58
 
59
+ def run_opt_conditions
60
+ @on_opts.each do |opt, procs|
61
+ if Shelldon.opts && Shelldon.opts.key?(opt)
62
+ procs.each do |proc|
63
+ instance_eval(&proc)
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ def handle_piped_input
70
+ return if $stdin.tty?
71
+ if @on_pipe
72
+ instance_exec($stdin.readlines, &@on_pipe)
73
+ else
74
+ $stdin.readlines.each { |cmd| run_commands(cmd) }
75
+ end
76
+ exit 0
77
+ end
78
+
45
79
  def run
80
+ @history_helper.load
81
+ run_opt_conditions
82
+ handle_piped_input
46
83
  instance_eval(&@startup) if @startup
47
84
  begin
48
85
  run_repl
49
86
  rescue *@accept_errors.keys => e
50
87
  print_error(e)
51
- on_error(e, @accept_errors[e.class])
88
+ @logger.warn(e)
89
+ on_error(e, @accept_errors[e.class], :accept)
52
90
  retry
53
91
  rescue *@reject_errors.keys => e
54
92
  print_error(e)
55
- on_error(e, @reject_errors[e.class])
93
+ @logger.fatal(e)
94
+ on_error(e, @reject_errors[e.class], :reject)
56
95
  rescue StandardError => e
57
96
  print_error(e)
58
97
  puts "Reached fatal error. G'bye!"
59
- raise e
60
98
  ensure
99
+ # puts "Last exception: #{$!.inspect}" #if @config[:debug_mode]
100
+ # puts "Last backtrace: \n#{$@.join("\n")}"# if @config[:debug_mode]
61
101
  instance_eval(&@shutdown) if @shutdown
62
102
  @history_helper.save if @history_helper
63
103
  quit
64
104
  end
65
105
  end
66
106
 
67
- def on_error(e, proc)
68
- instance_exec(e, &proc)
107
+ def on_error(e, proc, type = nil)
108
+ run_accept_error(e) if type == :accept
109
+ run_reject_error(e) if type == :reject
110
+ instance_exec(e, &proc) if proc
69
111
  end
70
112
 
71
113
  def print_error(e)
72
- return false unless @config[:debug_mode]
73
- puts e.message
114
+ return unless @config[:debug_mode]
115
+ puts "Hit Error! (#{e.class})"
116
+ msg = (e.message == e.class.to_s ? '' : "(#{e.message})")
117
+ puts msg unless msg == '()'
74
118
  puts e.backtrace.join("\n")
75
119
  end
76
120
 
@@ -101,7 +145,7 @@ module Shelldon
101
145
  end
102
146
 
103
147
  def run_command(cmd)
104
- @history_helper << cmd
148
+ @history_helper << cmd if @history_helper
105
149
  run_precommand(cmd)
106
150
  @command_list.run(cmd)
107
151
  run_postcommand(cmd)
@@ -115,6 +159,14 @@ module Shelldon
115
159
  instance_exec(cmd, &@post_command) if @post_command
116
160
  end
117
161
 
162
+ def run_accept_error(error)
163
+ instance_exec(error, &@on_accept_error) if @on_accept_error
164
+ end
165
+
166
+ def run_reject_error(error)
167
+ instance_exec(error, &@on_reject_error) if @on_reject_error
168
+ end
169
+
118
170
  def init(&block)
119
171
  instance_eval(&block)
120
172
  end
@@ -131,6 +183,11 @@ module Shelldon
131
183
  @history_path = file
132
184
  end
133
185
 
186
+ def log_file(filepath, freq = 'daily')
187
+ filepath = Pathname.new(filepath).expand_path.to_s
188
+ @logger = Logger.new(filepath, freq)
189
+ end
190
+
134
191
  def prompt(str = '> ', &block)
135
192
  if block_given?
136
193
  @prompt_setter = block
@@ -160,8 +217,14 @@ module Shelldon
160
217
  end
161
218
 
162
219
  def errors(&block)
163
- @accept_errors, @reject_errors =
164
- Shelldon::ErrorFactory.new(&block).get
220
+ error_factory = Shelldon::ErrorFactory.new(&block)
221
+ @accept_errors, @reject_errors = error_factory.get
222
+ @on_accept_error = error_factory.on_accept_error
223
+ @on_reject_error = error_factory.on_reject_error
224
+ end
225
+
226
+ def autocomplete(&block)
227
+ @auto_complete_proc = block.to_proc
165
228
  end
166
229
  end
167
230
  end