elasticshell 0.0.2 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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,23 @@
1
+ require 'elasticshell/utils/recognizes_verb'
2
+
3
+ module Elasticshell
4
+ module Commands
5
+ class SetVerb < Command
6
+
7
+ include RecognizesVerb
8
+ extend RecognizesVerb
9
+
10
+ def self.matches? input
11
+ is_http_verb?(input)
12
+ end
13
+
14
+ def evaluate!
15
+ v = canonicalize_verb(input)
16
+ shell.verb = v
17
+ shell.scope.verb = v
18
+ end
19
+
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,17 @@
1
+ module Elasticshell
2
+ module Commands
3
+ class Unknown < Command
4
+
5
+ def self.matches? input
6
+ true
7
+ end
8
+
9
+ def evaluate!
10
+ raise ArgumentError.new("Invalid command: '#{shell.input}' for scope '#{shell.scope.path}'. Try typing 'help' for a list of available commands.")
11
+ end
12
+
13
+ end
14
+ end
15
+ end
16
+
17
+
@@ -1,4 +1,3 @@
1
- require 'elasticshell/error'
2
1
  require 'uri'
3
2
 
4
3
  module Elasticshell
@@ -52,12 +51,19 @@ module Elasticshell
52
51
 
53
52
  class Scope
54
53
 
55
- attr_accessor :path, :client, :last_refresh_at, :contents
54
+ include Elasticshell::HasVerb
56
55
 
56
+ attr_accessor :path, :client, :last_refresh_at, :scopes
57
+
58
+ def self.requests
59
+ @requests ||= {}
60
+ end
61
+
57
62
  def initialize path, options
58
- self.path = path
59
- self.client = options[:client]
60
- self.contents = initial_contents
63
+ self.verb = options.delete(:verb)
64
+ self.path = path
65
+ self.client = options[:client]
66
+ self.scopes = initial_scopes
61
67
  end
62
68
 
63
69
  def to_s
@@ -65,89 +71,114 @@ module Elasticshell
65
71
  end
66
72
 
67
73
  def completion_proc
68
- Proc.new do |prefix|
69
- refresh
74
+ Proc.new do |shell, prefix|
75
+ refresh if client.connected?
70
76
  case
71
77
  when Readline.line_buffer =~ /^\s*cd\s+\S*$/
72
- contents.find_all do |content|
73
- content[0...prefix.length] == prefix
74
- end
78
+ # User has typed 'cd' so we should be completing scopes only
79
+ completing_scope_path, prefix_within_completing_scope = completing_scope_path_and_prefix(prefix)
80
+ completing_scope = shell.scope_from_path(File.expand_path(completing_scope_path, shell.scope.path))
81
+ completing_scope.refresh if client.connected?
82
+ completing_scope.scopes_matching(prefix_within_completing_scope)
75
83
  when Readline.line_buffer =~ /^\s*\S*$/
76
- command_names.find_all do |command_name|
77
- command_name[0...prefix.length] == prefix
78
- end
84
+ # User has started but not completed the first word in the
85
+ # line so it must be a request available in the current
86
+ # scope.
87
+ requests_matching(prefix)
88
+ when Readline.line_buffer =~ />.*$/
89
+ # User has started to complete the name of a filesystem path
90
+ # to redirect output to.
91
+ Dir[prefix + '*']
79
92
  else
93
+ # The user has finished the first word on the line so we try
94
+ # to match to a filesystem path.
80
95
  Dir[prefix + '*']
81
96
  end
82
97
  end
83
98
  end
84
99
 
85
- def command_names
86
- refresh
87
- commands.keys.sort
100
+ def requests
101
+ self.class.requests[verb] || {}
88
102
  end
89
103
 
90
- def commands
91
- {}
104
+ def request_names
105
+ requests.keys
92
106
  end
93
107
 
108
+ def requests_matching prefix
109
+ if prefix.empty?
110
+ request_names.sort
111
+ else
112
+ request_names.find_all { |name| name[0...prefix.length] == prefix }.sort
113
+ end
114
+ end
115
+
94
116
  def refresh
95
117
  refresh! unless refreshed?
96
118
  end
97
119
 
98
120
  def refresh!
99
121
  reset!
100
- fetch_contents
122
+ fetch_scopes
101
123
  self.last_refresh_at = Time.now
102
124
  true
103
125
  end
104
126
 
105
- def fetch_contents
127
+ def fetch_scopes
106
128
  end
107
129
 
108
- def initial_contents
130
+ def initial_scopes
109
131
  []
110
132
  end
111
133
 
134
+ def completing_scope_path_and_prefix prefix
135
+ if prefix =~ %r!^/!
136
+ if prefix =~ %r!/$!
137
+ index = -1
138
+ prefix_within_completing_scope = ''
139
+ else
140
+ index = -2
141
+ end
142
+ completing_scope_path = prefix.split('/')[0..index].join('/')
143
+ completing_scope_path = '/' if completing_scope_path.empty?
144
+ prefix_within_completing_scope ||= (prefix.split('/').last || '')
145
+ else
146
+ if prefix =~ %r!/$!
147
+ index = -1
148
+ prefix_within_completing_scope = ''
149
+ else
150
+ index = -2
151
+ end
152
+ completing_scope_path = File.join(self.path, prefix.split('/')[0..index].join('/'))
153
+ completing_scope_path = self.path if completing_scope_path.empty?
154
+ prefix_within_completing_scope ||= (prefix.split('/').last || '')
155
+ end
156
+ [completing_scope_path, prefix_within_completing_scope]
157
+ end
158
+
159
+ def scopes_matching prefix
160
+ scopes.find_all do |scope|
161
+ prefix.empty? ? true : scope[0...prefix.length] == prefix
162
+ end.map do |scope|
163
+ scope =~ %r!/$! ? scope : scope + '/'
164
+ end.sort.map do |scope|
165
+ File.join(path, scope)
166
+ end
167
+ end
168
+
112
169
  def reset!
113
- self.contents = initial_contents
170
+ self.scopes = initial_scopes
114
171
  true
115
172
  end
116
173
 
117
174
  def refreshed?
118
- self.last_refresh_at
175
+ ! self.last_refresh_at.nil?
119
176
  end
120
177
 
121
178
  def exists?
122
179
  false
123
180
  end
124
-
125
- def command? command
126
- command_names.any? do |command_name|
127
- command[0...command_name.length] == command_name
128
- end
129
- end
130
-
131
- def execute command, shell
132
- if command_names.include?(command)
133
- raise NotImplementedError.new("Have not yet implemented '#{command}' for scope '#{path}'.")
134
- else
135
- raise ArgumentError.new("No such command '#{command}' in scope '#{path}'.")
136
- end
137
- end
138
-
139
- def help
140
- [].tap do |msg|
141
- msg << "Commands specific to the scope '#{path}':"
142
- msg << ''
143
- commands.each_pair do |command_name, description|
144
- msg << ' ' + command_name
145
- msg << (' ' + description)
146
- msg << ''
147
- end
148
- end.join("\n")
149
- end
150
-
181
+
151
182
  end
152
183
 
153
184
  end
@@ -1,5 +1,3 @@
1
- require 'elasticshell/scopes'
2
-
3
1
  module Elasticshell
4
2
 
5
3
  module Scopes
@@ -10,26 +8,20 @@ module Elasticshell
10
8
  super("/_cluster", options)
11
9
  end
12
10
 
13
- def commands
14
- {
15
- 'health' => "Retreive the health of the cluster.",
16
- 'state' => "Retreive the state of the cluster.",
17
- 'settings'=> "Retreive the settings for the cluster.",
11
+ def self.requests
12
+ @requests = {
13
+ "GET" =>
14
+ {
15
+ 'health' => "Retreive the health of the cluster.",
16
+ 'state' => "Retreive the state of the cluster.",
17
+ 'settings'=> "Retreive the settings for the cluster.",
18
+ }
18
19
  }
19
20
  end
20
21
 
21
22
  def exists?
22
23
  true
23
24
  end
24
-
25
- def execute command, shell
26
- case
27
- when command?(command)
28
- shell.request(:get, :index => '_cluster')
29
- else
30
- super(command, shell)
31
- end
32
- end
33
25
 
34
26
  end
35
27
  end
@@ -1,27 +1,41 @@
1
- require 'elasticshell/scopes'
2
-
3
1
  module Elasticshell
4
2
 
5
3
  module Scopes
6
4
 
7
5
  class Global < Scope
8
6
 
7
+ attr_reader :indices
8
+
9
9
  def initialize options={}
10
+ @indices = []
10
11
  super("/", options)
11
12
  end
12
13
 
13
- def commands
14
- {
15
- '_status' => "Retreive the status of all indices in the cluster."
14
+ def self.requests
15
+ @requests ||= {
16
+ "GET" => {
17
+ '_status' => "Retreive the status of all indices in the cluster."
18
+ }
16
19
  }
17
20
  end
18
21
 
19
- def initial_contents
22
+ def initial_scopes
20
23
  ['_cluster', '_nodes']
21
24
  end
22
25
 
23
- def fetch_contents
24
- self.contents += client.safely(:get, {:index => '_status'}, :return => {"indices" => {}})["indices"].keys
26
+ def status
27
+ @status ||= client.safely(:get, {:index => '_status'}, :return => {"indices" => {}}, :log => false)
28
+ end
29
+
30
+ def reset!
31
+ @indices = []
32
+ @status = nil
33
+ super()
34
+ end
35
+
36
+ def fetch_scopes
37
+ @indices = status["indices"].keys
38
+ self.scopes += @indices
25
39
  end
26
40
 
27
41
  def index name, options={}
@@ -31,21 +45,6 @@ module Elasticshell
31
45
  def exists?
32
46
  true
33
47
  end
34
-
35
- def execute command, shell
36
- case
37
- when command =~ /^_cluster/
38
- shell.scope = Scopes.cluster(:client => client)
39
- when command =~ /^_nodes/
40
- shell.scope = Scopes.nodes(:client => client)
41
- when command?(command)
42
- shell.request(:get)
43
- when index_names.include?(command)
44
- shell.scope = index(command)
45
- else
46
- super(command, shell)
47
- end
48
- end
49
48
 
50
49
  end
51
50
  end
@@ -1,30 +1,27 @@
1
- require 'elasticshell/scopes'
2
-
3
1
  module Elasticshell
4
2
 
5
3
  module Scopes
6
4
 
7
5
  class Index < Scope
8
6
 
9
- VALID_INDEX_NAME_RE = %r![^/]!
7
+ include HasName
8
+
9
+ attr_reader :mappings
10
10
 
11
11
  def initialize name, options={}
12
12
  self.name = name
13
+ @mappings = []
13
14
  super("/#{self.name}", options)
14
15
  end
15
16
 
16
- attr_reader :name
17
- def name= name
18
- raise ArgumentError.new("Invalid index name: '#{name}'") unless name =~ VALID_INDEX_NAME_RE
19
- @name = name
20
- end
21
-
22
- def commands
23
- {
24
- "_aliases" => "Find the aliases for this index.",
25
- "_status" => "Retrieve the status of this index.",
26
- "_stats" => "Retrieve usage stats for this index.",
27
- "_search" => "Search records within this index.",
17
+ def self.requests
18
+ @requests ||= {
19
+ "GET" => {
20
+ "_aliases" => "Find the aliases for this index.",
21
+ "_status" => "Retrieve the status of this index.",
22
+ "_stats" => "Retrieve usage stats for this index.",
23
+ "_search" => "Search records within this index.",
24
+ }
28
25
  }
29
26
  end
30
27
 
@@ -32,28 +29,37 @@ module Elasticshell
32
29
  @global ||= Scopes.global(:client => client)
33
30
  end
34
31
 
32
+ def status
33
+ @status ||= client.safely(:get, {:index => name, :op => '_status'}, :return => {"indices" => {name => {}}}, :log => false)
34
+ end
35
+
36
+ def reset!
37
+ @status = nil
38
+ @mappings = []
39
+ super()
40
+ end
41
+
35
42
  def exists?
43
+ return false unless client.connected?
36
44
  global.refresh
37
- global.contents.include?(name)
45
+ global.scopes.include?(name)
38
46
  end
39
47
 
40
- def fetch_contents
41
- @contents = (client.safely(:get, {:index => name, :op => '_mapping'}, :return => { name => {}})[name] || {}).keys
48
+ def multi?
49
+ name.include?(',')
42
50
  end
43
51
 
44
- def mapping mapping_name, options={}
45
- Scopes.mapping(self.name, mapping_name, options.merge(:client => client))
52
+ def single?
53
+ ! multi?
54
+ end
55
+
56
+ def fetch_scopes
57
+ @mappings = (client.safely(:get, {:index => name, :op => '_mapping'}, :return => { name => {}}, :log => false)[name] || {}).keys
58
+ self.scopes += @mappings
46
59
  end
47
60
 
48
- def execute command, shell
49
- case
50
- when command?(command)
51
- shell.request(:get, :index => name)
52
- when mapping_names.include?(command)
53
- shell.scope = mapping(command)
54
- else
55
- super(command, shell)
56
- end
61
+ def mapping mapping_name, options={}
62
+ Scopes.mapping(self.name, mapping_name, options.merge(:client => client))
57
63
  end
58
64
 
59
65
  end
@@ -1,12 +1,10 @@
1
- require 'elasticshell/scopes'
2
-
3
1
  module Elasticshell
4
2
 
5
3
  module Scopes
6
4
 
7
5
  class Mapping < Scope
8
6
 
9
- VALID_MAPPING_NAME_RE = %r![^/]!
7
+ include HasName
10
8
 
11
9
  attr_accessor :index
12
10
 
@@ -16,38 +14,20 @@ module Elasticshell
16
14
  super("/#{index.name}/#{self.name}", options)
17
15
  end
18
16
 
19
- attr_reader :name
20
- def name= name
21
- raise ArgumentError.new("Invalid mapping name: '#{name}'") unless name =~ VALID_MAPPING_NAME_RE
22
- @name = name
23
- end
24
-
25
- def commands
26
- {
27
- "_search" => "Search records within this mapping.",
28
- "_mapping" => "Retrieve the mapping settings for this mapping.",
17
+ def self.requests
18
+ @requests ||= {
19
+ "GET" => {
20
+ "_search" => "Search records within this mapping.",
21
+ "_mapping" => "Retrieve the mapping settings for this mapping.",
22
+ }
29
23
  }
30
24
  end
31
25
 
32
26
  def exists?
33
27
  index.refresh
34
- index.contents.include?(name)
28
+ index.scopes.include?(name)
35
29
  end
36
30
 
37
- def command? command
38
- true
39
- end
40
-
41
- def execute command, shell
42
- case
43
- when command?(command)
44
- shell.request(:get, :index => index.name, :type => name)
45
- else
46
- record = shell.request(:get, :index => index.name, :type => name)
47
- shell.print(record) if record
48
- end
49
- end
50
-
51
31
  end
52
32
  end
53
33
  end