djinni 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/lib/djinni.rb +207 -0
  3. data/lib/djinni_wish.rb +28 -0
  4. 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
@@ -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: