turbot 0.1.36 → 0.2.3

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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +8 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +15 -0
  5. data/.yardopts +3 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE +22 -0
  8. data/README.md +44 -25
  9. data/Rakefile +16 -0
  10. data/appveyor.yml +35 -0
  11. data/bin/turbot +2 -16
  12. data/data/schema.json +134 -0
  13. data/{templates → data/templates}/LICENSE.txt +0 -0
  14. data/{templates → data/templates}/manifest.json +0 -0
  15. data/{templates → data/templates}/python/scraper.py +0 -0
  16. data/{templates → data/templates}/ruby/scraper.rb +0 -0
  17. data/dist/deb.rake +32 -0
  18. data/dist/gem.rake +16 -0
  19. data/dist/manifest.rake +9 -0
  20. data/dist/pkg.rake +60 -0
  21. data/dist/resources/deb/control +10 -0
  22. data/dist/resources/deb/postinst +45 -0
  23. data/dist/resources/deb/turbot +25 -0
  24. data/dist/resources/deb/turbot-release-key.txt +30 -0
  25. data/dist/resources/pkg/Distribution.erb +15 -0
  26. data/dist/resources/pkg/PackageInfo.erb +6 -0
  27. data/dist/resources/pkg/postinstall +45 -0
  28. data/dist/resources/pkg/turbot +24 -0
  29. data/dist/resources/tgz/turbot +24 -0
  30. data/dist/rpm.rake +35 -0
  31. data/dist/tgz.rake +26 -0
  32. data/dist/zip.rake +40 -0
  33. data/lib/turbot.rb +18 -15
  34. data/lib/turbot/cli.rb +10 -27
  35. data/lib/turbot/command.rb +59 -212
  36. data/lib/turbot/command/auth.rb +72 -34
  37. data/lib/turbot/command/base.rb +22 -61
  38. data/lib/turbot/command/bots.rb +251 -300
  39. data/lib/turbot/command/help.rb +57 -110
  40. data/lib/turbot/command/version.rb +6 -10
  41. data/lib/turbot/handlers/base_handler.rb +21 -0
  42. data/lib/turbot/handlers/dump_handler.rb +10 -0
  43. data/lib/turbot/handlers/preview_handler.rb +30 -0
  44. data/lib/turbot/handlers/validation_handler.rb +17 -0
  45. data/lib/turbot/helpers.rb +14 -482
  46. data/lib/turbot/helpers/api_helper.rb +41 -0
  47. data/lib/turbot/helpers/netrc_helper.rb +66 -0
  48. data/lib/turbot/helpers/shell_helper.rb +36 -0
  49. data/lib/turbot/version.rb +1 -1
  50. data/spec/fixtures/bad_permissions +0 -0
  51. data/spec/fixtures/empty +0 -0
  52. data/spec/fixtures/netrc +6 -0
  53. data/spec/spec_helper.rb +17 -219
  54. data/spec/support/bot_helper.rb +102 -0
  55. data/spec/support/command_helper.rb +20 -0
  56. data/spec/support/custom_matchers.rb +5 -0
  57. data/spec/support/fixture_helper.rb +9 -0
  58. data/spec/support/netrc_helper.rb +21 -0
  59. data/spec/turbot/command/auth_spec.rb +202 -20
  60. data/spec/turbot/command/base_spec.rb +22 -58
  61. data/spec/turbot/command/bots_spec.rb +580 -89
  62. data/spec/turbot/command/help_spec.rb +32 -75
  63. data/spec/turbot/command/version_spec.rb +11 -10
  64. data/spec/turbot/command_spec.rb +55 -87
  65. data/spec/turbot/helpers_spec.rb +28 -44
  66. data/turbot.gemspec +31 -0
  67. metadata +88 -178
  68. data/data/cacert.pem +0 -3988
  69. data/lib/turbot/auth.rb +0 -315
  70. data/lib/turbot/client.rb +0 -757
  71. data/lib/turbot/client/cisaurus.rb +0 -25
  72. data/lib/turbot/client/pgbackups.rb +0 -113
  73. data/lib/turbot/client/rendezvous.rb +0 -111
  74. data/lib/turbot/client/ssl_endpoint.rb +0 -25
  75. data/lib/turbot/client/turbot_postgresql.rb +0 -148
  76. data/lib/turbot/command/ssl.rb +0 -43
  77. data/lib/turbot/deprecated.rb +0 -5
  78. data/lib/turbot/deprecated/help.rb +0 -38
  79. data/lib/turbot/distribution.rb +0 -9
  80. data/lib/turbot/errors.rb +0 -28
  81. data/lib/turbot/excon.rb +0 -11
  82. data/lib/turbot/helpers/log_displayer.rb +0 -70
  83. data/lib/turbot/helpers/pg_dump_restore.rb +0 -115
  84. data/lib/turbot/helpers/turbot_postgresql.rb +0 -213
  85. data/lib/turbot/plugin.rb +0 -165
  86. data/lib/turbot/updater.rb +0 -171
  87. data/lib/vendor/turbot/okjson.rb +0 -598
  88. data/spec/helper/legacy_help.rb +0 -16
  89. data/spec/helper/pg_dump_restore_spec.rb +0 -67
  90. data/spec/spec.opts +0 -1
  91. data/spec/support/display_message_matcher.rb +0 -49
  92. data/spec/support/dummy_api.rb +0 -120
  93. data/spec/support/openssl_mock_helper.rb +0 -8
  94. data/spec/support/organizations_mock_helper.rb +0 -11
  95. data/spec/turbot/auth_spec.rb +0 -214
  96. data/spec/turbot/client/pgbackups_spec.rb +0 -43
  97. data/spec/turbot/client/rendezvous_spec.rb +0 -62
  98. data/spec/turbot/client/ssl_endpoint_spec.rb +0 -48
  99. data/spec/turbot/client/turbot_postgresql_spec.rb +0 -71
  100. data/spec/turbot/client_spec.rb +0 -548
  101. data/spec/turbot/helpers/turbot_postgresql_spec.rb +0 -181
  102. data/spec/turbot/plugin_spec.rb +0 -172
  103. data/spec/turbot/updater_spec.rb +0 -44
data/lib/turbot/cli.rb CHANGED
@@ -1,42 +1,25 @@
1
- load('turbot/helpers.rb') # reload helpers after possible inject_loadpath
2
- load('turbot/updater.rb') # reload updater after possible inject_loadpath
3
-
4
- require "turbot"
5
- require "turbot/command"
6
- require "turbot/helpers"
7
-
8
- # workaround for rescue/reraise to define errors in command.rb failing in 1.8.6
9
- if RUBY_VERSION =~ /^1.8.6/
10
- require('turbot-api')
11
- require('rest_client')
12
- end
13
-
14
1
  class Turbot::CLI
15
-
16
2
  extend Turbot::Helpers
17
3
 
18
4
  def self.start(*args)
19
5
  begin
20
- if $stdin.isatty
21
- $stdin.sync = true
22
- end
23
- if $stdout.isatty
24
- $stdout.sync = true
25
- end
26
- command = args.shift.strip rescue "help"
27
- Turbot::Command.load
28
- trap "SIGINT" do
6
+ $stdout.sync = true
7
+ $stderr.sync = true
8
+
9
+ trap 'SIGINT' do
29
10
  # Script terminated by Control-C.
30
11
  exit 130
31
12
  end
13
+
14
+ Turbot::Command.load
15
+ command = args.shift.strip rescue 'help'
32
16
  Turbot::Command.run(command, args)
33
17
  rescue Interrupt
34
18
  `stty icanon echo`
35
- error("Command cancelled.")
36
- rescue => error
37
- styled_error(error)
19
+ error 'Command cancelled.'
20
+ rescue => e
21
+ styled_error(e)
38
22
  exit(1)
39
23
  end
40
24
  end
41
-
42
25
  end
@@ -1,23 +1,20 @@
1
- require 'turbot/helpers'
2
- require 'turbot/plugin'
3
- require 'turbot/version'
4
- require "optparse"
5
- require 'excon'
6
-
7
1
  module Turbot
8
2
  module Command
9
- class CommandFailed < RuntimeError; end
10
-
11
3
  extend Turbot::Helpers
12
4
 
13
5
  def self.load
14
- Dir[File.join(File.dirname(__FILE__), "command", "*.rb")].each do |file|
6
+ Dir[File.join(File.dirname(__FILE__), 'command', '*.rb')].each do |file|
15
7
  require file
16
8
  end
17
- Turbot::Plugin.load!
18
9
  unregister_commands_made_private_after_the_fact
19
10
  end
20
11
 
12
+ def self.unregister_commands_made_private_after_the_fact
13
+ commands.values.
14
+ select { |c| c[:klass].private_method_defined? c[:method] }.
15
+ each { |c| commands.delete(c[:command]) }
16
+ end
17
+
21
18
  def self.commands
22
19
  @@commands ||= {}
23
20
  end
@@ -38,12 +35,6 @@ module Turbot
38
35
  commands[command[:command]] = command
39
36
  end
40
37
 
41
- def self.unregister_commands_made_private_after_the_fact
42
- commands.values \
43
- .select { |c| c[:klass].private_method_defined? c[:method] } \
44
- .each { |c| commands.delete c[:command] }
45
- end
46
-
47
38
  def self.register_namespace(namespace)
48
39
  namespaces[namespace[:name]] = namespace
49
40
  end
@@ -52,109 +43,36 @@ module Turbot
52
43
  @current_command
53
44
  end
54
45
 
55
- def self.current_command=(new_current_command)
56
- @current_command = new_current_command
57
- end
58
-
59
- def self.current_args
60
- @current_args
61
- end
62
-
63
- def self.current_options
64
- @current_options ||= {}
65
- end
66
-
67
- def self.global_options
68
- @global_options ||= []
69
- end
70
-
71
- def self.invalid_arguments
72
- @invalid_arguments
73
- end
74
-
75
- def self.shift_argument
76
- # dup argument to get a non-frozen string
77
- @invalid_arguments.shift.dup rescue nil
78
- end
79
-
80
46
  def self.validate_arguments!
81
- unless invalid_arguments.empty?
82
- arguments = invalid_arguments.map {|arg| "\"#{arg}\""}
47
+ unless @invalid_arguments.empty?
48
+ arguments = @invalid_arguments.map(&:inspect)
83
49
  if arguments.length == 1
84
50
  message = "Invalid argument: #{arguments.first}"
85
- elsif arguments.length > 1
86
- message = "Invalid arguments: "
87
- message << arguments[0...-1].join(", ")
88
- message << " and "
89
- message << arguments[-1]
51
+ else
52
+ message = "Invalid arguments: #{arguments[0...-1].join(', ')} and #{arguments[-1]}"
90
53
  end
91
- $stderr.puts(format_with_bang(message))
92
- run(current_command, ["--help"])
93
- exit(1)
54
+ run('help', [current_command])
55
+ error message
94
56
  end
95
57
  end
96
58
 
97
- def self.warnings
98
- @warnings ||= []
99
- end
100
-
101
- def self.display_warnings
102
- unless warnings.empty?
103
- $stderr.puts(warnings.map {|warning| " ! #{warning}"}.join("\n"))
104
- end
105
- end
106
-
107
- def self.global_option(name, *args, &blk)
108
- # args.sort.reverse gives -l, --long order
109
- global_options << { :name => name.to_s, :args => args.sort.reverse, :proc => blk }
110
- end
111
-
112
- global_option :bot, "-b", "--bot APP" do |bot|
113
- raise OptionParser::InvalidOption.new(bot) if bot == "pp"
114
- end
115
-
116
- global_option :org, "-o", "--org ORG" do |org|
117
- raise OptionParser::InvalidOption.new(org) if org == "rg"
118
- end
119
- global_option :personal, "-p", "--personal"
120
-
121
- global_option :confirm, "--confirm APP"
122
- global_option :help, "-h", "--help"
123
- global_option :remote, "-r", "--remote REMOTE"
124
-
125
59
  def self.prepare_run(cmd, args=[])
126
- command = parse(cmd)
127
-
128
- if args.include?('-h') || args.include?('--help')
129
- args.unshift(cmd) unless cmd =~ /^-.*/
130
- cmd = 'help'
131
- command = parse(cmd)
132
- end
133
-
134
- if cmd == '--version'
135
- cmd = 'version'
136
- command = parse(cmd)
137
- end
138
-
60
+ command = commands[cmd] || commands[command_aliases[cmd]]
139
61
  @current_command = cmd
140
- @anonymized_args, @normalized_args = [], []
141
62
 
142
63
  opts = {}
143
64
  invalid_options = []
144
65
 
145
66
  parser = OptionParser.new do |parser|
146
67
  # remove OptionParsers Officious['version'] to avoid conflicts
147
- # see: https://github.com/ruby/ruby/blob/trunk/lib/optparse.rb#L814
68
+ # see: https://github.com/ruby/ruby/blob/6860034546808d4f67ba8f407f3d7aced0c54c5a/lib/optparse.rb#L989
148
69
  parser.base.long.delete('version')
149
- (global_options + (command && command[:options] || [])).each do |option|
150
- parser.on(*option[:args]) do |value|
151
- if option[:proc]
152
- option[:proc].call(value)
70
+ if command && command[:options]
71
+ command[:options].each do |option|
72
+ parser.on(*option[:args]) do |value|
73
+ opts[option[:name].gsub('-', '_').to_sym] = value
74
+ ARGV.join(' ') =~ /(#{option[:args].map {|arg| arg.split(' ', 2).first}.join('|')})/
153
75
  end
154
- opts[option[:name].gsub('-', '_').to_sym] = value
155
- ARGV.join(' ') =~ /(#{option[:args].map {|arg| arg.split(' ', 2).first}.join('|')})/
156
- @anonymized_args << "#{$1} _"
157
- @normalized_args << "#{option[:args].last.split(' ', 2).first} _"
158
76
  end
159
77
  end
160
78
  end
@@ -162,144 +80,73 @@ module Turbot
162
80
  begin
163
81
  parser.order!(args) do |nonopt|
164
82
  invalid_options << nonopt
165
- @anonymized_args << '!'
166
- @normalized_args << '!'
167
83
  end
168
- rescue OptionParser::InvalidOption => ex
169
- invalid_options << ex.args.first
170
- @anonymized_args << '!'
171
- @normalized_args << '!'
84
+ rescue OptionParser::InvalidOption => e
85
+ invalid_options << e.args.first
172
86
  retry
173
87
  end
174
88
 
175
89
  args.concat(invalid_options)
176
-
177
- @current_args = args
178
- @current_options = opts
179
90
  @invalid_arguments = invalid_options
180
91
 
181
- @anonymous_command = [ARGV.first, *@anonymized_args].join(' ')
182
- begin
183
- usage_directory = "#{home_directory}/.turbot/usage"
184
- FileUtils.mkdir_p(usage_directory)
185
- usage_file = usage_directory << "/#{Turbot::VERSION}"
186
- usage = if File.exists?(usage_file)
187
- json_decode(File.read(usage_file))
188
- else
189
- {}
190
- end
191
- usage[@anonymous_command] ||= 0
192
- usage[@anonymous_command] += 1
193
- File.write(usage_file, json_encode(usage) + "\n")
194
- rescue
195
- # usage writing is not important, allow failures
196
- end
197
-
198
92
  if command
199
93
  command_instance = command[:klass].new(args.dup, opts.dup)
200
-
201
- if !@normalized_args.include?('--bot _') && (implied_bot = command_instance.bot rescue nil)
202
- @normalized_args << '--bot _'
203
- end
204
- @normalized_command = [ARGV.first, @normalized_args.sort_by {|arg| arg.gsub('-', '')}].join(' ')
205
-
206
- [ command_instance, command[:method] ]
94
+ [command_instance, command[:method]]
207
95
  else
208
96
  error([
209
97
  "`#{cmd}` is not a turbot command.",
210
98
  suggestion(cmd, commands.keys + command_aliases.keys),
211
- "See `turbot help` for a list of available commands."
99
+ 'See `turbot help` for a list of available commands.'
212
100
  ].compact.join("\n"))
213
101
  end
214
102
  end
215
103
 
216
- def self.run(cmd, arguments=[])
217
- begin
218
- object, method = prepare_run(cmd, arguments.dup)
219
- object.send(method)
220
- rescue Interrupt, StandardError, SystemExit => error
221
- # load likely error classes, as they may not be loaded yet due to defered loads
222
- require 'rest_client'
223
- raise(error)
224
- end
225
- rescue Turbot::API::Errors::Unauthorized, RestClient::Unauthorized
226
- puts "Authentication failure"
227
- if ENV['TURBOT_API_KEY']
228
- exit 1
104
+ def self.run(command, arguments=[])
105
+ object, method = prepare_run(command, arguments.dup)
106
+ object.send(method)
107
+ rescue SocketError => e
108
+ error 'Unable to connect to Turbot API, please check internet connectivity and try again.'
109
+ rescue OptionParser::ParseError
110
+ if commands[command]
111
+ run('help', [command])
229
112
  else
230
- run "login"
231
- retry
232
- end
233
- rescue Turbot::API::Errors::NotFound => e
234
- error extract_error(e.response.body) {
235
- e.response.body =~ /^([\w\s]+ not found).?$/ ? $1 : "Resource not found"
236
- }
237
- rescue RestClient::ResourceNotFound => e
238
- error extract_error(e.http_body) {
239
- e.http_body =~ /^([\w\s]+ not found).?$/ ? $1 : "Resource not found"
240
- }
241
- rescue Turbot::API::Errors::Locked => e
242
- bot = e.response.headers[:x_confirmation_required]
243
- if confirm_command(bot, extract_error(e.response.body))
244
- arguments << '--confirm' << bot
245
- retry
246
- end
247
- rescue RestClient::Locked => e
248
- bot = e.response.headers[:x_confirmation_required]
249
- if confirm_command(bot, extract_error(e.http_body))
250
- arguments << '--confirm' << bot
251
- retry
113
+ run('help')
252
114
  end
253
- rescue RestClient::PaymentRequired => e
254
- # We've repurposed a 402 as a general error
255
- error extract_error(e.http_body)
256
- rescue Turbot::API::Errors::Timeout, RestClient::RequestTimeout
257
- error "API request timed out. Please try again, or contact support@turbot.com if this issue persists."
258
- rescue Turbot::API::Errors::ErrorWithResponse => e
259
- error extract_error(e.response.body)
260
- rescue RestClient::RequestFailed => e
261
- error extract_error(e.http_body)
262
- rescue CommandFailed => e
263
- error e.message
264
- rescue OptionParser::ParseError
265
- commands[cmd] ? run("help", [cmd]) : run("help")
266
- rescue Excon::Errors::SocketError, SocketError => e
267
- error("Unable to connect to Turbot API, please check internet connectivity and try again.")
268
- ensure
269
- display_warnings
270
115
  end
271
116
 
272
- def self.parse(cmd)
273
- commands[cmd] || commands[command_aliases[cmd]]
274
- end
275
-
276
- def self.extract_error(body, options={})
277
- default_error = block_given? ? yield : "Internal server error.\nRun `turbot status` to check for known platform issues."
278
- parse_error_xml(body) || parse_error_json(body) || parse_error_plain(body) || default_error
279
- end
280
-
281
- def self.parse_error_xml(body)
282
- xml_errors = REXML::Document.new(body).elements.to_a("//errors/error")
283
- msg = xml_errors.map { |a| a.text }.join(" / ")
284
- return msg unless msg.empty?
285
- rescue Exception
117
+ def self.extract_error(body, options = {})
118
+ if block_given?
119
+ default_error = yield
120
+ else
121
+ default_error = 'Internal server error'
122
+ end
123
+ parse_error_json(body) || default_error
286
124
  end
287
125
 
288
126
  def self.parse_error_json(body)
289
- json = json_decode(body.to_s) rescue false
290
- case json
291
- when Array
292
- json.first.join(' ') # message like [['base', 'message']]
293
- when Hash
294
- json['error'] || json['error_message'] || json['message'] # message like {'error' => 'message'}
295
- else
127
+ begin
128
+ JSON.load(body.to_s)['message']
129
+ rescue JSON::ParserError
296
130
  nil
297
131
  end
298
132
  end
299
133
 
300
- def self.parse_error_plain(body)
301
- return unless body.respond_to?(:headers) && body.headers[:content_type].to_s.include?("text/plain")
302
- body.to_s
134
+ private
135
+
136
+ def self.suggestion(actual, possibilities)
137
+ distances = Hash.new { |hash,key| hash[key] = [] }
138
+ possibilities.each do |possibility|
139
+ distances[Text::Levenshtein.distance(actual, possibility, 4)] << possibility
140
+ end
141
+ minimum_distance = distances.keys.min
142
+ if minimum_distance < 4
143
+ suggestions = distances[minimum_distance].sort
144
+ if suggestions.length == 1
145
+ "Perhaps you meant `#{suggestions.first}`."
146
+ else
147
+ "Perhaps you meant #{suggestions[0...-1].map { |suggestion| "`#{suggestion}`" }.join(', ')} or `#{suggestions.last}`."
148
+ end
149
+ end
303
150
  end
304
151
  end
305
152
  end
@@ -1,85 +1,123 @@
1
- require "turbot/command/base"
2
-
3
- # authentication (login, logout)
1
+ #Login or logout from Turbot
4
2
  #
5
3
  class Turbot::Command::Auth < Turbot::Command::Base
6
-
7
4
  # auth
8
5
  #
9
- # Authenticate, display token and current user
6
+ #Login or logout. Display your Turbot API token or email address.
10
7
  def index
11
8
  validate_arguments!
12
-
13
9
  Turbot::Command::Help.new.send(:help_for_command, current_command)
14
10
  end
15
11
 
16
12
  # auth:login
17
13
  #
18
- # log in with your turbot credentials
14
+ #Login to Turbot and save your Turbot credentials.
19
15
  #
20
16
  #Example:
21
17
  #
22
- # $ turbot auth:login
23
- # Enter your Turbot credentials:
24
- # Email: email@example.com
25
- # Password (typing will be hidden):
26
- # Authentication successful.
18
+ # $ turbot auth:login
19
+ # Enter your Turbot email and password:
20
+ # Email: email@example.com
21
+ # Password (typing will be hidden):
22
+ # Logged in. Saved Turbot API key.
27
23
  #
28
24
  def login
29
25
  validate_arguments!
30
-
31
- Turbot::Auth.login
32
- display "Authentication successful."
26
+ email_address, api_key = ask_for_email_address_and_password
27
+ if api_key.empty?
28
+ error 'Authentication failed.'
29
+ end
30
+ save_netrc_entry(email_address, api_key)
31
+ puts 'Authentication successful.'
33
32
  end
34
-
35
- alias_command "login", "auth:login"
33
+ alias_command 'login', 'auth:login'
36
34
 
37
35
  # auth:logout
38
36
  #
39
- # clear local authentication credentials
37
+ #Delete your Turbot credentials.
40
38
  #
41
39
  #Example:
42
40
  #
43
- # $ turbot auth:logout
44
- # Local credentials cleared.
41
+ # $ turbot auth:logout
42
+ # Deleted Turbot credentials.
45
43
  #
46
44
  def logout
47
45
  validate_arguments!
48
-
49
- Turbot::Auth.logout
50
- display "Local credentials cleared."
46
+ delete_netrc_entry
47
+ puts 'Deleted Turbot credentials.'
51
48
  end
52
-
53
- alias_command "logout", "auth:logout"
49
+ alias_command 'logout', 'auth:logout'
54
50
 
55
51
  # auth:token
56
52
  #
57
- # display your api token
53
+ #Display your Turbot API token.
58
54
  #
59
55
  #Example:
60
56
  #
61
- # $ turbot auth:token
62
- # ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCD
57
+ # $ turbot auth:token
58
+ # 93a5c45595ed37dc9d040116
63
59
  #
64
60
  def token
65
61
  validate_arguments!
66
-
67
- display Turbot::Auth.api_key
62
+ result = email_address_and_api_key[1]
63
+ if result
64
+ puts result
65
+ else
66
+ error 'not logged in'
67
+ end
68
68
  end
69
69
 
70
70
  # auth:whoami
71
71
  #
72
- # display your turbot email address
72
+ #Display your Turbot email address.
73
73
  #
74
74
  #Example:
75
75
  #
76
- # $ turbot auth:whoami
77
- # email@example.com
76
+ # $ turbot auth:whoami
77
+ # email@example.com
78
78
  #
79
79
  def whoami
80
80
  validate_arguments!
81
+ result = email_address_and_api_key[0]
82
+ if result
83
+ puts result
84
+ else
85
+ error 'not logged in'
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ ### Shell-related
81
92
 
82
- display Turbot::Auth.user
93
+ # Prompts the user for an email address and password, and returns the email
94
+ # address and the user's API key.
95
+ #
96
+ # @return [Array<String>] the user's email address and API key
97
+ def ask_for_email_address_and_password
98
+ puts 'Enter your Turbot email and password.'
99
+
100
+ print 'Email: '
101
+ email_address = ask
102
+
103
+ print 'Password (typing will be hidden): '
104
+ password = running_on_windows? ? ask_for_password_on_windows : ask_for_password
105
+
106
+ puts
107
+
108
+ [email_address, get_api_key(email_address, password)]
83
109
  end
84
110
 
111
+ def running_on_windows?
112
+ RUBY_PLATFORM =~ /mswin32|mingw32/
113
+ end
114
+
115
+ ### API-related
116
+
117
+ # Gets the user's API key.
118
+ #
119
+ # @return [String] the API key, or the empty string if authentication fails
120
+ def get_api_key(email_address, password)
121
+ api.get_api_key_for_credentials(email_address, password)['api_key']
122
+ end
85
123
  end