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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1050dd8126c65561d242ce862a4cf2947ed61618
4
- data.tar.gz: fd6ad927cc21f2087011bd1584e652d4b4ac415d
3
+ metadata.gz: 4c9b127c3231b3a6616c774fe92a69847b25bb10
4
+ data.tar.gz: d9a68228b4bcfe2ac70c55123c3b08ce99ca4a41
5
5
  SHA512:
6
- metadata.gz: f9a2c2be6b2cba4b8bd61fa6b666fd1b5daf9d15bc9d8d04eb2c1bfb2c7811aa3836f18357d779d8ca86ef6f98b9fbe64cdcbb2c0c48f23d2ee0150bb1a767c4
7
- data.tar.gz: 168ab3b8d30c478081e3d2dbac3f032f5b74f93d93736c618eb2909e235207eb5f0bcf9a409bdb75ae8bf440b05c270aee8148e81feb79fcdea04d457f77787d
6
+ metadata.gz: 3b2f291dd74ff9ab4e29f2626a3331f93a72da07629f0f3eb6026466b188eff6e4b92af70c3371f41bc0eddc03fd6bc6787768ef8b45270389d95551f51828e3
7
+ data.tar.gz: 413aa23e7e03186a0d6684c3a74b38226f6d8f7ee05a46dcab1297638122446667efb1ec3c38db8864afefb0bf9e55f638cfb1a3acd1b2be3ed17d71e02e519d
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.gem
11
+ send_to_opsadm01.sh
@@ -0,0 +1,60 @@
1
+ #inherit_from: ../.rubocop.yml
2
+
3
+ # Why do I even use robocop lol
4
+ # Oh yeah because it puts a space in before my comments.
5
+
6
+ AllCops:
7
+ Exclude:
8
+ - test_scripts/*
9
+ - example_data/*
10
+
11
+ Metrics/LineLength:
12
+ Enabled: false
13
+
14
+ Style/Documentation:
15
+ Enabled: false
16
+
17
+ Style/Encoding:
18
+ Enabled: false
19
+
20
+ Style/Semicolon:
21
+ Enabled: false
22
+
23
+ Style/AccessorMethodName:
24
+ Enabled: false
25
+
26
+ Metrics/MethodLength:
27
+ Enabled: false
28
+
29
+ Metrics/AbcSize:
30
+ Enabled: false
31
+
32
+ Metrics/ParameterLists:
33
+ Enabled: false
34
+
35
+ Style/Next:
36
+ Enabled: false
37
+
38
+ Style/FileName:
39
+ Enabled: false
40
+
41
+ Style/PredicateName:
42
+ Enabled: false
43
+
44
+ Style/NestedTernaryOperator:
45
+ Enabled: false
46
+
47
+ Metrics/CyclomaticComplexity:
48
+ Enabled: false
49
+
50
+ Metrics/PerceivedComplexity:
51
+ Enabled: false
52
+
53
+ Metrics/ClassLength:
54
+ Enabled: false
55
+
56
+ Lint/AssignmentInCondition:
57
+ Enabled: false
58
+
59
+ Style/TrivialAccessors:
60
+ Enabled: false
data/Gemfile CHANGED
@@ -1,8 +1,3 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'byebug'
4
- gem 'terminal-table'
5
- gem 'rb-readline'
6
- gem 'getopt'
7
-
8
3
  gemspec
data/README.md CHANGED
@@ -10,7 +10,6 @@ There are some good gems out there for building command-line executables, but I
10
10
  # Gemfile
11
11
  gem 'shelldon'
12
12
 
13
-
14
13
  $ bundle install
15
14
  ```
16
15
  Or just `gem install shelldon` -- You know the drill.
@@ -0,0 +1,34 @@
1
+ # require 'abbrev'
2
+
3
+ module Shelldon
4
+ class AutoComplete
5
+ def initialize(shell)
6
+ @shell = shell
7
+ end
8
+
9
+ def set_proc
10
+ @comp = proc { |s| auto_comp.grep(/^#{Regexp.escape(s)}/) }
11
+ Readline.completion_append_character = ' '
12
+ Readline.completion_proc = @comp
13
+ end
14
+
15
+ def buf
16
+ Readline.line_buffer
17
+ end
18
+
19
+ def auto_comp
20
+ Readline.completion_append_character = ' '
21
+ @commands = @shell.command_list.commands.map(&:to_s)
22
+ length = buf.split(' ').length
23
+ return @commands if length <= 1 && !buf.end_with?(' ')
24
+ cmd, = @shell.command_list.find(buf)
25
+ return [] unless cmd
26
+ remainder = buf.gsub("#{cmd.christian_name} ", '')
27
+ cmd.complete(remainder)
28
+ end
29
+
30
+ def tokenize(str)
31
+ str.split(' ')
32
+ end
33
+ end
34
+ end
@@ -1,6 +1,6 @@
1
1
  module Shelldon
2
2
  class Command
3
- attr_reader :name, :aliases, :subcommands, :parent
3
+ attr_reader :name, :aliases, :subcommands, :parent, :autocomplete, :times_out
4
4
 
5
5
  def initialize(name, parent, &block)
6
6
  @name = name
@@ -8,6 +8,7 @@ module Shelldon
8
8
  @subcommands = {}
9
9
  @show = true
10
10
  @parent = parent
11
+ @times_out = true
11
12
  instance_eval(&block)
12
13
  end
13
14
 
@@ -19,8 +20,12 @@ module Shelldon
19
20
  end
20
21
  end
21
22
 
23
+ def fuzzy(cmd)
24
+ FuzzyMatch.new(command_list.to_a.map(&:first).map(&:to_s)).find(cmd)
25
+ end
26
+
22
27
  def command_list
23
- @parent
28
+ @parent.command_list
24
29
  end
25
30
 
26
31
  def shell
@@ -34,8 +39,10 @@ module Shelldon
34
39
  end
35
40
 
36
41
  def run(tokens = [])
37
- tokens = [tokens] unless tokens.is_a?(Array)
38
- instance_exec(tokens.join(' '), &@action)
42
+ tokens = [tokens] unless tokens.is_a?(Array)
43
+ Timeout.timeout(timeout_length, Shelldon::TimeoutError) do
44
+ instance_exec(tokens.join(' '), &@action)
45
+ end
39
46
  end
40
47
 
41
48
  def valid?(input)
@@ -47,10 +54,6 @@ module Shelldon
47
54
  end
48
55
  end
49
56
 
50
- def auto_complete(_input)
51
- @autocomplete = @subcommands.values
52
- end
53
-
54
57
  def has_subcommand?
55
58
  !@subcommands.empty?
56
59
  end
@@ -98,14 +101,41 @@ module Shelldon
98
101
  end
99
102
  end
100
103
 
101
- def timeout(i = nil)
102
- i ? @timeout = i : @timeout
104
+ def to_a
105
+ [@name, @aliases.map { |a| "'#{a}'" }.join(', '), @help || '']
106
+ end
107
+
108
+ def sub_to_a
109
+ @subcommands.values.uniq
110
+ .map { |cmd| cmd.show ? cmd.to_a : nil }
111
+ .compact.sort_by { |(n, _, _)| n.to_s }
112
+ end
113
+
114
+ def timeout_length(_i = nil)
115
+ return 0 unless @times_out
116
+ return shell.config[:timeout] unless @timeout
117
+ @timeout
118
+ end
119
+
120
+ def complete(buf)
121
+ length = buf.split(' ').length
122
+ res = (length <= 1 && !buf.end_with?(' ')) ? subcommand_list : []
123
+ res += instance_exec(buf, &@autocomplete) if @autocomplete
124
+ res
103
125
  end
104
126
 
105
127
  # DSL Only
106
128
 
107
129
  private
108
130
 
131
+ def times_out(bool = true)
132
+ @times_out = bool
133
+ end
134
+
135
+ def timeout(i = nil)
136
+ i ? @timeout = i : @timeout
137
+ end
138
+
109
139
  def validate(error, &block)
110
140
  @error = error if error
111
141
  @validator = block
@@ -123,12 +153,34 @@ module Shelldon
123
153
  @subcommands[name.to_sym] = Shelldon::Command.new(name, self, &block)
124
154
  end
125
155
 
156
+ def subcommand_list
157
+ return [] if @subcommands.empty?
158
+ @subcommands.keys.map(&:to_s)
159
+ end
160
+
161
+ def script(dir)
162
+ Shelldon::Script.from_dir(dir, self).each do |cmd|
163
+ @subcommands[cmd.name.to_sym] = cmd
164
+ end
165
+ end
166
+
167
+ alias_method :scripts, :script
168
+
126
169
  def completion(arr = [], &block)
127
170
  @completion = (block_given? ? block : arr)
128
171
  end
129
172
 
130
173
  def placeholder
131
- @action = proc { fail StandardError }
174
+ @action = proc { fail Shelldon::NotImplementedError }
175
+ end
176
+
177
+ def autocomplete(arr = nil, &block)
178
+ if block_given?
179
+ @autocomplete = block.to_proc
180
+ else
181
+ arr ||= []
182
+ @autocomplete = proc { arr }
183
+ end
132
184
  end
133
185
  end
134
186
  end
@@ -13,6 +13,10 @@ module Shelldon
13
13
  command.aliases.each { |a| @commands[a.to_sym] = command }
14
14
  end
15
15
 
16
+ def command_list
17
+ self
18
+ end
19
+
16
20
  def register_default(cmd)
17
21
  @default_command = cmd
18
22
  end
@@ -38,11 +42,11 @@ module Shelldon
38
42
  end
39
43
 
40
44
  def compile_help(cmd)
41
- res = [['Command', cmd.christian_name]]
42
- res << ['Help', cmd.help] if cmd.help
43
- res << ['Usage', "\"#{cmd.usage}\""] if cmd.usage
44
- res << ['Examples', cmd.examples] if cmd.examples
45
- res << ['Subcommands', cmd.subcommands.values.map(&:name)] unless cmd.subcommands.empty?
45
+ res = { command: cmd.christian_name }
46
+ res[:help] = cmd.help if cmd.help
47
+ res[:usage] = "\"#{cmd.usage}\"" if cmd.usage
48
+ res[:examples] = cmd.examples if cmd.examples
49
+ res[:subcommands] = cmd.subcommands.values.map(&:name).join(', ') unless cmd.subcommands.empty?
46
50
  res
47
51
  end
48
52
 
@@ -52,9 +56,9 @@ module Shelldon
52
56
  else
53
57
  cmd = find(str).first
54
58
  if cmd.show && !is_default?(cmd)
55
- compile_help(cmd)
59
+ [compile_help(cmd), cmd.sub_to_a]
56
60
  else
57
- fail StandardError
61
+ fail Shelldon::NoSuchCommandError
58
62
  end
59
63
  end
60
64
  end
@@ -65,8 +69,12 @@ module Shelldon
65
69
 
66
70
  def to_a
67
71
  @commands.values.uniq
68
- .map { |cmd| cmd.show ? [cmd.name, cmd.aliases, cmd.help] : nil }
72
+ .map { |cmd| cmd.show ? cmd.to_a : nil }
69
73
  .compact.sort_by { |(n, _, _)| n.to_s }
70
74
  end
75
+
76
+ def commands
77
+ @commands.keys
78
+ end
71
79
  end
72
80
  end
@@ -0,0 +1,33 @@
1
+ module Shelldon
2
+ class Script < Command
3
+ def self.from_dir(dir, shell)
4
+ commands = []
5
+ Dir["#{Pathname.new(dir).expand_path}/*"].reject { |f| File.directory?(f) }.each do |script|
6
+ commands << new(script, shell.command_list)
7
+ end
8
+ commands
9
+ end
10
+
11
+ def initialize(filepath, parent, &block)
12
+ @path = Pathname.new(filepath)
13
+ @name = @path.basename.to_s.split('.')[0].to_sym
14
+ @aliases = []
15
+ @subcommands = {}
16
+ @show = true
17
+ @parent = parent
18
+ instance_eval(&block) if block_given?
19
+ end
20
+
21
+ def run(tokens = [], &block)
22
+ tokens = [tokens] unless tokens.is_a?(Array)
23
+ cmd = "#{@path.expand_path}"
24
+ cmd << " #{tokens.join(' ')}" unless tokens.empty?
25
+ res = `#{@path.expand_path} #{tokens.join(' ')}`
26
+ if block_given?
27
+ instance_exec(res, &block)
28
+ else
29
+ puts res
30
+ end
31
+ end
32
+ end
33
+ end
@@ -20,8 +20,13 @@ module Shelldon
20
20
  load_config_file
21
21
  end
22
22
 
23
+ def toggle(key)
24
+ fail Shelldon::NotBooleanError unless @config[key].is_a?(Shelldon::BooleanParam)
25
+ @config[key].toggle
26
+ end
27
+
23
28
  def register(param)
24
- fail StandardError if @config.key?(param.name)
29
+ fail Shelldon::DuplicateParamError if @config.key?(param.name)
25
30
  @config[param.name] = param
26
31
  end
27
32
 
@@ -43,7 +48,7 @@ module Shelldon
43
48
  end
44
49
 
45
50
  def to_a
46
- @config.map { |k, v| [k, v.pretty] }.sort_by(&:first)
51
+ @config.values.map(&:to_a).sort_by(&:first)
47
52
  end
48
53
 
49
54
  def to_hash
@@ -54,7 +59,7 @@ module Shelldon
54
59
  if @config.key?(key.to_sym)
55
60
  true
56
61
  else
57
- raise ? fail(StandardError) : false
62
+ raise ? fail(Shelldon::NoSuchParamError) : false
58
63
  end
59
64
  end
60
65
 
@@ -67,7 +72,11 @@ module Shelldon
67
72
  # Then we validate them all
68
73
  hash.each do |k, v|
69
74
  key = k.to_sym
70
- @config.key?(key) ? set(key, v) : fail(StandardError)
75
+ if @config.key?(key)
76
+ set(key, v) unless @config[key].override || Shelldon.opts.key?(@config[key].opt)
77
+ else
78
+ fail(Shelldon::NoSuchParamError)
79
+ end
71
80
  end
72
81
  hash.each do |k, _|
73
82
  @config[k].valid? unless @config[k].val == @config[k].default
@@ -1,5 +1,9 @@
1
+ require 'logger'
2
+
1
3
  module Shelldon
2
4
  class ConfigFactory
5
+ attr_accessor :on_opts, :config_file_manager
6
+
3
7
  def self.create(shell, &block)
4
8
  ConfigFactory.new(shell, &block)
5
9
  end
@@ -1,6 +1,6 @@
1
1
  module Shelldon
2
2
  class Param
3
- attr_accessor :pretty, :default, :opt, :validator, :adjustor, :error
3
+ attr_accessor :pretty, :default, :opt, :validator, :adjustor, :error, :override
4
4
  attr_reader :name
5
5
 
6
6
  def self.create(name, &block)
@@ -13,20 +13,14 @@ module Shelldon
13
13
  end
14
14
 
15
15
  def val
16
- if @val.nil?
17
- if Shelldon.opts.key?(@opt)
18
- Shelldon.opts[@opt]
19
- else
20
- @default
21
- end
22
- else
23
- @val
24
- end
16
+ return @val if @val
17
+ return @override if @override
18
+ @val = Shelldon.opts.key?(@opt) ? Shelldon.opts[@opt] : @default
25
19
  end
26
20
 
27
21
  def val=(value)
28
22
  value = instance_exec(value, &adjustor) if adjustor
29
- valid?(value) ? @val = value : fail(StandardError)
23
+ valid?(value) ? @val = value : fail(Shelldon::InvalidParamValueError)
30
24
  end
31
25
 
32
26
  def set(value)
@@ -34,6 +28,18 @@ module Shelldon
34
28
  @val = value
35
29
  end
36
30
 
31
+ def to_a
32
+ flag =
33
+ if @opt.nil?
34
+ ''
35
+ elsif @opt.length > 1
36
+ "'--#{@opt}'"
37
+ else
38
+ "'-#{@opt}'"
39
+ end
40
+ [@name, pretty, "#{flag}"]
41
+ end
42
+
37
43
  def pretty
38
44
  if @pretty
39
45
  instance_exec(val, &@pretty)