bugly 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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