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.
Files changed (52) hide show
  1. data/README.rdoc +119 -55
  2. data/VERSION +1 -1
  3. data/bin/es +0 -2
  4. data/lib/elasticshell.rb +30 -25
  5. data/lib/elasticshell/client.rb +34 -13
  6. data/lib/elasticshell/command.rb +14 -170
  7. data/lib/elasticshell/commands/blank.rb +14 -0
  8. data/lib/elasticshell/commands/cd.rb +27 -0
  9. data/lib/elasticshell/commands/connect.rb +31 -0
  10. data/lib/elasticshell/commands/df.rb +23 -0
  11. data/lib/elasticshell/commands/help.rb +77 -0
  12. data/lib/elasticshell/commands/ls.rb +66 -0
  13. data/lib/elasticshell/commands/pretty.rb +21 -0
  14. data/lib/elasticshell/commands/pwd.rb +17 -0
  15. data/lib/elasticshell/commands/request.rb +81 -0
  16. data/lib/elasticshell/commands/request_parser.rb +77 -0
  17. data/lib/elasticshell/commands/set_verb.rb +23 -0
  18. data/lib/elasticshell/commands/unknown.rb +17 -0
  19. data/lib/elasticshell/scopes.rb +81 -50
  20. data/lib/elasticshell/scopes/cluster.rb +8 -16
  21. data/lib/elasticshell/scopes/global.rb +22 -23
  22. data/lib/elasticshell/scopes/index.rb +35 -29
  23. data/lib/elasticshell/scopes/mapping.rb +8 -28
  24. data/lib/elasticshell/scopes/nodes.rb +6 -15
  25. data/lib/elasticshell/shell.rb +155 -93
  26. data/lib/elasticshell/utils.rb +10 -0
  27. data/lib/elasticshell/{error.rb → utils/error.rb} +1 -0
  28. data/lib/elasticshell/utils/has_name.rb +14 -0
  29. data/lib/elasticshell/utils/has_verb.rb +15 -0
  30. data/lib/elasticshell/{log.rb → utils/log.rb} +1 -1
  31. data/lib/elasticshell/utils/recognizes_verb.rb +25 -0
  32. data/spec/elasticshell/client_spec.rb +55 -0
  33. data/spec/elasticshell/commands/blank_spec.rb +14 -0
  34. data/spec/elasticshell/commands/cd_spec.rb +23 -0
  35. data/spec/elasticshell/commands/connect_spec.rb +21 -0
  36. data/spec/elasticshell/commands/df_spec.rb +18 -0
  37. data/spec/elasticshell/commands/help_spec.rb +21 -0
  38. data/spec/elasticshell/commands/ls_spec.rb +24 -0
  39. data/spec/elasticshell/commands/pretty_spec.rb +19 -0
  40. data/spec/elasticshell/commands/pwd_spec.rb +14 -0
  41. data/spec/elasticshell/commands/request_parser_spec.rb +4 -0
  42. data/spec/elasticshell/commands/request_spec.rb +60 -0
  43. data/spec/elasticshell/commands/set_verb_spec.rb +14 -0
  44. data/spec/elasticshell/scopes_spec.rb +79 -0
  45. data/spec/elasticshell/shell_spec.rb +19 -0
  46. data/spec/elasticshell/utils/has_name_spec.rb +15 -0
  47. data/spec/elasticshell/utils/has_verb_spec.rb +24 -0
  48. data/spec/elasticshell/utils/recognizes_verb_spec.rb +23 -0
  49. data/spec/spec_helper.rb +4 -5
  50. data/spec/support/data.yml +45 -0
  51. data/spec/support/fake_output.rb +27 -0
  52. metadata +73 -4
@@ -0,0 +1,14 @@
1
+ module Elasticshell
2
+ module Commands
3
+ class Blank < Command
4
+
5
+ def self.matches? input
6
+ input.empty?
7
+ end
8
+
9
+ def evaluate!
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,27 @@
1
+ module Elasticshell
2
+ module Commands
3
+ class Cd < Command
4
+
5
+ def self.matches? input
6
+ input =~ /^cd/
7
+ end
8
+
9
+ def evaluate!
10
+ if input =~ /^cd$/
11
+ shell.scope = Scopes.global(:client => shell.client)
12
+ return
13
+ end
14
+
15
+ return unless input =~ /^cd\s+(.+)$/
16
+ scope = $1
17
+ if scope =~ %r!^/!
18
+ shell.scope = Scopes.from_path(scope, :client => shell.client)
19
+ else
20
+ shell.scope = Scopes.from_path(File.expand_path(File.join(shell.scope.path, scope)), :client => shell.client)
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
27
+
@@ -0,0 +1,31 @@
1
+ require 'uri'
2
+
3
+ module Elasticshell
4
+ module Commands
5
+ class Connect < Command
6
+
7
+ def self.matches? input
8
+ input =~ /^connect(?: |$)/i
9
+ end
10
+
11
+ def evaluate!
12
+ servers = (input.split(/\s+/, 2)[1] || '').split(/\s+/)
13
+ if servers.empty?
14
+ shell.client.connect()
15
+ else
16
+ servers.each do |server|
17
+ begin
18
+ uri = URI.parse(server + "/")
19
+ rescue => e
20
+ raise ArgumentError.new("#{server} is not a valid URI")
21
+ end
22
+ raise ArgumentError.new("#{server} is not a valid URI for an ElasticSearch server") unless uri.path == '/'
23
+ end
24
+ shell.client.connect(:servers => servers)
25
+ end
26
+ end
27
+
28
+ end
29
+ end
30
+ end
31
+
@@ -0,0 +1,23 @@
1
+ module Elasticshell
2
+ module Commands
3
+ class Df < Command
4
+
5
+ def self.matches? input
6
+ input =~ /^df/i
7
+ end
8
+
9
+ def evaluate!
10
+ be_connected!
11
+ global = shell.scope_from_path("/")
12
+ global.status["indices"].each_pair do |index_name, data|
13
+ size = data["index"]["size_in_bytes"]
14
+ human_size = data["index"]["size"]
15
+ shell.print("%6s %6s \e[32m%s\e[0m" % [size, human_size, index_name])
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
22
+
23
+
@@ -0,0 +1,77 @@
1
+ module Elasticshell
2
+ module Commands
3
+ class Help < Command
4
+
5
+ def self.matches? input
6
+ input =~ /^help/i
7
+ end
8
+
9
+ def long?
10
+ input =~ /help +help/i
11
+ end
12
+
13
+ def evaluate!
14
+ if long?
15
+ shell.print <<HEADER
16
+
17
+ INTRODUCTION
18
+
19
+ Elasticshell wraps Elasticsearch's HTTP REST API with a convenient
20
+ command-line shell.
21
+
22
+ Elasticshell will try by default to connect to a locally running copy
23
+ of Elasticsearch on its default port (9200) when it starts up. If
24
+ Elasticshell cannot connect on startup, it will print an error
25
+ message. You can make Elasticshell connect to a different set of
26
+ servers using the `connect' command below.
27
+
28
+ COMMANDS
29
+ HEADER
30
+ end
31
+ shell.print <<HELP
32
+
33
+ pwd
34
+ Print the current scope.
35
+
36
+ connect [SERVER[,SERVER]...]
37
+ Connect to the given list of comma-separated Elasticseach servers.
38
+
39
+ ls
40
+ Show what common requests or child scopes are within the
41
+ current scope. Try `ll' for a long listing.
42
+
43
+ cd [SCOPE]
44
+ Change to the given scope. Current scope is reflected in the
45
+ prompt (it's `#{shell.scope.path}' right now).
46
+
47
+ [get|post|put|delete]
48
+ Set the default HTTP verb (currently `#{shell.verb}').
49
+
50
+ df
51
+ Show a brief listing of disk usage by index.
52
+
53
+ help
54
+ Show contextual help. Try `help help' for even more detail.
55
+
56
+ [VERB] PATH[?QUERY] [BODY]
57
+ Send an HTTP request using the given VERB to the given PATH, including
58
+ QUERY string and BODY if given. BODY can be the name of a local file on
59
+ disk or `-' to read from STDIN. If no verb is given, use the default
60
+ verb (currently `#{shell.verb}').
61
+
62
+ HELP
63
+ shell.print("Try `help help' for more detailed help with examples.") unless long?
64
+ if long?
65
+ shell.print <<FOOTER
66
+
67
+ EXAMPLES
68
+ TBD.
69
+ FOOTER
70
+ end
71
+ end
72
+
73
+ end
74
+ end
75
+ end
76
+
77
+
@@ -0,0 +1,66 @@
1
+ module Elasticshell
2
+ module Commands
3
+ class Ls < Command
4
+
5
+ attr_accessor :scope
6
+
7
+ def self.matches? input
8
+ input =~ /^l(s|l|a)?(?: .+)?$/i
9
+ end
10
+
11
+ def evaluate!
12
+ be_connected!
13
+ if input =~ /^l(?:s|l|a)? +(.+)$/
14
+ self.scope = shell.scope_from_path($1)
15
+ else
16
+ self.scope = shell.scope
17
+ end
18
+ self.scope.refresh!
19
+ input =~ /^l(?:l|a)/ ? ll! : ls!
20
+ end
21
+
22
+ def sort array
23
+ with_underscores = array.find_all { |element| element =~ /^_/ }
24
+ without_underscores = array.find_all { |element| element =~ /^[^_]/ }
25
+ without_underscores.sort + with_underscores.sort
26
+ end
27
+
28
+ def ll!
29
+ sort(scope.scopes).each do |scope_name|
30
+ case
31
+ when scope.path == '/' && scope.indices.include?(scope_name)
32
+ index = shell.scope_from_path("/#{scope_name}")
33
+ total_shards = index.status["_shards"]["total"]
34
+ succ_shards = index.status["_shards"]["successful"]
35
+ fail_shards = index.status["_shards"]["failed"]
36
+ size = index.status["indices"][scope_name]["index"]["size_in_bytes"]
37
+ human_size = index.status["indices"][scope_name]["index"]["size"]
38
+ num_docs = index.status["indices"][scope_name]["docs"]["num_docs"]
39
+ shell.print("i %10s %6s %6s \e[32m%s\e[0m" % ["#{total_shards}/#{succ_shards}/#{fail_shards}", num_docs, human_size, scope_name])
40
+ when scope.class == Scopes::Index && scope.mappings.include?(scope_name)
41
+ shell.print("m \e[32m%s\e[0m" % [scope_name])
42
+ else
43
+ shell.print shell.format(:scope_long_format, "%s", scope_name)
44
+ end
45
+ end
46
+ sort(scope.request_names).each do |request|
47
+ shell.print shell.format(:request_long_format, "%r", request)
48
+ end
49
+ end
50
+
51
+ def ls!
52
+ output = []
53
+ sort(scope.scopes).map do |scope|
54
+ output << shell.format(:scope_format, "%s", scope)
55
+ end
56
+ sort(scope.request_names).map do |request|
57
+ output << shell.format(:request_format, "%r", request)
58
+ end
59
+ shell.print output.join(' ')
60
+ end
61
+
62
+ end
63
+ end
64
+ end
65
+
66
+
@@ -0,0 +1,21 @@
1
+ module Elasticshell
2
+ module Commands
3
+ class Pretty < Command
4
+
5
+ def self.matches? input
6
+ input =~ /^pretty/i
7
+ end
8
+
9
+ def evaluate!
10
+ if shell.pretty?
11
+ shell.not_pretty!
12
+ else
13
+ shell.pretty!
14
+ end
15
+ end
16
+
17
+ end
18
+ end
19
+ end
20
+
21
+
@@ -0,0 +1,17 @@
1
+ module Elasticshell
2
+ module Commands
3
+ class Pwd < Command
4
+
5
+ def self.matches? input
6
+ input =~ /^pwd/i
7
+ end
8
+
9
+ def evaluate!
10
+ shell.print shell.scope.path
11
+ end
12
+
13
+ end
14
+ end
15
+ end
16
+
17
+
@@ -0,0 +1,81 @@
1
+ module Elasticshell
2
+ module Commands
3
+ class Request < Command
4
+
5
+ extend RecognizesVerb
6
+
7
+ attr_accessor :response, :request
8
+
9
+ def self.matches? input
10
+ input =~ Regexp.new("^(#{verb_re}\s+)?.", true)
11
+ end
12
+
13
+ def parse!
14
+ self.request = Parser.new(self).request
15
+ end
16
+
17
+ def perform_request!
18
+ self.response = shell.client.request(request[:verb], {:op => request[:path] }, request[:query_options].merge(:log => shell.log_requests?), request[:body])
19
+ end
20
+
21
+ def pipe?
22
+ pipe_to_ruby? || pipe_to_irb? || pipe_to_file?
23
+ end
24
+
25
+ def pipe_to_file?
26
+ input =~ /\s>\s*.+$/
27
+ end
28
+
29
+ def output_filename
30
+ input =~ /\s>\s*(.+)$/
31
+ $1
32
+ end
33
+
34
+ def pipe_to_file!
35
+ File.open(output_filename, 'w') { |f| f.puts response.to_s }
36
+ end
37
+
38
+ def pipe_to_irb?
39
+ input =~ /\s\|\s*$/
40
+ end
41
+
42
+ def irb!
43
+ require 'ripl'
44
+ Ripl.start(:binding => binding)
45
+ end
46
+
47
+ def pipe_to_ruby?
48
+ input =~ /\s\|\s*\S+/
49
+ end
50
+
51
+ def ruby_code
52
+ return unless pipe?
53
+ input =~ /\s\|(.*)$/
54
+ $1.to_s
55
+ end
56
+
57
+ def ruby!
58
+ eval(ruby_code, binding)
59
+ end
60
+
61
+ def evaluate!
62
+ be_connected!
63
+ parse!
64
+ perform_request!
65
+ case
66
+ when pipe_to_irb?
67
+ irb!
68
+ when pipe_to_ruby?
69
+ ruby!
70
+ when pipe_to_file?
71
+ pipe_to_file!
72
+ else
73
+ shell.print(response)
74
+ end
75
+ end
76
+
77
+ end
78
+ end
79
+ end
80
+
81
+ require 'elasticshell/commands/request_parser'
@@ -0,0 +1,77 @@
1
+ module Elasticshell
2
+ module Commands
3
+ class Request
4
+
5
+ class Parser
6
+
7
+ include RecognizesVerb
8
+
9
+ attr_accessor :command, :raw, :verb, :request_string, :raw_path, :raw_body, :path, :query_options, :body
10
+
11
+ def initialize(command)
12
+ self.command = command
13
+ end
14
+
15
+ def request
16
+ parse!
17
+ { :verb => verb, :path => path, :query_options => query_options, :body => body }
18
+ end
19
+
20
+ def parse!
21
+ strip_redirect!
22
+ split_verb_and_request!
23
+ split_path_and_body!
24
+ interpret_path!
25
+ construct_body!
26
+ end
27
+
28
+ def strip_redirect!
29
+ self.raw = command.shell.input.gsub(/ (?:\||>).*$/,'')
30
+ end
31
+
32
+ def strip_file_redirect!
33
+ self.raw = command.shell.input.gsub(/ >.*$/,'')
34
+ end
35
+
36
+ def split_verb_and_request!
37
+ if raw =~ Regexp.new("^(#{verb_re})\s+(.+)$", true)
38
+ self.verb, self.request_string = canonicalize_verb($1), $2
39
+ else
40
+ self.verb, self.request_string = command.shell.verb, raw
41
+ end
42
+ end
43
+
44
+ def split_path_and_body!
45
+ self.raw_path, self.raw_body = request_string.split(/ /, 2)
46
+ end
47
+
48
+ def interpret_path!
49
+ relative_path, query_string = raw_path.split('?')
50
+ self.path = File.expand_path(relative_path, command.shell.scope.path)
51
+
52
+ self.query_options = {}
53
+ URI.decode_www_form(query_string || '').each do |k, v|
54
+ self.query_options[k] = v
55
+ end
56
+ end
57
+
58
+ def construct_body!
59
+ self.body = case
60
+ when raw_body.nil?
61
+ ''
62
+ when raw_body == '-'
63
+ command.shell.error.puts("Reading request body from STDIN. Press `C-d' to terminate input...")
64
+ command.shell.input_stream.gets(nil)
65
+ when File.exist?(raw_body)
66
+ File.read(raw_body)
67
+ else
68
+ raw_body
69
+ end
70
+ end
71
+
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+