bugsnag 6.24.2 → 6.25.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b0c0062b72786fa763b8b17ff0f72647808c2c0372c08b098b70007acc52dec9
4
- data.tar.gz: c467bdbbe899f9e6f2ec1bf47071c8f70228f7cac51f292e8b77a6c9724da368
3
+ metadata.gz: b368e5f42be9d382e6a4fc6900155646612e86378597604162f9ca3ac7a9f9bd
4
+ data.tar.gz: 380fb1702fd23efb10d3a0155e2c7909f3b599cc6e7863bd4e6b3f057b06c2db
5
5
  SHA512:
6
- metadata.gz: c2cd2286a921e1cb2ad6e78e620c097a7a5937be53ebb75f020cc7eaba31505c904315083b545a1f9758ddb14c2e902c8e47e8845eef3a242fd8aec810860496
7
- data.tar.gz: 15410f4d0280b5abb8f9804a6b66c6d05eed52c025e394ed0fbc642507d7ac64b2cb90db5a1dda0a946143d85dbab4d148dafab88697fa47bb79363d4ff6df3e
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,13 @@
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
+
4
11
  ## v6.24.2 (21 January 2022)
5
12
 
6
13
  ### Fixes
data/VERSION CHANGED
@@ -1 +1 @@
1
- 6.24.2
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
@@ -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.2
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: 2022-01-21 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