shell_shock 0.0.1 → 0.0.2
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.
- data/README.rdoc +65 -5
- data/lib/shell_shock/context.rb +49 -16
- metadata +23 -9
data/README.rdoc
CHANGED
@@ -14,10 +14,70 @@ A library for creating command line shell application.
|
|
14
14
|
|
15
15
|
== Usage
|
16
16
|
|
17
|
-
|
17
|
+
The basic idea is that you create classes with the ShellShock::Context mixin. Start a shell by instantiating the class and executing the push method. Any number of nested shells may be pushed and exit falls back to the previous shell.
|
18
18
|
|
19
|
-
|
19
|
+
These classes represent a shell context with a number of commands registered. These commands are matched with tab completion. In addition to registered commands, 'exit', 'quit', 'help' and '?' are always available.
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
Each command must have an execute method that accepts a string (the remaining content after the command name). Commands may also implement usage and help methods. They may also implement a completion method so may have arbitrarily complex completion rules per parameter.
|
22
|
+
|
23
|
+
Here's a sample shell application using shell shock. It has 'cd' and 'cat' commands. 'cd' performs tab completion for directories and execution enters a nested context. 'cat' performs tab completion for files and execution dumps the file content.
|
24
|
+
|
25
|
+
require 'shell_shock/context'
|
26
|
+
|
27
|
+
class CatFileCommand
|
28
|
+
def initialize path
|
29
|
+
@path = path
|
30
|
+
end
|
31
|
+
|
32
|
+
def usage
|
33
|
+
'<file name>'
|
34
|
+
end
|
35
|
+
|
36
|
+
def help
|
37
|
+
'displays the content of a file'
|
38
|
+
end
|
39
|
+
|
40
|
+
def completion text
|
41
|
+
Dir.glob("#{@path}/#{text}*").select {|f| File.file?(f) }
|
42
|
+
end
|
43
|
+
|
44
|
+
def execute path
|
45
|
+
File.open(path) {|f| f.each_with_index {|l,i| puts "#{i+1}: #{l}" } }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class ChangeDirectoryCommand
|
50
|
+
def initialize path
|
51
|
+
@path = path
|
52
|
+
end
|
53
|
+
|
54
|
+
def usage
|
55
|
+
'<directory name>'
|
56
|
+
end
|
57
|
+
|
58
|
+
def help
|
59
|
+
'switches to a new shell context in the specified directory'
|
60
|
+
end
|
61
|
+
|
62
|
+
def completion text
|
63
|
+
Dir.glob("#{@path}/#{text}*").select {|f| File.directory?(f) }
|
64
|
+
end
|
65
|
+
|
66
|
+
def execute text
|
67
|
+
DirectoryContext.new(text).push
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class DirectoryContext
|
72
|
+
include ShellShock::Context
|
73
|
+
|
74
|
+
def initialize path
|
75
|
+
@prompt_text = "#{path} > "
|
76
|
+
@commands = {
|
77
|
+
'cd' => ChangeDirectoryCommand.new(path),
|
78
|
+
'cat' => CatFileCommand.new(path)
|
79
|
+
}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
DirectoryContext.new('.').push
|
data/lib/shell_shock/context.rb
CHANGED
@@ -1,15 +1,28 @@
|
|
1
|
-
require 'readline'
|
2
1
|
require 'rubygems'
|
3
|
-
require '
|
2
|
+
require 'readline'
|
4
3
|
|
5
4
|
module ShellShock
|
6
5
|
module Context
|
7
6
|
def refresh
|
8
|
-
|
9
|
-
Readline.completion_proc = lambda do |text|
|
10
|
-
(commands + @commands.keys + ['quit', 'exit']).grep( /^#{Regexp.escape(text)}/ ).sort
|
11
|
-
end
|
7
|
+
refresh_commands if respond_to?(:refresh_commands)
|
12
8
|
Readline.completer_word_break_characters = ''
|
9
|
+
Readline.completion_proc = lambda do |word|
|
10
|
+
name, *words = word.scan(/[\w?]+/)
|
11
|
+
if name
|
12
|
+
command = @commands[name]
|
13
|
+
if command
|
14
|
+
if command.respond_to?(:completion)
|
15
|
+
rest = words || []
|
16
|
+
completions = command.completion(rest.join(' ')).map {|c| "#{name} #{c}" }
|
17
|
+
return completions
|
18
|
+
else
|
19
|
+
return []
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
@commands.keys.grep( /^#{Regexp.escape(word)}/ ).sort
|
25
|
+
end
|
13
26
|
end
|
14
27
|
|
15
28
|
def push
|
@@ -18,14 +31,14 @@ module ShellShock
|
|
18
31
|
while line = Readline.readline(@prompt_text, true)
|
19
32
|
line.strip!
|
20
33
|
case line
|
21
|
-
when
|
22
|
-
return
|
23
|
-
when /(\w+) (.*)/
|
34
|
+
when /^([\w?]+) (.*)/
|
35
|
+
return if ['quit', 'exit'].include?($1)
|
24
36
|
process_command $1, $2
|
25
|
-
when
|
37
|
+
when /^([\w?]+)/
|
38
|
+
return if ['quit', 'exit'].include?($1)
|
26
39
|
process_command $1
|
27
40
|
else
|
28
|
-
puts
|
41
|
+
puts "can not process line \"#{line}\""
|
29
42
|
end
|
30
43
|
puts
|
31
44
|
refresh
|
@@ -36,16 +49,36 @@ module ShellShock
|
|
36
49
|
puts
|
37
50
|
end
|
38
51
|
|
52
|
+
def display_help_for_command command_name
|
53
|
+
command = @commands[command_name]
|
54
|
+
if command
|
55
|
+
@io.say "Command \"#{command_name}\""
|
56
|
+
@io.say "Usage: #{command_name} #{command.usage}" if command.respond_to?(:usage)
|
57
|
+
@io.say "Help:\n #{command.help}" if command.respond_to?(:help)
|
58
|
+
else
|
59
|
+
puts "no help available for command \"#{command_name}\""
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def process_help command=nil
|
64
|
+
if command
|
65
|
+
display_help_for_command command
|
66
|
+
else
|
67
|
+
@io.say "Available commands:"
|
68
|
+
@commands.keys.sort.each { |command| @io.say command }
|
69
|
+
@io.say
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
39
73
|
def process_command name, parameter=nil
|
40
|
-
|
41
|
-
|
42
|
-
send(m, parameter)
|
74
|
+
if ['?', 'help'].include?(name)
|
75
|
+
process_help parameter
|
43
76
|
return
|
44
|
-
end
|
77
|
+
end
|
45
78
|
if @commands[name]
|
46
79
|
@commands[name].execute parameter
|
47
80
|
else
|
48
|
-
@io.say
|
81
|
+
@io.say "unknown command \"#{name}\""
|
49
82
|
end
|
50
83
|
end
|
51
84
|
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 2
|
9
|
+
version: 0.0.2
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Mark Ryall
|
@@ -14,23 +14,37 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-04-
|
17
|
+
date: 2010-04-13 00:00:00 +10:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
|
-
name:
|
21
|
+
name: rake
|
22
22
|
prerelease: false
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - ~>
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
segments:
|
28
|
-
-
|
29
|
-
-
|
30
|
-
-
|
31
|
-
version:
|
32
|
-
type: :
|
28
|
+
- 0
|
29
|
+
- 8
|
30
|
+
- 7
|
31
|
+
version: 0.8.7
|
32
|
+
type: :development
|
33
33
|
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: gemesis
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ~>
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
- 0
|
44
|
+
- 3
|
45
|
+
version: 0.0.3
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id002
|
34
48
|
description: |
|
35
49
|
This is just some code extracted from a few command line gems i've created (shh and cardigan).
|
36
50
|
|