elasticshell 0.0.2 → 0.0.4
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 +119 -55
- data/VERSION +1 -1
- data/bin/es +0 -2
- data/lib/elasticshell.rb +30 -25
- data/lib/elasticshell/client.rb +34 -13
- data/lib/elasticshell/command.rb +14 -170
- data/lib/elasticshell/commands/blank.rb +14 -0
- data/lib/elasticshell/commands/cd.rb +27 -0
- data/lib/elasticshell/commands/connect.rb +31 -0
- data/lib/elasticshell/commands/df.rb +23 -0
- data/lib/elasticshell/commands/help.rb +77 -0
- data/lib/elasticshell/commands/ls.rb +66 -0
- data/lib/elasticshell/commands/pretty.rb +21 -0
- data/lib/elasticshell/commands/pwd.rb +17 -0
- data/lib/elasticshell/commands/request.rb +81 -0
- data/lib/elasticshell/commands/request_parser.rb +77 -0
- data/lib/elasticshell/commands/set_verb.rb +23 -0
- data/lib/elasticshell/commands/unknown.rb +17 -0
- data/lib/elasticshell/scopes.rb +81 -50
- data/lib/elasticshell/scopes/cluster.rb +8 -16
- data/lib/elasticshell/scopes/global.rb +22 -23
- data/lib/elasticshell/scopes/index.rb +35 -29
- data/lib/elasticshell/scopes/mapping.rb +8 -28
- data/lib/elasticshell/scopes/nodes.rb +6 -15
- data/lib/elasticshell/shell.rb +155 -93
- data/lib/elasticshell/utils.rb +10 -0
- data/lib/elasticshell/{error.rb → utils/error.rb} +1 -0
- data/lib/elasticshell/utils/has_name.rb +14 -0
- data/lib/elasticshell/utils/has_verb.rb +15 -0
- data/lib/elasticshell/{log.rb → utils/log.rb} +1 -1
- data/lib/elasticshell/utils/recognizes_verb.rb +25 -0
- data/spec/elasticshell/client_spec.rb +55 -0
- data/spec/elasticshell/commands/blank_spec.rb +14 -0
- data/spec/elasticshell/commands/cd_spec.rb +23 -0
- data/spec/elasticshell/commands/connect_spec.rb +21 -0
- data/spec/elasticshell/commands/df_spec.rb +18 -0
- data/spec/elasticshell/commands/help_spec.rb +21 -0
- data/spec/elasticshell/commands/ls_spec.rb +24 -0
- data/spec/elasticshell/commands/pretty_spec.rb +19 -0
- data/spec/elasticshell/commands/pwd_spec.rb +14 -0
- data/spec/elasticshell/commands/request_parser_spec.rb +4 -0
- data/spec/elasticshell/commands/request_spec.rb +60 -0
- data/spec/elasticshell/commands/set_verb_spec.rb +14 -0
- data/spec/elasticshell/scopes_spec.rb +79 -0
- data/spec/elasticshell/shell_spec.rb +19 -0
- data/spec/elasticshell/utils/has_name_spec.rb +15 -0
- data/spec/elasticshell/utils/has_verb_spec.rb +24 -0
- data/spec/elasticshell/utils/recognizes_verb_spec.rb +23 -0
- data/spec/spec_helper.rb +4 -5
- data/spec/support/data.yml +45 -0
- data/spec/support/fake_output.rb +27 -0
- metadata +73 -4
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'elasticshell/scopes'
|
2
|
-
|
3
1
|
module Elasticshell
|
4
2
|
|
5
3
|
module Scopes
|
@@ -10,25 +8,18 @@ module Elasticshell
|
|
10
8
|
super("/_nodes", options)
|
11
9
|
end
|
12
10
|
|
13
|
-
def
|
14
|
-
{
|
15
|
-
|
16
|
-
|
11
|
+
def self.requests
|
12
|
+
@requests ||= {
|
13
|
+
"GET" => {
|
14
|
+
'info' => "Retreive info about the cluster's nodes.",
|
15
|
+
'stats' => "Retreive stats for the cluter's nodes.",
|
16
|
+
}
|
17
17
|
}
|
18
18
|
end
|
19
19
|
|
20
20
|
def exists?
|
21
21
|
true
|
22
22
|
end
|
23
|
-
|
24
|
-
def execute command, shell
|
25
|
-
case
|
26
|
-
when command?(command)
|
27
|
-
shell.request(:get, :index => '_nodes')
|
28
|
-
else
|
29
|
-
super(command, shell)
|
30
|
-
end
|
31
|
-
end
|
32
23
|
|
33
24
|
end
|
34
25
|
end
|
data/lib/elasticshell/shell.rb
CHANGED
@@ -1,23 +1,68 @@
|
|
1
1
|
require 'readline'
|
2
2
|
require 'uri'
|
3
3
|
|
4
|
-
require 'elasticshell/command'
|
5
|
-
require 'elasticshell/scopes'
|
6
|
-
require 'elasticshell/client'
|
7
|
-
|
8
4
|
module Elasticshell
|
9
5
|
|
10
6
|
class Shell
|
11
7
|
|
12
|
-
|
8
|
+
Settings.define(:passive_http_verb_format,
|
9
|
+
:description => "Format string for the passive HTTP verb GET. The string `%v' will be replaced by the verb.",
|
10
|
+
:default => "\e[34m%v",
|
11
|
+
:internal => true)
|
12
|
+
|
13
|
+
Settings.define(:active_http_verb_format,
|
14
|
+
:description => "Format string for the active HTTP verbs PUT, POST, and DELETE. The string `%v' will be replaced by the verb.",
|
15
|
+
:default => "\e[31m%v",
|
16
|
+
:internal => true)
|
17
|
+
|
18
|
+
Settings.define(:existing_scope_format,
|
19
|
+
:description => "Format string for an existing scope. The string `%s' will be replaced by the scope name.",
|
20
|
+
:default => "\e[32m%s",
|
21
|
+
:internal => true)
|
22
|
+
|
23
|
+
Settings.define(:missing_scope_format,
|
24
|
+
:description => "Format string for scope which doesn't exist. The string `%s' will be replaced by the scope name.",
|
25
|
+
:default => "\e[33m%s",
|
26
|
+
:internal => true)
|
27
|
+
|
28
|
+
Settings.define(:prompt_format,
|
29
|
+
:description => "Format string for the prompt. The strings `%v' and `%s' will be replaced by the (already-formatted) HTTP verb and current scope name.",
|
30
|
+
:default => "\e[1m%v %s$ \e[0m",
|
31
|
+
:internal => true)
|
32
|
+
|
33
|
+
Settings.define(:pretty_prompt_format,
|
34
|
+
:description => "Format string for the prompt when in pretty-mode. The strings `%v' and `%s' will be replaced by the (already-formatted) HTTP verb and current scope name.",
|
35
|
+
:default => "\e[1m%v %s$$ \e[0m",
|
36
|
+
:internal => true)
|
37
|
+
|
38
|
+
Settings.define(:scope_long_format,
|
39
|
+
:description => "Format string for displaying a scope in a long (`ll') listing. The string `%s' will be replaced by the scope name.",
|
40
|
+
:default => "s \e[32m%s\e[0m",
|
41
|
+
:internal => true)
|
42
|
+
|
43
|
+
Settings.define(:index_long_format,
|
44
|
+
:description => "Format string for displaying an index in a long (`ll') listing. The string `%n' will be replaced by the index name, `%T' with the total number of shards, `%S' with the number of successful shards, `%F' with the number of failed shards, `%s' with the size in bytes, `%h' with the human-readable size",
|
45
|
+
:default => "d %S/%F %h \e[32m%n\e[0m",
|
46
|
+
:internal => true)
|
13
47
|
|
14
|
-
|
48
|
+
Settings.define(:request_long_format,
|
49
|
+
:description => "Format string for displaying a request in a long (`ll') listing. The string `%r' will be replaced by the request name.",
|
50
|
+
:default => "- %r",
|
51
|
+
:internal => true)
|
52
|
+
|
53
|
+
Settings.define(:scope_format,
|
54
|
+
:description => "Format string for displaying a scope in a listing. The string `%s' will be replaced by the scope name.",
|
55
|
+
:default => "\e[32m%s\e[0m",
|
56
|
+
:internal => true)
|
57
|
+
|
58
|
+
Settings.define(:request_format,
|
59
|
+
:description => "Format string for displaying a request in a listing. The string `%r' will be replaced by the request name.",
|
60
|
+
:default => "%r",
|
61
|
+
:internal => true)
|
62
|
+
|
63
|
+
include Elasticshell::HasVerb
|
15
64
|
|
16
|
-
|
17
|
-
def verb= v
|
18
|
-
raise ArgumentError.new("'#{v}' is not a valid HTTP verb. Must be one of: #{VERBS.join(', ')}") unless VERBS.include?(v.upcase)
|
19
|
-
@verb = v.upcase
|
20
|
-
end
|
65
|
+
attr_accessor :client, :state, :only, :input, :cache, :output, :error, :line, :input_stream
|
21
66
|
|
22
67
|
attr_reader :scope
|
23
68
|
def scope= scope
|
@@ -25,35 +70,61 @@ module Elasticshell
|
|
25
70
|
proc = scope.completion_proc
|
26
71
|
Readline.completion_proc = Proc.new do |prefix|
|
27
72
|
self.state = :completion
|
28
|
-
proc.call(prefix)
|
73
|
+
proc.call(self, prefix)
|
29
74
|
end
|
30
75
|
end
|
31
76
|
|
77
|
+
def path
|
78
|
+
scope.path
|
79
|
+
end
|
80
|
+
|
81
|
+
def connected?
|
82
|
+
client.connected?
|
83
|
+
end
|
84
|
+
|
32
85
|
def initialize options={}
|
86
|
+
@interactive = false
|
33
87
|
self.state = :init
|
34
88
|
self.client = Client.new(options)
|
89
|
+
self.cache = {}
|
90
|
+
@initial_servers = (options[:servers] || [])
|
35
91
|
self.verb = (options[:verb] || 'GET')
|
36
|
-
self.scope =
|
92
|
+
self.scope = scope_from_path(options[:scope] || '/')
|
37
93
|
self.only = options[:only]
|
94
|
+
self.input_stream = (options[:input] || $stdin)
|
95
|
+
self.output = (options[:output] || $stdout)
|
96
|
+
self.error = (options[:error] || $stderr)
|
97
|
+
self.line = 0
|
98
|
+
@log_requests = (options[:log_requests] == false ? false : true)
|
38
99
|
pretty! if options[:pretty]
|
39
100
|
end
|
40
101
|
|
41
|
-
def
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
102
|
+
def scope_from_path path
|
103
|
+
if cache[path]
|
104
|
+
cache[path]
|
105
|
+
else
|
106
|
+
cache[path] = Scopes.from_path(path, :client => self.client)
|
107
|
+
end
|
47
108
|
end
|
48
109
|
|
49
|
-
def
|
50
|
-
|
110
|
+
def format name, codes, values
|
111
|
+
cs = [codes].flatten
|
112
|
+
vs = [values].flatten
|
113
|
+
raise ArgumentError.new("Must provide the same number of format codes as value strings.") unless cs.length == vs.length
|
114
|
+
Settings[name].dup.tap do |s|
|
115
|
+
cs.each_with_index do |c, index|
|
116
|
+
v = vs[index]
|
117
|
+
s.gsub!(c, v)
|
118
|
+
end
|
119
|
+
end
|
51
120
|
end
|
52
121
|
|
53
|
-
def
|
54
|
-
|
122
|
+
def prompt
|
123
|
+
verb_string = format((verb =~ /^(?:G|H)/i ? :passive_http_verb_format : :active_http_verb_format), "%v", verb)
|
124
|
+
scope_string = format((scope.exists? ? :existing_scope_format : :missing_scope_format), "%s", scope.path)
|
125
|
+
format((pretty? ? :pretty_prompt_format : :prompt_format), ["%s", "%v"], [scope_string, verb_string])
|
55
126
|
end
|
56
|
-
|
127
|
+
|
57
128
|
def pretty?
|
58
129
|
@pretty
|
59
130
|
end
|
@@ -66,6 +137,10 @@ module Elasticshell
|
|
66
137
|
@pretty = false
|
67
138
|
end
|
68
139
|
|
140
|
+
def interactive?
|
141
|
+
@interactive
|
142
|
+
end
|
143
|
+
|
69
144
|
def setup
|
70
145
|
trap("INT") do
|
71
146
|
int
|
@@ -73,62 +148,87 @@ module Elasticshell
|
|
73
148
|
|
74
149
|
Readline.completer_word_break_characters = " \t\n\"\\'`$><=|&{("
|
75
150
|
|
76
|
-
|
151
|
+
print <<EOF
|
77
152
|
Elasticshell v. #{Elasticshell.version}
|
78
153
|
Type "help" for contextual help.
|
79
154
|
EOF
|
155
|
+
@interactive = true
|
156
|
+
|
157
|
+
self.line = 1
|
80
158
|
end
|
81
159
|
|
82
160
|
def run
|
83
161
|
setup
|
162
|
+
connect
|
84
163
|
loop
|
85
164
|
end
|
86
165
|
|
166
|
+
def connect
|
167
|
+
eval_line("connect #{@initial_servers.join(',')}")
|
168
|
+
end
|
169
|
+
|
87
170
|
def loop
|
88
171
|
self.state = :read
|
89
172
|
while line = Readline.readline(prompt, true)
|
90
173
|
eval_line(line)
|
91
174
|
end
|
175
|
+
die
|
92
176
|
end
|
93
177
|
|
94
178
|
def eval_line line
|
95
179
|
begin
|
96
|
-
self.input = line.strip
|
97
|
-
self.command = Command.new(self, input)
|
98
180
|
self.state = :eval
|
99
|
-
self.
|
181
|
+
self.input = line.strip
|
182
|
+
command.evaluate!
|
100
183
|
rescue ::Elasticshell::Error => e
|
101
|
-
|
184
|
+
error.puts e.message
|
102
185
|
end
|
103
186
|
self.state = :read
|
187
|
+
self.line += 1
|
188
|
+
self
|
104
189
|
end
|
105
190
|
|
191
|
+
def command
|
192
|
+
matching_command_class_name = Commands::PRIORITY.detect do |command_class_name|
|
193
|
+
Commands.const_get(command_class_name).matches?(input)
|
194
|
+
end
|
195
|
+
|
196
|
+
# We should never hit the following ArgumentError as there
|
197
|
+
# exists a catch-all command: Unknown
|
198
|
+
raise ArgumentError.new("Could not parse command: '#{input}'") unless matching_command_class_name
|
199
|
+
|
200
|
+
Commands.const_get(matching_command_class_name).new(self, input)
|
201
|
+
end
|
202
|
+
|
106
203
|
def print obj, ignore_only=false
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
204
|
+
self.output.puts(format_output(obj, ignore_only))
|
205
|
+
end
|
206
|
+
|
207
|
+
def format_output obj, ignore_only=false
|
208
|
+
case
|
209
|
+
when self.only == true && !ignore_only
|
210
|
+
format_output(obj, true)
|
211
|
+
when self.only && !ignore_only
|
212
|
+
format_only_part_of(obj)
|
213
|
+
when obj.nil?
|
214
|
+
nil
|
215
|
+
when String, Fixnum
|
216
|
+
obj
|
217
|
+
when pretty?
|
218
|
+
JSON.pretty_generate(obj)
|
119
219
|
else
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
end
|
220
|
+
obj.to_json
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def format_only_part_of obj
|
225
|
+
only_parts = self.only.to_s.split('.')
|
226
|
+
obj_to_print = obj
|
227
|
+
while obj_to_print && only_parts.size > 0
|
228
|
+
this_only = only_parts.shift
|
229
|
+
obj_to_print = (obj_to_print || {})[this_only]
|
131
230
|
end
|
231
|
+
format_output(obj_to_print, true)
|
132
232
|
end
|
133
233
|
|
134
234
|
def int
|
@@ -140,51 +240,13 @@ EOF
|
|
140
240
|
end
|
141
241
|
end
|
142
242
|
|
143
|
-
def clear_line
|
144
|
-
while Readline.point > 0
|
145
|
-
$stdin.write("\b \b")
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
243
|
def die
|
150
|
-
|
151
|
-
print("C-d...quitting")
|
152
|
-
exit()
|
244
|
+
raise ShellError.new("C-d...quitting")
|
153
245
|
end
|
154
246
|
|
155
|
-
def
|
156
|
-
|
157
|
-
|
158
|
-
c_and_q = parts[0]
|
159
|
-
c, q = c_and_q.split('?')
|
160
|
-
o = {}
|
161
|
-
URI.decode_www_form(q || '').each do |k, v|
|
162
|
-
o[k] = v
|
163
|
-
end
|
164
|
-
|
165
|
-
path = parts[1]
|
166
|
-
case
|
167
|
-
when path && File.exist?(path) && File.readable?(path)
|
168
|
-
b = File.read(path)
|
169
|
-
when path && path == '-'
|
170
|
-
b = $stdin.gets(nil)
|
171
|
-
when path
|
172
|
-
b = path
|
173
|
-
else
|
174
|
-
# b = (command.split(' ', 2).last || '')
|
175
|
-
b = ''
|
176
|
-
end
|
177
|
-
|
178
|
-
[c, o, b]
|
179
|
-
end
|
180
|
-
|
181
|
-
def request verb, params={}
|
182
|
-
c, o, b = command_and_query_and_body(input)
|
183
|
-
body = (params.delete(:body) || b || '')
|
184
|
-
print(client.request(verb, params.merge(:op => c), o, b))
|
247
|
+
def log_requests?
|
248
|
+
@log_requests
|
185
249
|
end
|
186
250
|
|
187
251
|
end
|
188
|
-
|
189
252
|
end
|
190
|
-
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'elasticshell/utils/error'
|
2
|
+
require 'elasticshell/utils/log'
|
3
|
+
|
4
|
+
module Elasticshell
|
5
|
+
|
6
|
+
autoload :HasName, 'elasticshell/utils/has_name'
|
7
|
+
autoload :HasVerb, 'elasticshell/utils/has_verb'
|
8
|
+
autoload :RecognizesVerb, 'elasticshell/utils/recognizes_verb'
|
9
|
+
|
10
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Elasticshell
|
2
|
+
module HasName
|
3
|
+
|
4
|
+
FORBIDDEN_NAME_CHARS = %r![/\s]!
|
5
|
+
|
6
|
+
attr_reader :name
|
7
|
+
|
8
|
+
def name= new_name
|
9
|
+
raise ArgumentError.new("Invalid index name: '#{new_name}'") if new_name =~ FORBIDDEN_NAME_CHARS
|
10
|
+
@name = new_name
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Elasticshell
|
2
|
+
module RecognizesVerb
|
3
|
+
|
4
|
+
VERB_RE = "(?:HEAD|GET|PUT|POST|DELETE)"
|
5
|
+
|
6
|
+
def verb_re
|
7
|
+
RecognizesVerb::VERB_RE
|
8
|
+
end
|
9
|
+
|
10
|
+
def canonicalize_verb v
|
11
|
+
case v.to_s
|
12
|
+
when /^G/i then "GET"
|
13
|
+
when /^PO/i then "POST"
|
14
|
+
when /^PU/i then "PUT"
|
15
|
+
when /^D/i then "DELETE"
|
16
|
+
when /^H/i then "HEAD"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def is_http_verb? s
|
21
|
+
s =~ Regexp.new("^" + verb_re + "$", true)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|