bugsnag 6.24.1 → 6.25.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8f9bcfd0b8a1fa0f25ff5bb465319cb96f254f3cef1f7aaac847ecd7f624062b
4
- data.tar.gz: 3eb77e9bd754fe86d185dc062b9b41d9c224d0b8901991d75f95f80c22b266f3
3
+ metadata.gz: b368e5f42be9d382e6a4fc6900155646612e86378597604162f9ca3ac7a9f9bd
4
+ data.tar.gz: 380fb1702fd23efb10d3a0155e2c7909f3b599cc6e7863bd4e6b3f057b06c2db
5
5
  SHA512:
6
- metadata.gz: d5f463a241c45255229297447cbc047526db02c04b6cf0ea03905f46a0ba6ada617d0de2d9aeadaa17da5e944f0ddcbe6418c564071331230c50e825b801bea6
7
- data.tar.gz: c27128f59737759e67c028a3984f41f6792746d27c42c7ee4c8820f3d0eda1bbef580c0dde4dbaf1ad63d4433eeeadb6c462344c58c2bcaf4deb5d5f218e54e5
6
+ metadata.gz: df743de96ef2f1fcb04c3b38cbcf7c9ff293926d5ce7373b990a1512cfcf1707e90dee8e1cd04d34c37c34359138123f8b6da537cebd933d24f7b3e5afe3c5e0
7
+ data.tar.gz: 7aadfbaeec36c193d50d144cb4bcd2d09a8072156d1dd5c96bc5a8c7a2b262ca6ed7c24321216b1239e9ce3b22cd2a3cfef07f8f4fba58ed5004bbe6e11b8b5e
data/.yardopts CHANGED
@@ -4,6 +4,7 @@
4
4
  --no-private
5
5
  --protected
6
6
  --title "bugsnag-ruby API Documentation"
7
+ --embed-mixins
7
8
  lib/**/*.rb
8
9
  -
9
10
  README.md
data/CHANGELOG.md CHANGED
@@ -1,6 +1,21 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ ## v6.26.0 (1 December 2022)
5
+
6
+ ### Enhancements
7
+
8
+ * Add support for feature flags & experiments. For more information, please see https://docs.bugsnag.com/product/features-experiments
9
+ | [#758](https://github.com/bugsnag/bugsnag-ruby/pull/758)
10
+
11
+ ## v6.24.2 (21 January 2022)
12
+
13
+ ### Fixes
14
+
15
+ * Avoid rescuing from errors in Active Record transaction callbacks in versions of Rails where they will be re-raised
16
+ | [#709](https://github.com/bugsnag/bugsnag-ruby/pull/709)
17
+ | [apalmblad](https://github.com/apalmblad)
18
+
4
19
  ## v6.24.1 (30 November 2021)
5
20
 
6
21
  ### Fixes
data/VERSION CHANGED
@@ -1 +1 @@
1
- 6.24.1
1
+ 6.25.0
data/bugsnag.gemspec CHANGED
@@ -18,5 +18,13 @@ Gem::Specification.new do |s|
18
18
  ]
19
19
  s.require_paths = ["lib"]
20
20
  s.required_ruby_version = '>= 1.9.2'
21
- s.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
21
+
22
+ ruby_version = Gem::Version.new(RUBY_VERSION.dup)
23
+
24
+ if ruby_version < Gem::Version.new('2.2.0')
25
+ # concurrent-ruby 1.1.10 requires Ruby 2.2+
26
+ s.add_runtime_dependency 'concurrent-ruby', '~> 1.0', '< 1.1.10'
27
+ else
28
+ s.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
29
+ end
22
30
  end
@@ -0,0 +1,74 @@
1
+ module Bugsnag
2
+ class FeatureFlag
3
+ # Get the name of this feature flag
4
+ #
5
+ # @return [String]
6
+ attr_reader :name
7
+
8
+ # Get the variant of this feature flag
9
+ #
10
+ # @return [String, nil]
11
+ attr_reader :variant
12
+
13
+ # @param name [String] The name of this feature flags
14
+ # @param variant [String, nil] An optional variant for this flag
15
+ def initialize(name, variant = nil)
16
+ @name = name
17
+ @variant = coerce_variant(variant)
18
+ end
19
+
20
+ def ==(other)
21
+ self.class == other.class && @name == other.name && @variant == other.variant
22
+ end
23
+
24
+ def hash
25
+ [@name, @variant].hash
26
+ end
27
+
28
+ # Convert this flag to a hash
29
+ #
30
+ # @example With no variant
31
+ # { "featureFlag" => "name" }
32
+ #
33
+ # @example With a variant
34
+ # { "featureFlag" => "name", "variant" => "variant" }
35
+ #
36
+ # @return [Hash{String => String}]
37
+ def to_h
38
+ if @variant.nil?
39
+ { "featureFlag" => @name }
40
+ else
41
+ { "featureFlag" => @name, "variant" => @variant }
42
+ end
43
+ end
44
+
45
+ # Check if this flag is valid, i.e. has a name that's a String and a variant
46
+ # that's either nil or a String
47
+ #
48
+ # @return [Boolean]
49
+ def valid?
50
+ @name.is_a?(String) &&
51
+ !@name.empty? &&
52
+ (@variant.nil? || @variant.is_a?(String))
53
+ end
54
+
55
+ private
56
+
57
+ # Coerce this variant into a valid value (String or nil)
58
+ #
59
+ # If the variant is not already a string or nil, we use #to_s to coerce it.
60
+ # If #to_s raises, the variant will be set to nil
61
+ #
62
+ # @param variant [Object]
63
+ # @return [String, nil]
64
+ def coerce_variant(variant)
65
+ if variant.nil? || variant.is_a?(String)
66
+ variant
67
+ else
68
+ variant.to_s
69
+ end
70
+ rescue StandardError
71
+ nil
72
+ end
73
+ end
74
+ end
@@ -47,6 +47,29 @@ module Bugsnag
47
47
  end
48
48
  end
49
49
 
50
+ ##
51
+ # Do we need to rescue (& notify) in Active Record callbacks?
52
+ #
53
+ # On Rails versions < 4.2, Rails did not raise errors in AR callbacks
54
+ # On Rails version 4.2, a config option was added to control this
55
+ # On Rails version 5.0, the config option was removed and errors in callbacks
56
+ # always bubble up
57
+ #
58
+ # @api private
59
+ def self.rescue_in_active_record_callbacks?
60
+ # Rails 5+ will re-raise errors in callbacks, so we don't need to rescue them
61
+ return false if ::Rails::VERSION::MAJOR > 4
62
+
63
+ # before 4.2, errors were always swallowed, so we need to rescue them
64
+ return true if ::Rails::VERSION::MAJOR < 4
65
+
66
+ # a config option was added in 4.2 to control this, but won't exist in 4.0 & 4.1
67
+ return true unless ActiveRecord::Base.respond_to?(:raise_in_transactional_callbacks)
68
+
69
+ # if the config option is false, we need to rescue and notify
70
+ ActiveRecord::Base.raise_in_transactional_callbacks == false
71
+ end
72
+
50
73
  rake_tasks do
51
74
  require "bugsnag/integrations/rake"
52
75
  load "bugsnag/tasks/bugsnag.rake"
@@ -70,8 +93,10 @@ module Bugsnag
70
93
  end
71
94
 
72
95
  ActiveSupport.on_load(:active_record) do
73
- require "bugsnag/integrations/rails/active_record_rescue"
74
- include Bugsnag::Rails::ActiveRecordRescue
96
+ if Bugsnag::Railtie.rescue_in_active_record_callbacks?
97
+ require "bugsnag/integrations/rails/active_record_rescue"
98
+ include Bugsnag::Rails::ActiveRecordRescue
99
+ end
75
100
  end
76
101
 
77
102
  ActiveSupport.on_load(:active_job) do
@@ -6,6 +6,8 @@ require "bugsnag/stacktrace"
6
6
  module Bugsnag
7
7
  # rubocop:todo Metrics/ClassLength
8
8
  class Report
9
+ include Utility::FeatureDataStore
10
+
9
11
  NOTIFIER_NAME = "Ruby Bugsnag Notifier"
10
12
  NOTIFIER_VERSION = Bugsnag::VERSION
11
13
  NOTIFIER_URL = "https://www.bugsnag.com"
@@ -143,6 +145,7 @@ module Bugsnag
143
145
  self.user = {}
144
146
 
145
147
  @metadata_delegate = Utility::MetadataDelegate.new
148
+ @feature_flag_delegate = Bugsnag.feature_flag_delegate.dup
146
149
  end
147
150
 
148
151
  ##
@@ -220,6 +223,7 @@ module Bugsnag
220
223
  time: @created_at
221
224
  },
222
225
  exceptions: exceptions,
226
+ featureFlags: @feature_flag_delegate.as_json,
223
227
  groupingHash: grouping_hash,
224
228
  metaData: meta_data,
225
229
  session: session,
@@ -239,6 +243,7 @@ module Bugsnag
239
243
  :version => NOTIFIER_VERSION,
240
244
  :url => NOTIFIER_URL
241
245
  },
246
+ :payloadVersion => CURRENT_PAYLOAD_VERSION,
242
247
  :events => [payload_event]
243
248
  }
244
249
  end
@@ -357,6 +362,13 @@ module Bugsnag
357
362
  @metadata_delegate.clear_metadata(@meta_data, section, *args)
358
363
  end
359
364
 
365
+ # Get the array of stored feature flags
366
+ #
367
+ # @return [Array<Bugsnag::FeatureFlag>]
368
+ def feature_flags
369
+ @feature_flag_delegate.to_a
370
+ end
371
+
360
372
  ##
361
373
  # Set information about the current user
362
374
  #
@@ -393,6 +405,8 @@ module Bugsnag
393
405
 
394
406
  private
395
407
 
408
+ attr_reader :feature_flag_delegate
409
+
396
410
  def update_handled_counts(is_unhandled, was_unhandled)
397
411
  # do nothing if there is no session to update
398
412
  return if @session.nil?
@@ -0,0 +1,41 @@
1
+ module Bugsnag::Utility
2
+ # @abstract Requires a #feature_flag_delegate method returning a
3
+ # {Bugsnag::Utility::FeatureFlagDelegate}
4
+ module FeatureDataStore
5
+ # Add a feature flag with the given name & variant
6
+ #
7
+ # @param name [String]
8
+ # @param variant [String, nil]
9
+ # @return [void]
10
+ def add_feature_flag(name, variant = nil)
11
+ feature_flag_delegate.add(name, variant)
12
+ end
13
+
14
+ # Merge the given array of FeatureFlag instances into the stored feature
15
+ # flags
16
+ #
17
+ # New flags will be appended to the array. Flags with the same name will be
18
+ # overwritten, but their position in the array will not change
19
+ #
20
+ # @param feature_flags [Array<Bugsnag::FeatureFlag>]
21
+ # @return [void]
22
+ def add_feature_flags(feature_flags)
23
+ feature_flag_delegate.merge(feature_flags)
24
+ end
25
+
26
+ # Remove the stored flag with the given name
27
+ #
28
+ # @param name [String]
29
+ # @return [void]
30
+ def clear_feature_flag(name)
31
+ feature_flag_delegate.remove(name)
32
+ end
33
+
34
+ # Remove all the stored flags
35
+ #
36
+ # @return [void]
37
+ def clear_feature_flags
38
+ feature_flag_delegate.clear
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,89 @@
1
+ module Bugsnag::Utility
2
+ # @api private
3
+ class FeatureFlagDelegate
4
+ def initialize
5
+ # feature flags are stored internally in a hash of "name" => <FeatureFlag>
6
+ # we don't use a Set because new feature flags should overwrite old ones
7
+ # that share a name, but FeatureFlag equality also uses the variant
8
+ @storage = {}
9
+ end
10
+
11
+ def initialize_dup(original)
12
+ super
13
+
14
+ # copy the internal storage when 'dup' is called
15
+ @storage = @storage.dup
16
+ end
17
+
18
+ # Add a feature flag with the given name & variant
19
+ #
20
+ # @param name [String]
21
+ # @param variant [String, nil]
22
+ # @return [void]
23
+ def add(name, variant)
24
+ flag = Bugsnag::FeatureFlag.new(name, variant)
25
+
26
+ return unless flag.valid?
27
+
28
+ @storage[flag.name] = flag
29
+ end
30
+
31
+ # Merge the given array of FeatureFlag instances into the stored feature
32
+ # flags
33
+ #
34
+ # New flags will be appended to the array. Flags with the same name will be
35
+ # overwritten, but their position in the array will not change
36
+ #
37
+ # @param feature_flags [Array<Bugsnag::FeatureFlag>]
38
+ # @return [void]
39
+ def merge(feature_flags)
40
+ feature_flags.each do |flag|
41
+ next unless flag.is_a?(Bugsnag::FeatureFlag)
42
+ next unless flag.valid?
43
+
44
+ @storage[flag.name] = flag
45
+ end
46
+ end
47
+
48
+ # Remove the stored flag with the given name
49
+ #
50
+ # @param name [String]
51
+ # @return [void]
52
+ def remove(name)
53
+ @storage.delete(name)
54
+ end
55
+
56
+ # Remove all the stored flags
57
+ #
58
+ # @return [void]
59
+ def clear
60
+ @storage.clear
61
+ end
62
+
63
+ # Get an array of FeatureFlag instances
64
+ #
65
+ # @example
66
+ # [
67
+ # <#Bugsnag::FeatureFlag>,
68
+ # <#Bugsnag::FeatureFlag>,
69
+ # ]
70
+ #
71
+ # @return [Array<Bugsnag::FeatureFlag>]
72
+ def to_a
73
+ @storage.values
74
+ end
75
+
76
+ # Get the feature flags in their JSON representation
77
+ #
78
+ # @example
79
+ # [
80
+ # { "featureFlag" => "name", "variant" => "variant" },
81
+ # { "featureFlag" => "another name" },
82
+ # ]
83
+ #
84
+ # @return [Array<Hash{String => String}>]
85
+ def as_json
86
+ to_a.map(&:to_h)
87
+ end
88
+ end
89
+ end
data/lib/bugsnag.rb CHANGED
@@ -2,6 +2,7 @@ require "rubygems"
2
2
  require "thread"
3
3
 
4
4
  require "bugsnag/version"
5
+ require "bugsnag/utility/feature_data_store"
5
6
  require "bugsnag/configuration"
6
7
  require "bugsnag/meta_data"
7
8
  require "bugsnag/report"
@@ -14,6 +15,8 @@ require "bugsnag/delivery"
14
15
  require "bugsnag/delivery/synchronous"
15
16
  require "bugsnag/delivery/thread_queue"
16
17
 
18
+ require "bugsnag/feature_flag"
19
+
17
20
  # Rack is not bundled with the other integrations
18
21
  # as it doesn't auto-configure when loaded
19
22
  require "bugsnag/integrations/rack"
@@ -36,6 +39,7 @@ require "bugsnag/breadcrumbs/breadcrumbs"
36
39
 
37
40
  require "bugsnag/utility/duplicator"
38
41
  require "bugsnag/utility/metadata_delegate"
42
+ require "bugsnag/utility/feature_flag_delegate"
39
43
 
40
44
  # rubocop:todo Metrics/ModuleLength
41
45
  module Bugsnag
@@ -45,6 +49,8 @@ module Bugsnag
45
49
  NIL_EXCEPTION_DESCRIPTION = "'nil' was notified as an exception"
46
50
 
47
51
  class << self
52
+ include Utility::FeatureDataStore
53
+
48
54
  ##
49
55
  # Configure the Bugsnag notifier application-wide settings.
50
56
  #
@@ -426,6 +432,16 @@ module Bugsnag
426
432
  configuration.clear_metadata(section, *args)
427
433
  end
428
434
 
435
+ # Expose the feature flag delegate internally for use when creating new Events
436
+ #
437
+ # The Bugsnag module's feature_flag_delegate is request-specific
438
+ #
439
+ # @return [Bugsnag::Utility::FeatureFlagDelegate]
440
+ # @api private
441
+ def feature_flag_delegate
442
+ configuration.request_data[:feature_flag_delegate] ||= Utility::FeatureFlagDelegate.new
443
+ end
444
+
429
445
  private
430
446
 
431
447
  def should_deliver_notification?(exception, auto_notify)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bugsnag
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.24.1
4
+ version: 6.25.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Smith
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-30 00:00:00.000000000 Z
11
+ date: 2022-12-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -55,6 +55,7 @@ files:
55
55
  - lib/bugsnag/endpoint_validator.rb
56
56
  - lib/bugsnag/error.rb
57
57
  - lib/bugsnag/event.rb
58
+ - lib/bugsnag/feature_flag.rb
58
59
  - lib/bugsnag/helpers.rb
59
60
  - lib/bugsnag/integrations/delayed_job.rb
60
61
  - lib/bugsnag/integrations/mailman.rb
@@ -97,6 +98,8 @@ files:
97
98
  - lib/bugsnag/tasks/bugsnag.rake
98
99
  - lib/bugsnag/utility/circular_buffer.rb
99
100
  - lib/bugsnag/utility/duplicator.rb
101
+ - lib/bugsnag/utility/feature_data_store.rb
102
+ - lib/bugsnag/utility/feature_flag_delegate.rb
100
103
  - lib/bugsnag/utility/metadata_delegate.rb
101
104
  - lib/bugsnag/version.rb
102
105
  - lib/generators/bugsnag/bugsnag_generator.rb
@@ -119,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
119
122
  - !ruby/object:Gem::Version
120
123
  version: '0'
121
124
  requirements: []
122
- rubygems_version: 3.2.22
125
+ rubygems_version: 3.3.7
123
126
  signing_key:
124
127
  specification_version: 4
125
128
  summary: Ruby notifier for bugsnag.com