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 +4 -4
- data/.yardopts +1 -0
- data/CHANGELOG.md +7 -0
- data/VERSION +1 -1
- data/bugsnag.gemspec +9 -1
- data/lib/bugsnag/feature_flag.rb +74 -0
- data/lib/bugsnag/report.rb +14 -0
- data/lib/bugsnag/utility/feature_data_store.rb +41 -0
- data/lib/bugsnag/utility/feature_flag_delegate.rb +89 -0
- data/lib/bugsnag.rb +16 -0
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b368e5f42be9d382e6a4fc6900155646612e86378597604162f9ca3ac7a9f9bd
|
4
|
+
data.tar.gz: 380fb1702fd23efb10d3a0155e2c7909f3b599cc6e7863bd4e6b3f057b06c2db
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: df743de96ef2f1fcb04c3b38cbcf7c9ff293926d5ce7373b990a1512cfcf1707e90dee8e1cd04d34c37c34359138123f8b6da537cebd933d24f7b3e5afe3c5e0
|
7
|
+
data.tar.gz: 7aadfbaeec36c193d50d144cb4bcd2d09a8072156d1dd5c96bc5a8c7a2b262ca6ed7c24321216b1239e9ce3b22cd2a3cfef07f8f4fba58ed5004bbe6e11b8b5e
|
data/.yardopts
CHANGED
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.
|
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
|
-
|
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
|
data/lib/bugsnag/report.rb
CHANGED
@@ -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.
|
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
|
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.
|
125
|
+
rubygems_version: 3.3.7
|
123
126
|
signing_key:
|
124
127
|
specification_version: 4
|
125
128
|
summary: Ruby notifier for bugsnag.com
|