djinni 0.1.0
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.
- checksums.yaml +7 -0
- data/lib/djinni.rb +207 -0
- data/lib/djinni_wish.rb +28 -0
- metadata +48 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 456765bdb284f65c1bb9ce5369c628db200411a8
|
4
|
+
data.tar.gz: 9123c45ea10d64f348ef1e5db41b007f56a2fb75
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ae78efcc88af21ce1abc2b26430a590c9e10ec7168486fe41b8d5d618157a03697cf69f952528274d6a8b53f88d790de490acc1ff98130b3b26436258b9333a2
|
7
|
+
data.tar.gz: e92ecba723083a54092408b6da6b501068b1590d1341c65d3ab6bffee6496aad955b52ae9c4f3fa18c6ca7d8914e9fbddf2560a9897e9ba91c734fb537dcbaa5
|
data/lib/djinni.rb
ADDED
@@ -0,0 +1,207 @@
|
|
1
|
+
require "io/console"
|
2
|
+
require "pathname"
|
3
|
+
require "terminfo"
|
4
|
+
|
5
|
+
require_relative "djinni_wish"
|
6
|
+
|
7
|
+
class Exit
|
8
|
+
GOOD = 0
|
9
|
+
UNKNOWN_WISH = 1
|
10
|
+
end
|
11
|
+
|
12
|
+
class Djinni
|
13
|
+
def grant_wish(input)
|
14
|
+
return "" if (input.nil? || input.empty?)
|
15
|
+
|
16
|
+
case input[-1]
|
17
|
+
when "\x03" # ^C
|
18
|
+
puts if (@interactive)
|
19
|
+
return ""
|
20
|
+
when "\x04" # ^D
|
21
|
+
return "\x04"
|
22
|
+
when "\x7F" # Backspace
|
23
|
+
return input[0..-3]
|
24
|
+
when "\f" # ^L
|
25
|
+
system("clear") if (@interactive)
|
26
|
+
return input[0..-2]
|
27
|
+
when "\t" # Tab
|
28
|
+
input = input[0..-2]
|
29
|
+
if (input.include?(" "))
|
30
|
+
name, args = input.split(" ", 2)
|
31
|
+
return input if (!@wishes.has_key?(name))
|
32
|
+
wish = @wishes[name]
|
33
|
+
return "#{name} #{wish.tab_complete(args)}"
|
34
|
+
else
|
35
|
+
wishes = @wishes.keys
|
36
|
+
wishes.push("help")
|
37
|
+
wishes.push("history")
|
38
|
+
wishes.sort.each do |wish|
|
39
|
+
if (wish.start_with?(input))
|
40
|
+
return wish
|
41
|
+
end
|
42
|
+
end
|
43
|
+
return input
|
44
|
+
end
|
45
|
+
when /\r|\n/ # Enter
|
46
|
+
input.strip!
|
47
|
+
puts if (@interactive)
|
48
|
+
return "" if (input.empty?)
|
49
|
+
|
50
|
+
# Only keep newest wish if repeat
|
51
|
+
@history.delete(input)
|
52
|
+
@history.push(input)
|
53
|
+
@hist_index = nil
|
54
|
+
|
55
|
+
name, args = input.split(" ", 2)
|
56
|
+
|
57
|
+
if ((name == "help") || (name == "?"))
|
58
|
+
print_help
|
59
|
+
return ""
|
60
|
+
end
|
61
|
+
|
62
|
+
if ((name == "history") || (name == "hist"))
|
63
|
+
print_history
|
64
|
+
return ""
|
65
|
+
end
|
66
|
+
|
67
|
+
@wishes.sort.map do |aliaz, wish|
|
68
|
+
if (aliaz == name)
|
69
|
+
wish.execute(args)
|
70
|
+
return ""
|
71
|
+
end
|
72
|
+
end
|
73
|
+
return nil
|
74
|
+
when "\e" # Arrow keys
|
75
|
+
code = "\e"
|
76
|
+
Thread.new do
|
77
|
+
code += STDIN.getch + STDIN.getch
|
78
|
+
end.join(0.001).kill
|
79
|
+
|
80
|
+
case code
|
81
|
+
when "\e[A" # Up arrow
|
82
|
+
@hist_index = @history.size if (@hist_index.nil?)
|
83
|
+
@hist_index = 1 if (@hist_index == 0)
|
84
|
+
@hist_index -= 1
|
85
|
+
return @history[@hist_index]
|
86
|
+
when "\e[B" # Up arrow
|
87
|
+
@hist_index = @history.size if (@hist_index.nil?)
|
88
|
+
@hist_index += 1
|
89
|
+
if (@hist_index < @history.size)
|
90
|
+
return @history[@hist_index]
|
91
|
+
else
|
92
|
+
@hist_index = @history.size
|
93
|
+
return ""
|
94
|
+
end
|
95
|
+
when "\e[C" # Right arrow
|
96
|
+
# TODO maybe
|
97
|
+
return input[0..-2]
|
98
|
+
when "\e[D" # Left arrow
|
99
|
+
# TODO maybe
|
100
|
+
return input[0..-2]
|
101
|
+
else
|
102
|
+
return input[0..-2]
|
103
|
+
end
|
104
|
+
else
|
105
|
+
return input
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def initialize(interactive = false)
|
110
|
+
@wishes = Hash.new
|
111
|
+
@history = Array.new
|
112
|
+
@hist_index = nil
|
113
|
+
@interactive = interactive
|
114
|
+
@loaded_from = Array.new
|
115
|
+
@width = TermInfo.screen_size[1]
|
116
|
+
|
117
|
+
Signal.trap(
|
118
|
+
"SIGWINCH",
|
119
|
+
proc do
|
120
|
+
@width = TermInfo.screen_size[1]
|
121
|
+
end
|
122
|
+
)
|
123
|
+
end
|
124
|
+
|
125
|
+
def load_wishes(dir)
|
126
|
+
return if @loaded_from.include?(dir)
|
127
|
+
|
128
|
+
path = Pathname.new(dir).expand_path
|
129
|
+
|
130
|
+
# puts "Loading wishes from #{path}"
|
131
|
+
Dir["#{path}/*.rb"].each do |file|
|
132
|
+
# puts "Loading #{clas}"
|
133
|
+
require_relative file
|
134
|
+
|
135
|
+
%x(
|
136
|
+
\grep -E "class .* \< DjinniWish" #{file} | \
|
137
|
+
awk '{print $2}'
|
138
|
+
).each_line do |clas|
|
139
|
+
next if (clas.nil?)
|
140
|
+
clas.strip!
|
141
|
+
next if (clas.empty?)
|
142
|
+
|
143
|
+
wish = nil
|
144
|
+
begin
|
145
|
+
wish = Object::const_get(clas).new
|
146
|
+
rescue NameError => e
|
147
|
+
puts "Unknown wish class #{clas}!"
|
148
|
+
exit Exit::UNKNOWN_WISH
|
149
|
+
end
|
150
|
+
|
151
|
+
next if (wish.nil?)
|
152
|
+
|
153
|
+
wish.aliases.each do |aliaz|
|
154
|
+
@wishes[aliaz] = wish
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
@loaded_from.push(dir)
|
160
|
+
end
|
161
|
+
|
162
|
+
def print_help
|
163
|
+
@wishes.sort.map do |aliaz, wish|
|
164
|
+
puts "#{aliaz}\t#{wish.description}"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def print_history
|
169
|
+
@history.each do |wish|
|
170
|
+
puts wish
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def prompt(prompt_sym = "$ ")
|
175
|
+
@interactive = true
|
176
|
+
|
177
|
+
buffer = ""
|
178
|
+
loop do
|
179
|
+
blank_line = Array.new(@width, " ").join
|
180
|
+
fill_len = @width - prompt_sym.length - buffer.length + 1
|
181
|
+
|
182
|
+
# Handle long line-wrapped prompts
|
183
|
+
lines = (prompt_sym.length + buffer.length) / @width
|
184
|
+
lines.times do
|
185
|
+
print "\r#{blank_line}"
|
186
|
+
print "\e[F"
|
187
|
+
end
|
188
|
+
|
189
|
+
# Redisplay prompt
|
190
|
+
print "\r#{blank_line}"
|
191
|
+
print "\r#{prompt_sym}#{buffer}"
|
192
|
+
|
193
|
+
# Process input
|
194
|
+
buffer = grant_wish(buffer + STDIN.getch)
|
195
|
+
|
196
|
+
if (buffer.nil?)
|
197
|
+
puts "Wish not found!"
|
198
|
+
buffer = ""
|
199
|
+
end
|
200
|
+
|
201
|
+
# Exit on ^D
|
202
|
+
if (buffer == "\x04")
|
203
|
+
return
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
data/lib/djinni_wish.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
class DjinniWish
|
2
|
+
class InterfaceNotImplementedError < NoMethodError
|
3
|
+
end
|
4
|
+
|
5
|
+
def aliases
|
6
|
+
raise InterfaceNotImplementedError.new(
|
7
|
+
"aliases() not implemented!"
|
8
|
+
)
|
9
|
+
end
|
10
|
+
|
11
|
+
def description
|
12
|
+
raise InterfaceNotImplementedError.new(
|
13
|
+
"description() not implemented!"
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def execute(args)
|
18
|
+
raise InterfaceNotImplementedError.new(
|
19
|
+
"execute() not implemented!"
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def tab_complete(input)
|
24
|
+
raise InterfaceNotImplementedError.new(
|
25
|
+
"tab_complete() not implemented!"
|
26
|
+
)
|
27
|
+
end
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: djinni
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Miles Whittaker
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-08-24 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: This Ruby gem accepts user input and handles commands (a.k.a. wishes).
|
14
|
+
It dynamically loads user-defined wishes, maintains a history, and provides tab
|
15
|
+
completion.
|
16
|
+
email: mjwhitta@gmail.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- lib/djinni.rb
|
22
|
+
- lib/djinni_wish.rb
|
23
|
+
homepage: http://rubygems.org/gems/djinni
|
24
|
+
licenses:
|
25
|
+
- GPL-3.0
|
26
|
+
metadata: {}
|
27
|
+
post_install_message:
|
28
|
+
rdoc_options: []
|
29
|
+
require_paths:
|
30
|
+
- lib
|
31
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
requirements: []
|
42
|
+
rubyforge_project:
|
43
|
+
rubygems_version: 2.4.8
|
44
|
+
signing_key:
|
45
|
+
specification_version: 4
|
46
|
+
summary: A Ruby command handler
|
47
|
+
test_files: []
|
48
|
+
has_rdoc:
|