bugly 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.
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
3
+
4
+ libs = " -r irb/completion"
5
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/bugly'}"
6
+ puts "Loading bugly gem"
7
+ exec "#{irb} #{libs} --simple-prompt"
@@ -0,0 +1,680 @@
1
+ # Bugly Ruby bindings
2
+ # API spec at http://bug.ly/docs/api
3
+
4
+ require 'cgi'
5
+ require 'set'
6
+
7
+ require 'rubygems'
8
+ require 'openssl'
9
+
10
+ gem 'rest-client', '~> 1.4'
11
+ require 'rest_client'
12
+
13
+ begin
14
+ require 'json'
15
+ rescue LoadError
16
+ raise if defined?(JSON)
17
+ require File.join(File.dirname(__FILE__), '../vendor/bugly-json/lib/json/pure')
18
+
19
+ # moderately ugly hack to deal with the clobbering that
20
+ # ActiveSupport's JSON subjects us to
21
+ class JSON::Pure::Generator::State
22
+ attr_reader :encoder, :only, :except
23
+ end
24
+ end
25
+
26
+ require File.join(File.dirname(__FILE__), 'bugly/version')
27
+
28
+ module Bugly
29
+ @@ssl_bundle_path = File.join(File.dirname(__FILE__), 'data/ca-certificates.crt')
30
+ @@api_key = nil
31
+ @@api_base = nil
32
+ @@verify_ssl_certs = true
33
+
34
+ module Util
35
+ def self.objects_to_ids(h)
36
+ case h
37
+ when APIResource
38
+ h.id
39
+ when Hash
40
+ res = {}
41
+ h.each { |k, v| res[k] = objects_to_ids(v) unless v.nil? }
42
+ res
43
+ when Array
44
+ h.map { |v| objects_to_ids(v) }
45
+ else
46
+ h
47
+ end
48
+ end
49
+
50
+ def self.convert_to_bugly_object(resp, api_key)
51
+ types = {
52
+ 'issue' => Issue,
53
+ 'project' => Project,
54
+ 'milestone' => Milestone,
55
+ 'category' => Category,
56
+ 'view' => View,
57
+ 'user' => User,
58
+ 'changeset' => Changeset,
59
+ 'comment' => Comment,
60
+ 'page' => Page
61
+ }
62
+ case resp
63
+ when Array
64
+ resp.map { |i| convert_to_bugly_object(i, api_key) }
65
+ when Hash
66
+ # Try converting to a known object class. If none available, fall back to generic APIResource
67
+ if klass_name = resp[:object]
68
+ klass = types[klass_name]
69
+ end
70
+ klass ||= BuglyObject
71
+ klass.construct_from(resp, api_key)
72
+ else
73
+ resp
74
+ end
75
+ end
76
+
77
+ def self.file_readable(file)
78
+ begin
79
+ File.open(file) { |f| }
80
+ rescue
81
+ false
82
+ else
83
+ true
84
+ end
85
+ end
86
+
87
+ def self.symbolize_names(object)
88
+ case object
89
+ when Hash
90
+ new = {}
91
+ object.each do |key, value|
92
+ key = (key.to_sym rescue key) || key
93
+ new[key] = symbolize_names(value)
94
+ end
95
+ new
96
+ when Array
97
+ object.map { |value| symbolize_names(value) }
98
+ else
99
+ object
100
+ end
101
+ end
102
+ end
103
+
104
+ module APIOperations
105
+ module Create
106
+ module ClassMethods
107
+ def create(params={}, api_key=nil)
108
+ response, api_key = Bugly.request(:post, self.url, api_key, params)
109
+ Util.convert_to_bugly_object(response, api_key)
110
+ end
111
+ end
112
+ def self.included(base)
113
+ base.extend(ClassMethods)
114
+ end
115
+ end
116
+
117
+ module Update
118
+ def save
119
+ if @unsaved_values.length > 0
120
+ values = {}
121
+ @unsaved_values.each { |k| values[k] = @values[k] }
122
+ response, api_key = Bugly.request(:post, url, @api_key, values)
123
+ refresh_from(response, api_key)
124
+ end
125
+ self
126
+ end
127
+ end
128
+
129
+ module Delete
130
+ def delete
131
+ response, api_key = Bugly.request(:delete, url, @api_key)
132
+ refresh_from(response, api_key)
133
+ self
134
+ end
135
+ end
136
+
137
+ module List
138
+ module ClassMethods
139
+ def all(filters={}, api_key=nil)
140
+ response, api_key = Bugly.request(:get, url, api_key, filters)
141
+ Util.convert_to_bugly_object(response, api_key)
142
+ end
143
+ end
144
+
145
+ def self.included(base)
146
+ base.extend(ClassMethods)
147
+ end
148
+ end
149
+
150
+ module Issues
151
+ def issues(filters={}, api_key=nil)
152
+ response, api_key = Bugly.request(:get, "#{url}/issues", api_key, filters)
153
+ Util.convert_to_bugly_object(response, api_key)
154
+ end
155
+ end
156
+
157
+ end
158
+
159
+ class BuglyObject
160
+ include Enumerable
161
+
162
+ attr_accessor :api_key, :api_base
163
+ @@permanent_attributes = Set.new([:api_key, :api_base])
164
+
165
+ # The default :id method is deprecated and isn't useful to us
166
+ if method_defined?(:id)
167
+ undef :id
168
+ end
169
+
170
+ def initialize(id=nil, api_key=nil)
171
+ @api_key = api_key
172
+ @values = {}
173
+ # This really belongs in APIResource, but not putting it there allows us
174
+ # to have a unified inspect method
175
+ @unsaved_values = Set.new
176
+ @transient_values = Set.new
177
+ self.id = id if id
178
+ end
179
+
180
+ def self.construct_from(values, api_key=nil)
181
+ obj = self.new(values[:id], api_key)
182
+ obj.refresh_from(values, api_key)
183
+ obj
184
+ end
185
+
186
+ def to_s(*args); JSON.pretty_generate(@values); end
187
+
188
+ def inspect()
189
+ id_string = (self.respond_to?(:id) && !self.id.nil?) ? " id=#{self.id}" : ""
190
+ "#<#{self.class}:0x#{self.object_id.to_s(16)}#{id_string}> JSON: " + JSON.pretty_generate(@values)
191
+ end
192
+
193
+ def refresh_from(values, api_key, partial=false)
194
+ @api_key = api_key
195
+
196
+ removed = partial ? Set.new : Set.new(@values.keys - values.keys)
197
+ added = Set.new(values.keys - @values.keys)
198
+ # Wipe old state before setting new. Mark those values
199
+ # which don't persist as transient
200
+
201
+ instance_eval do
202
+ remove_accessors(removed)
203
+ add_accessors(added)
204
+ end
205
+ removed.each do |k|
206
+ @values.delete(k)
207
+ @transient_values.add(k)
208
+ @unsaved_values.delete(k)
209
+ end
210
+ values.each do |k, v|
211
+ @values[k] = Util.convert_to_bugly_object(v, api_key)
212
+ @transient_values.delete(k)
213
+ @unsaved_values.delete(k)
214
+ end
215
+ end
216
+
217
+ def [](k)
218
+ k = k.to_sym if k.kind_of?(String)
219
+ @values[k]
220
+ end
221
+ def []=(k, v)
222
+ send(:"#{k}=", v)
223
+ end
224
+ def keys; @values.keys; end
225
+ def values; @values.values; end
226
+ def to_json(*a); @values.to_json(*a); end
227
+ def as_json(opts=nil); @values.as_json(opts); end
228
+ def to_hash; @values; end
229
+ def each(&blk); @values.each(&blk); end
230
+
231
+ protected
232
+
233
+ def metaclass
234
+ class << self; self; end
235
+ end
236
+
237
+ def remove_accessors(keys)
238
+ metaclass.instance_eval do
239
+ keys.each do |k|
240
+ next if @@permanent_attributes.include?(k)
241
+ k_eq = :"#{k}="
242
+ remove_method(k) if method_defined?(k)
243
+ remove_method(k_eq) if method_defined?(k_eq)
244
+ end
245
+ end
246
+ end
247
+
248
+ def add_accessors(keys)
249
+ metaclass.instance_eval do
250
+ keys.each do |k|
251
+ next if @@permanent_attributes.include?(k)
252
+ k_eq = :"#{k}="
253
+ define_method(k) { @values[k] }
254
+ define_method(k_eq) do |v|
255
+ @values[k] = v
256
+ @unsaved_values.add(k)
257
+ end
258
+ end
259
+ end
260
+ end
261
+
262
+ def method_missing(name, *args)
263
+ # TODO: only allow setting in updateable classes.
264
+ if name.to_s.end_with?('=')
265
+ attr = name.to_s[0...-1].to_sym
266
+ @values[attr] = args[0]
267
+ @unsaved_values.add(attr)
268
+ add_accessors([attr])
269
+ return
270
+ else
271
+ return @values[name] if @values.has_key?(name)
272
+ end
273
+
274
+ begin
275
+ super
276
+ rescue NoMethodError => e
277
+ if @transient_values.include?(name)
278
+ raise NoMethodError.new(e.message + ". HINT: The '#{name}' attribute was set in the past, however. It was then wiped when refreshing the object with the result returned by Bugly's API, probably as a result of a save(). The attributes currently available on this object are: #{@values.keys.join(', ')}")
279
+ else
280
+ raise
281
+ end
282
+ end
283
+ end
284
+ end
285
+
286
+ class APIResource < BuglyObject
287
+ def self.url
288
+ if self == APIResource
289
+ raise NotImplementedError.new("APIResource is an abstract class. You should perform actions on its subclasses (Issue, Project, etc.)")
290
+ end
291
+ shortname = self.name.split('::')[-1]
292
+ # HACK: Use a proper pluralize method instead of this terrible hack
293
+ shortname = "Categorie" if shortname == "Category"
294
+ shortname = "Prioritie" if shortname == "Priority"
295
+ shortname = "Statuse" if shortname == "Status"
296
+ "/#{CGI.escape(shortname.downcase)}s"
297
+ end
298
+
299
+ def url
300
+ id = self['id'].to_s
301
+ raise InvalidRequestError.new("Could not determine which URL to request: #{self.class} instance has invalid ID: #{id.inspect}", 'id') if not id or id == ""
302
+ "#{self.class.url}/#{CGI.escape(id)}"
303
+ end
304
+
305
+ # Some resources have an 'url' method, so we sometimes need to use a different method name
306
+ alias :api_url :url
307
+
308
+ def refresh
309
+ response, api_key = Bugly.request(:get, url, @api_key)
310
+ refresh_from(response, api_key)
311
+ self
312
+ end
313
+
314
+ def self.retrieve(id, api_key=nil)
315
+ instance = self.new(id, api_key)
316
+ instance.refresh
317
+ instance
318
+ end
319
+
320
+ protected
321
+ end
322
+
323
+ class Issue < APIResource
324
+ include Bugly::APIOperations::Create
325
+ include Bugly::APIOperations::Delete
326
+ include Bugly::APIOperations::Update
327
+ include Bugly::APIOperations::List
328
+
329
+ # def labels
330
+ # Label.all({ :issue_id => id }, @api_key)
331
+ # end
332
+
333
+ def labels
334
+ response, api_key = Bugly.request(:get, "#{url}/labels", @api_key)
335
+ Util.convert_to_bugly_object(response, api_key)
336
+ end
337
+
338
+ def comments
339
+ response, api_key = Bugly.request(:get, "#{url}/comments", @api_key)
340
+ Util.convert_to_bugly_object(response, api_key)
341
+ end
342
+
343
+
344
+ def commits
345
+ Changeset.all({ :issue_id => id }, @api_key)
346
+ end
347
+
348
+ def watchers
349
+ response, api_key = Bugly.request(:get, "#{url}/watchers", @api_key)
350
+ Util.convert_to_bugly_object(response, api_key)
351
+ end
352
+
353
+ def prerequisites
354
+ response, api_key = Bugly.request(:get, "#{url}/prerequisites", @api_key)
355
+ Util.convert_to_bugly_object(response, api_key)
356
+ end
357
+
358
+ def dependents
359
+ response, api_key = Bugly.request(:get, "#{url}/dependents", @api_key)
360
+ Util.convert_to_bugly_object(response, api_key)
361
+ end
362
+
363
+ def duplicates
364
+ response, api_key = Bugly.request(:get, "#{url}/duplicates", @api_key)
365
+ Util.convert_to_bugly_object(response, api_key)
366
+ end
367
+
368
+ def related
369
+ response, api_key = Bugly.request(:get, "#{url}/related", @api_key)
370
+ Util.convert_to_bugly_object(response, api_key)
371
+ end
372
+
373
+ # def add_label(params)
374
+ # Label.create(params.merge(:issue_id => id), @api_key)
375
+ # end
376
+
377
+ # def cancel_something(params={})
378
+ # response, api_key = Bugly.request(:delete, something_url, @api_key, params)
379
+ # refresh_from({ :something => response }, api_key, true)
380
+ # something
381
+ # end
382
+
383
+ end
384
+
385
+ class Project < APIResource
386
+ include Bugly::APIOperations::Create
387
+ include Bugly::APIOperations::Delete
388
+ include Bugly::APIOperations::Update
389
+ include Bugly::APIOperations::List
390
+ include Bugly::APIOperations::Issues
391
+
392
+ def milestones
393
+ response, api_key = Bugly.request(:get, "#{url}/milestones", @api_key)
394
+ Util.convert_to_bugly_object(response, api_key)
395
+ end
396
+
397
+ def categories
398
+ response, api_key = Bugly.request(:get, "#{url}/categories", @api_key)
399
+ Util.convert_to_bugly_object(response, api_key)
400
+ end
401
+
402
+ def commits
403
+ response, api_key = Bugly.request(:get, "#{url}/changesets", @api_key)
404
+ Util.convert_to_bugly_object(response, api_key)
405
+ end
406
+
407
+ def labels
408
+ response, api_key = Bugly.request(:get, "#{url}/labels", @api_key)
409
+ Util.convert_to_bugly_object(response, api_key)
410
+ end
411
+
412
+ end
413
+
414
+ class Milestone < APIResource
415
+ include Bugly::APIOperations::Create
416
+ include Bugly::APIOperations::Delete
417
+ include Bugly::APIOperations::Update
418
+ include Bugly::APIOperations::List
419
+ include Bugly::APIOperations::Issues
420
+ end
421
+
422
+ class Category < APIResource
423
+ include Bugly::APIOperations::Create
424
+ include Bugly::APIOperations::Delete
425
+ include Bugly::APIOperations::Update
426
+ include Bugly::APIOperations::List
427
+ include Bugly::APIOperations::Issues
428
+ end
429
+
430
+ class View < APIResource
431
+ include Bugly::APIOperations::Delete
432
+ include Bugly::APIOperations::List
433
+ include Bugly::APIOperations::Issues
434
+ end
435
+
436
+ class Changeset < APIResource
437
+ include Bugly::APIOperations::List
438
+ include Bugly::APIOperations::Issues
439
+ end
440
+
441
+ class Watcher < APIResource
442
+ include Bugly::APIOperations::List
443
+ include Bugly::APIOperations::Issues
444
+ end
445
+
446
+ class Label < APIResource
447
+ include Bugly::APIOperations::Create
448
+ include Bugly::APIOperations::Delete
449
+ include Bugly::APIOperations::Update
450
+ include Bugly::APIOperations::List
451
+ include Bugly::APIOperations::Issues
452
+ end
453
+
454
+ class User < APIResource
455
+ include Bugly::APIOperations::List
456
+ include Bugly::APIOperations::Update
457
+ include Bugly::APIOperations::Create
458
+ include Bugly::APIOperations::Delete
459
+ include Bugly::APIOperations::Issues
460
+
461
+ def project_access
462
+ response, api_key = Bugly.request(:get, "#{api_url}/project_access", @api_key)
463
+ Util.convert_to_bugly_object(response, api_key)
464
+ end
465
+
466
+ end
467
+
468
+ class Comment < APIResource
469
+ include Bugly::APIOperations::List
470
+ end
471
+
472
+ class Status < APIResource
473
+ include Bugly::APIOperations::Create
474
+ include Bugly::APIOperations::Delete
475
+ include Bugly::APIOperations::Update
476
+ include Bugly::APIOperations::List
477
+ include Bugly::APIOperations::Issues
478
+ end
479
+
480
+ class Page < APIResource
481
+ include Bugly::APIOperations::Create
482
+ include Bugly::APIOperations::Delete
483
+ include Bugly::APIOperations::Update
484
+ include Bugly::APIOperations::List
485
+ end
486
+
487
+ class Priority < APIResource
488
+ include Bugly::APIOperations::List
489
+ end
490
+
491
+ class BuglyError < StandardError
492
+ attr_reader :message
493
+ attr_reader :http_status
494
+ attr_reader :http_body
495
+ attr_reader :json_body
496
+
497
+ def initialize(message=nil, http_status=nil, http_body=nil, json_body=nil)
498
+ @message = message
499
+ @http_status = http_status
500
+ @http_body = http_body
501
+ @json_body = json_body
502
+ end
503
+
504
+ def to_s
505
+ status_string = @http_status.nil? ? "" : "(Status #{@http_status}) "
506
+ "#{status_string}#{@message}"
507
+ end
508
+ end
509
+
510
+ class APIError < BuglyError; end
511
+ class APIConnectionError < BuglyError; end
512
+ class InvalidRequestError < BuglyError
513
+ attr_accessor :param
514
+
515
+ def initialize(message, param, http_status=nil, http_body=nil, json_body=nil)
516
+ super(message, http_status, http_body, json_body)
517
+ @param = param
518
+ end
519
+ end
520
+ class AuthenticationError < BuglyError; end
521
+
522
+ def self.api_url(url=''); @@api_base + url; end
523
+ # def self.api_url(url=''); @@api_base; end
524
+ def self.api_key=(api_key); @@api_key = api_key; end
525
+ def self.api_key; @@api_key; end
526
+ def self.api_base=(api_base); @@api_base = api_base; end
527
+ def self.api_base; @@api_base; end
528
+ def self.verify_ssl_certs=(verify); @@verify_ssl_certs = verify; end
529
+ def self.verify_ssl_certs; @@verify_ssl_certs; end
530
+
531
+ def self.request(method, url, api_key, params=nil, headers={})
532
+ api_key ||= @@api_key
533
+ raise AuthenticationError.new('No API key provided. (HINT: set your API key using "Bugly.api_key = <API-KEY>". You can generate API keys from the Bugly web interface. See http://bug.ly/docs/api for details, or email support@bug.ly if you have any questions.)') unless api_key
534
+ raise APIConnectionError.new('No API base URL set. (HINT: set your API base URL using "Bugly.api_base = <API-URL>". Use the full URL to your account, as well as a version string, for example "https://myaccount.bug.ly/v1". See http://bug.ly/docs/api for details, or email support@bug.ly if you have any questions.)') unless @@api_base
535
+
536
+ if !verify_ssl_certs
537
+ unless @no_verify
538
+ $stderr.puts "WARNING: Running without SSL cert verification. Execute 'Bugly.verify_ssl_certs = true' to enable verification."
539
+ @no_verify = true
540
+ end
541
+ ssl_opts = { :verify_ssl => false }
542
+ elsif !Util.file_readable(@@ssl_bundle_path)
543
+ unless @no_bundle
544
+ $stderr.puts "WARNING: Running without SSL cert verification because #{@@ssl_bundle_path} isn't readable"
545
+ @no_bundle = true
546
+ end
547
+ ssl_opts = { :verify_ssl => false }
548
+ else
549
+ ssl_opts = {
550
+ :verify_ssl => OpenSSL::SSL::VERIFY_PEER,
551
+ :ssl_ca_file => @@ssl_bundle_path
552
+ }
553
+ end
554
+ uname = (@@uname ||= RUBY_PLATFORM =~ /linux|darwin/i ? `uname -a 2>/dev/null`.strip : nil)
555
+ lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})"
556
+ ua = {
557
+ :bindings_version => Bugly::VERSION,
558
+ :lang => 'ruby',
559
+ :lang_version => lang_version,
560
+ :platform => RUBY_PLATFORM,
561
+ :publisher => 'bugly',
562
+ :uname => uname
563
+ }
564
+
565
+ params = Util.objects_to_ids(params)
566
+ case method.to_s.downcase.to_sym
567
+ when :get, :head, :delete
568
+ # Make params into GET parameters
569
+ headers = { :params => params }.merge(headers)
570
+ payload = nil
571
+ else
572
+ payload = params
573
+ end
574
+
575
+ # There's a bug in some version of activesupport where JSON.dump
576
+ # stops working
577
+ begin
578
+ headers = { :x_bugly_client_user_agent => JSON.dump(ua) }.merge(headers)
579
+ rescue => e
580
+ headers = {
581
+ :x_bugly_client_raw_user_agent => ua.inspect,
582
+ :error => "#{e} (#{e.class})"
583
+ }.merge(headers)
584
+ end
585
+
586
+ headers = {
587
+ :user_agent => "Bugly/v1 RubyBindings/#{Bugly::VERSION}",
588
+ "X-BuglyToken" => api_key,
589
+ :accept => "application/json"
590
+ }.merge(headers)
591
+ opts = {
592
+ :method => method,
593
+ :url => self.api_url(url),
594
+ # :user => api_key,
595
+ :headers => headers,
596
+ :open_timeout => 30,
597
+ :payload => payload,
598
+ :timeout => 80
599
+ }.merge(ssl_opts)
600
+
601
+ begin
602
+ response = execute_request(opts)
603
+ rescue SocketError => e
604
+ self.handle_restclient_error(e)
605
+ rescue NoMethodError => e
606
+ # Work around RestClient bug
607
+ if e.message =~ /\WRequestFailed\W/
608
+ e = APIConnectionError.new('Unexpected HTTP response code')
609
+ self.handle_restclient_error(e)
610
+ else
611
+ raise
612
+ end
613
+ rescue RestClient::ExceptionWithResponse => e
614
+ if rcode = e.http_code and rbody = e.http_body
615
+ self.handle_api_error(rcode, rbody)
616
+ else
617
+ self.handle_restclient_error(e)
618
+ end
619
+ rescue RestClient::Exception, Errno::ECONNREFUSED => e
620
+ self.handle_restclient_error(e)
621
+ end
622
+
623
+ rbody = response.body
624
+ rcode = response.code
625
+ begin
626
+ # Would use :symbolize_names => true, but apparently there is
627
+ # some library out there that makes symbolize_names not work.
628
+ resp = JSON.parse(rbody)
629
+ rescue JSON::ParserError
630
+ raise APIError.new("Invalid response object from API: #{rbody.inspect} (HTTP response code was #{rcode})", rcode, rbody)
631
+ end
632
+
633
+ resp = Util.symbolize_names(resp)
634
+ [resp, api_key]
635
+ end
636
+
637
+ private
638
+
639
+ def self.execute_request(opts)
640
+ RestClient::Request.execute(opts)
641
+ end
642
+
643
+ def self.handle_api_error(rcode, rbody)
644
+ begin
645
+ error_obj = JSON.parse(rbody)
646
+ error_obj = Util.symbolize_names(error_obj)
647
+ error = error_obj[:error] or raise BuglyError.new # escape from parsing
648
+ rescue JSON::ParserError, BuglyError
649
+ raise APIError.new("Invalid response object from API: #{rbody.inspect} (HTTP response code was #{rcode})", rcode, rbody)
650
+ end
651
+
652
+ case rcode
653
+ when 400, 402, 404 then
654
+ raise invalid_request_error(error, rcode, rbody, error_obj)
655
+ when 401
656
+ raise authentication_error(error, rcode, rbody, error_obj)
657
+ else
658
+ raise api_error(error, rcode, rbody, error_obj)
659
+ end
660
+ end
661
+
662
+ def self.invalid_request_error(error, rcode, rbody, error_obj); InvalidRequestError.new(error[:message], error[:param], rcode, rbody, error_obj); end
663
+ def self.authentication_error(error, rcode, rbody, error_obj); AuthenticationError.new(error[:message], rcode, rbody, error_obj); end
664
+ def self.api_error(error, rcode, rbody, error_obj); APIError.new(error[:message], rcode, rbody, error_obj); end
665
+
666
+ def self.handle_restclient_error(e)
667
+ case e
668
+ when RestClient::ServerBrokeConnection, RestClient::RequestTimeout
669
+ message = "Could not connect to Bugly (#{@@api_base}). Please check your internet connection and try again. If this problem persists, you should check Bugly's service status at https://twitter.com/buglystatus, or let us know at support@bug.ly."
670
+ when RestClient::SSLCertificateNotVerified
671
+ message = "Could not verify Bugly's SSL certificate. Please make sure that your network is not intercepting certificates. (Try going to https://api.bug.ly/v1 in your browser.) If this problem persists, let us know at support@bug.ly."
672
+ when SocketError
673
+ message = "Unexpected error communicating when trying to connect to Bugly. HINT: You may be seeing this message because your DNS is not working. To check, try running 'host bug.ly' from the command line."
674
+ else
675
+ message = "Unexpected error communicating with Bugly. If this problem persists, let us know at support@bug.ly."
676
+ end
677
+ message += "\n\n(Network error: #{e.message})"
678
+ raise APIConnectionError.new(message)
679
+ end
680
+ end