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.
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: