barsoom_utils 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e5c4a4cdd58c36b3f57238e598a74ca4e3a2c64c3c4fea946413c451141a6545
4
+ data.tar.gz: 42215d01e3f207796368dd691a46c05f761f2c2031d0047a4d17173914c15e0d
5
+ SHA512:
6
+ metadata.gz: bc2d73e1c4b8e876a75286387bf25c3982d5229d2bf2b600b66ad292799b009acc68395512485e489540b50dc28fd65ec764b15c9179fe8f4fcf75e0c2ec096f
7
+ data.tar.gz: a9a1bb896ead589837004ff62cce39d98536c14bed2341aa5e563ed90807356ba1a7aa265bb81bb7a71e913f0ea1c7878254e8768f6e1c21601cccf7cbffa20c
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ # See http://help.github.com/ignore-files/ for more about ignoring files.
2
+ #
3
+ # If you find yourself ignoring temporary files generated by your text editor
4
+ # or operating system, you probably want to add a global ignore instead:
5
+ # git config --global core.excludesfile '~/.gitignore_global'
6
+
7
+ # Ignore bundler config.
8
+ /.bundle
9
+
10
+ # Ignore all tempfiles.
11
+ /tmp
12
+
13
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format progress
2
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1 @@
1
+ inherit_from: "./shared_rubocop.yml"
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+ source "https://rubygems.org"
3
+
4
+ # Specify your gem's dependencies in barsoom_utils.gemspec
5
+ gemspec
data/README.md ADDED
@@ -0,0 +1,94 @@
1
+ # BarsoomUtils
2
+
3
+ Barsoom Utils is our internal toolbox for small classes that we share in most of our projects.
4
+
5
+ This gem is intentionally not released since it's for internal use, but we keep it open
6
+ source since it might have some value for others.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem "barsoom_utils", github: "barsoom/barsoom_utils"
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ ## Usage
21
+
22
+ You need to explicitly require each class you want to use.
23
+ Also note that we use BarsoomUtils as namespace.
24
+
25
+ List of utils:
26
+
27
+ ```ruby
28
+ require "barsoom_utils/exception_notifier" # Notify devs about an exception without necessarily letting it appear to the user as a 500 error.
29
+ require "barsoom_utils/spec/debug_helpers" # Add show_page helper for capybara feature specs
30
+ require "barsoom_utils/feature_toggle" # Convenient way of hiding/showing a feature.
31
+ require "barsoom_utils/ping_health_check" # Report running of background jobs to healthchecks.io. NOTE: You need to set the env ENABLE_HEALTH_CHECKS=true to enable reporting (only set it in production).
32
+ ```
33
+
34
+ We also have some shared configuration. To use our Rubocop configuration, add this to the beginning of your `.rubocop.yml`.
35
+
36
+ If you use Rails (also make sure `rubocop-rails` is in your `Gemfile`):
37
+
38
+ ```yaml
39
+ require: rubocop-rails
40
+
41
+ inherit_gem:
42
+ barsoom_utils:
43
+ - shared_rubocop.yml
44
+ - shared_rubocop_rails.yml
45
+ ```
46
+
47
+ If you don't use Rails:
48
+
49
+ ```yaml
50
+ inherit_gem:
51
+ barsoom_utils:
52
+ - shared_rubocop.yml
53
+ ```
54
+
55
+ ## Developing
56
+
57
+ Running tests:
58
+
59
+ rake
60
+
61
+ ## Contributing
62
+
63
+ 1. Fork it ( https://github.com/barsoom/barsoom_utils/fork )
64
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
65
+ 3. Commit your changes (`git commit -am "Add some feature"`)
66
+ 4. Push to the branch (`git push origin my-new-feature`)
67
+ 5. Create a new Pull Request
68
+
69
+ ## Credits and license
70
+
71
+ By [Barsoom](http://barsoom.se) under the MIT license:
72
+
73
+ Copyright (c) 2014 Barsoom AB
74
+
75
+ MIT License
76
+
77
+ Permission is hereby granted, free of charge, to any person obtaining
78
+ a copy of this software and associated documentation files (the
79
+ "Software"), to deal in the Software without restriction, including
80
+ without limitation the rights to use, copy, modify, merge, publish,
81
+ distribute, sublicense, and/or sell copies of the Software, and to
82
+ permit persons to whom the Software is furnished to do so, subject to
83
+ the following conditions:
84
+
85
+ The above copyright notice and this permission notice shall be
86
+ included in all copies or substantial portions of the Software.
87
+
88
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
89
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
90
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
91
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
92
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
93
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
94
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+ lib = File.expand_path("../lib", __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "barsoom_utils/version"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "barsoom_utils"
9
+ spec.version = BarsoomUtils::VERSION
10
+ spec.authors = [ "Tomas Skogberg" ]
11
+ spec.email = [ "tomas.skogberg@gmail.com" ]
12
+ spec.summary = %q{Various helpful utils}
13
+ spec.homepage = "http://barsoom.se"
14
+ spec.license = "MIT"
15
+ spec.metadata = { "rubygems_mfa_required" => "true" }
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = [ "lib" ]
21
+
22
+ spec.add_development_dependency "bundler"
23
+ spec.add_development_dependency "rake"
24
+ spec.add_development_dependency "rspec"
25
+ spec.add_development_dependency "httparty"
26
+ spec.add_development_dependency "lolcat"
27
+ spec.add_development_dependency "attr_extras"
28
+ spec.add_development_dependency "redis"
29
+ spec.add_development_dependency "honeybadger"
30
+ spec.add_development_dependency "fixme"
31
+ spec.add_development_dependency "rubocop"
32
+
33
+ # Run time dependencies should not be specified here. You are unlikely to use all of BarsoomUtils in each project, so instead, add the gems that are needed in the project.
34
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+ # Notify devs about an exception without necessarily
3
+ # letting it appear to the user as a 500 error.
4
+
5
+ module BarsoomUtils
6
+ class ExceptionNotifier
7
+ def self.notify(exception, context: {})
8
+ # Inelegant workaround for the fact that we've confused this method with .message at least once.
9
+ # TODO: Fold them into a single method?
10
+ unless exception.is_a?(Exception)
11
+ raise "Expected an exception but got: #{exception.inspect}"
12
+ end
13
+
14
+ Honeybadger.notify(exception, context: context)
15
+ end
16
+
17
+ def self.message(message, details_or_context = nil, context_or_nothing = nil)
18
+ if context_or_nothing
19
+ details = details_or_context
20
+ context = context_or_nothing
21
+ elsif details_or_context.is_a?(Hash)
22
+ details = nil
23
+ context = details_or_context
24
+ else
25
+ details = details_or_context
26
+ context = {}
27
+ end
28
+
29
+ details ||= "(no message)"
30
+
31
+ Honeybadger.notify(
32
+ error_class: message,
33
+ error_message: details.to_s,
34
+ context: context.to_h,
35
+ )
36
+ end
37
+
38
+ # Wrap this around code to add context when reporting errors.
39
+ def self.run_with_context(context, &block)
40
+ # The load/dump achieves a "deep dup" without the "deep dep" of Active Support 🥁
41
+ old_context = Marshal.load(Marshal.dump(Honeybadger.get_context))
42
+
43
+ Honeybadger.context(context)
44
+ block.call
45
+ ensure
46
+ Honeybadger.context.clear!
47
+ Honeybadger.context(old_context)
48
+ end
49
+
50
+ # While developing a feature we'd like the feature developers to be responsible for any errors that occur.
51
+ # Wrapping the new code with this tags the errors as "wip" in order to hide them from the dashboard.
52
+ def self.developers_working_on_this_feature_are_responsible_for_errors_until(expire_on, &block)
53
+ block.call
54
+ rescue => ex
55
+ FIXME "#{expire_on}: WIP error-handling code needs to be removed!"
56
+ notify(ex, context: { tags: "wip" })
57
+
58
+ is_rails_production = defined?(Rails) && Rails.env.production?
59
+ raise unless is_rails_production
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+ require "attr_extras"
3
+ require "redis"
4
+
5
+ module BarsoomUtils
6
+ class FeatureToggle
7
+ # We (counter-intuitively, perhaps) store disabled toggles, so we can assume them to be enabled by default in dev, staging and tests.
8
+ REDIS_KEY = "disabled_feature_toggles"
9
+
10
+ def self.redis=(redis)
11
+ @redis = redis
12
+ end
13
+
14
+ def self.redis
15
+ @redis || $redis
16
+ end
17
+
18
+ def self.on?(feature, controller_or_view = nil, redis: self.redis)
19
+ new(feature, controller_or_view: controller_or_view, redis: redis).on?
20
+ end
21
+
22
+ def self.off?(feature, controller_or_view = nil, redis: self.redis)
23
+ new(feature, controller_or_view: controller_or_view, redis: redis).off?
24
+ end
25
+
26
+ def self.turn_on(feature, redis: self.redis)
27
+ new(feature, redis: redis).turn_on
28
+ end
29
+
30
+ def self.turn_off(feature, redis: self.redis)
31
+ new(feature, redis: redis).turn_off
32
+ end
33
+
34
+ def self.list
35
+ redis.smembers(REDIS_KEY).sort
36
+ end
37
+
38
+ pattr_initialize :feature_name, [ :controller_or_view, :redis ]
39
+
40
+ def on?
41
+ if has_param_override?
42
+ on_according_to_param?
43
+ else
44
+ on_according_to_redis?
45
+ end
46
+ end
47
+
48
+ def off?
49
+ not on?
50
+ end
51
+
52
+ def turn_off
53
+ redis.sadd REDIS_KEY, feature_name
54
+ end
55
+
56
+ def turn_on
57
+ redis.srem REDIS_KEY, feature_name
58
+ end
59
+
60
+ private
61
+
62
+ def has_param_override?
63
+ controller_or_view && controller_or_view.params.has_key?("ft_#{feature_name}")
64
+ end
65
+
66
+ def on_according_to_param?
67
+ value = controller_or_view && controller_or_view.params["ft_#{feature_name}"]
68
+ value == "true"
69
+ end
70
+
71
+ def on_according_to_redis?
72
+ not redis.sismember(REDIS_KEY, feature_name)
73
+ end
74
+
75
+ def redis
76
+ @redis || self.class.redis
77
+ end
78
+
79
+ def feature_name
80
+ @feature_name.to_s
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ require "httparty"
3
+ require "attr_extras"
4
+ require "barsoom_utils/exception_notifier"
5
+
6
+ module BarsoomUtils
7
+ class PingHealthCheck
8
+ method_object :id
9
+
10
+ def call
11
+ return unless ENV["ENABLE_HEALTH_CHECKS"]
12
+
13
+ response = ping_healthcheck
14
+
15
+ if response.code != 200
16
+ # "The presence of the cf-request-id header in the response confirms
17
+ # the request was proxied through Cloudflare"
18
+ # https://support.cloudflare.com/hc/en-us/articles/203118044-Gathering-information-for-troubleshooting-sites
19
+ raise "Bad response, cf-request-id header: #{response.headers["cf-request-id"]}, response body: #{response.inspect}"
20
+ else
21
+ response
22
+ end
23
+ rescue => ex
24
+ BarsoomUtils::ExceptionNotifier.message("Couldn't report to healthchecks.io, maybe the service is down?", "Check: #{id}, Error: #{ex.inspect}")
25
+ end
26
+
27
+ private
28
+
29
+ def ping_healthcheck
30
+ HTTParty.get("https://hchk.io/#{id}")
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ require "lolcat"
3
+
4
+ module BarsoomUtils
5
+ module Spec
6
+ module DebugHelpers
7
+ def show_page
8
+ path = Rails.root.join("public/show_page.html")
9
+ File.write(path, page.source)
10
+ Lol.println "show_page: http://#{hostname}/show_page.html", os: 10, freq: 0.1, spread: 1
11
+ end
12
+
13
+ def hostname
14
+ raise "Implement me. Ex: def hostname; \"auctionet.dev\"; end"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ module BarsoomUtils
3
+ VERSION = "0.1.1"
4
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+ require "barsoom_utils/version"
3
+
4
+ module BarsoomUtils
5
+ end
data/script/test ADDED
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ rspec $@
@@ -0,0 +1,247 @@
1
+ # Configuration for the Rubocop code linting tool. See README.
2
+
3
+ AllCops:
4
+ DisabledByDefault: true
5
+ DisplayStyleGuide: true
6
+ SuggestExtensions: false
7
+ Exclude:
8
+ - db/schema.rb
9
+ - vendor/**/*
10
+ - node_modules/**/*
11
+ - tmp/**/*
12
+ - deploy/tmp/**/*
13
+
14
+ # Enable all Security cops
15
+ Security:
16
+ Enabled: true
17
+
18
+ # Allow:
19
+ #
20
+ # foo
21
+ # .bar
22
+ #
23
+ # Disallow:
24
+ #
25
+ # foo.
26
+ # bar
27
+ #
28
+ Layout/DotPosition:
29
+ EnforcedStyle: leading
30
+
31
+ # Allow: "foo"
32
+ # Disallow: 'foo'
33
+ Style/StringLiterals:
34
+ EnforcedStyle: double_quotes
35
+ StyleGuide: https://github.com/barsoom/devbook/tree/master/styleguide#double-quote-strings-unless-the-string-contains-double-quotes
36
+
37
+ #https://docs.rubocop.org/rubocop/cops_style.html#stylequotedsymbols
38
+ Style/QuotedSymbols:
39
+ EnforcedStyle: same_as_string_literals
40
+
41
+ # Require a comma after the last item in a multi-line hash or array.
42
+ #
43
+ # The "consistent_comma" setting allows
44
+ #
45
+ # [
46
+ # 1, 2,
47
+ # 3,
48
+ # ]
49
+ #
50
+ # Whereas the "comma" setting seems to not want the comma after 3 if 1 and 2 were on the same line.
51
+ Style/TrailingCommaInArrayLiteral:
52
+ EnforcedStyleForMultiline: consistent_comma
53
+ StyleGuide: https://github.com/barsoom/devbook/tree/master/styleguide#add-commas-to-the-end-of-multiline-lists-and-hashes
54
+ Style/TrailingCommaInHashLiteral:
55
+ EnforcedStyleForMultiline: consistent_comma
56
+ StyleGuide: https://github.com/barsoom/devbook/tree/master/styleguide#add-commas-to-the-end-of-multiline-lists-and-hashes
57
+
58
+ # Allow:
59
+ #
60
+ # list.map { |x| x }
61
+ #
62
+ # Disallow:
63
+ #
64
+ # list.map{ |x| x }
65
+ #
66
+ # Not in our styleguide, but it's what we've done for the most part.
67
+ Layout/SpaceBeforeBlockBraces:
68
+ Enabled: true
69
+
70
+ # Allow:
71
+ #
72
+ # list.map {|x| x }
73
+ #
74
+ # Disallow:
75
+ #
76
+ # list.map {|x| x }
77
+ #
78
+ # Not in our styleguide, but it's what we've done for the most part.
79
+ Layout/SpaceInsideBlockBraces:
80
+ Enabled: true
81
+
82
+ # Allow:
83
+ #
84
+ # { a: 1 }
85
+ #
86
+ # Disallow:
87
+ #
88
+ # {a: 1}
89
+ #
90
+ Layout/SpaceInsideHashLiteralBraces:
91
+ Enabled: true
92
+ StyleGuide: https://github.com/barsoom/devbook/tree/master/styleguide#whitespace-in-arrays-and-hashes
93
+
94
+ # Allow:
95
+ #
96
+ # [ 1 ]
97
+ #
98
+ # Disallow:
99
+ #
100
+ # [1]
101
+ #
102
+ Layout/SpaceInsideArrayLiteralBrackets:
103
+ EnforcedStyle: space
104
+ StyleGuide: https://github.com/barsoom/devbook/tree/master/styleguide#whitespace-in-arrays-and-hashes
105
+
106
+ # Allow:
107
+ #
108
+ # "foo" + 1 * 2 / 3**4
109
+ #
110
+ # Disallow:
111
+ #
112
+ # "foo"+1*2/3 ** 4
113
+ #
114
+ # Not in our styleguide, but it's what we've done for the most part.
115
+ Layout/SpaceAroundOperators:
116
+ Enabled: true
117
+
118
+ # bad: f( 3)
119
+ # good: f(3)
120
+ Layout/SpaceInsideParens:
121
+ Enabled: true
122
+
123
+ # bad: "foo #{ var } bar"
124
+ # good: "foo #{var} bar"
125
+ Layout/SpaceInsideStringInterpolation:
126
+ Enabled: true
127
+
128
+ # Disallow trailing whitespace.
129
+ Layout/TrailingWhitespace:
130
+ Enabled: true
131
+ StyleGuide: https://github.com/barsoom/devbook/tree/master/styleguide#avoid-trailing-whitespace
132
+
133
+ # `BigDecimal.new` is deprecated on Ruby 2.5 in favour of `BigDecimal()`.
134
+ Lint/BigDecimalNew:
135
+ Enabled: true
136
+
137
+ Lint/DuplicateMethods:
138
+ Enabled: true
139
+
140
+ Lint/DeprecatedClassMethods:
141
+ Enabled: true
142
+
143
+ Layout/EmptyLineAfterGuardClause:
144
+ Enabled: true
145
+ StyleGuide: https://github.com/barsoom/devbook/tree/master/styleguide#put-a-blank-line-below-guard-statements
146
+
147
+ # Allow:
148
+ #
149
+ # Date.new(2019, 1, 2)
150
+ #
151
+ # Disallow:
152
+ #
153
+ # Date.new(2019,1,2)
154
+ #
155
+ # Not in our styleguide, but it's what we've done for the most part.
156
+ Layout/SpaceAfterComma:
157
+ Enabled: true
158
+
159
+ Style/NumericLiterals:
160
+ Enabled: false
161
+
162
+ Style/StabbyLambdaParentheses:
163
+ Enabled: true
164
+ EnforcedStyle: require_parentheses
165
+ StyleGuide: https://github.com/barsoom/devbook/tree/master/styleguide#lambdas-should-stab-their-arguments
166
+
167
+ # Not in our styleguide, but it's what we've done for the most part. And it ensures "rubocop --auto-correct" doesn't make the StabbyLambdaParentheses rule add in unconventional-to-us spaces.
168
+ Layout/SpaceInLambdaLiteral:
169
+ Enabled: true
170
+ EnforcedStyle: require_no_space
171
+
172
+ Lint/RaiseException:
173
+ Enabled: true
174
+
175
+ # Skip redundant "begin"/"end" at the top level of method definitions or blocks.
176
+ Style/RedundantBegin:
177
+ Enabled: true
178
+
179
+ # Allow:
180
+ #
181
+ # foo.bar
182
+ #
183
+ # Disallow:
184
+ #
185
+ # foo.bar()
186
+ #
187
+ Style/MethodCallWithoutArgsParentheses:
188
+ Enabled: true
189
+
190
+ # https://docs.rubocop.org/rubocop/cops_style.html#enforcedstyleformultiline-comma
191
+ Style/TrailingCommaInArguments:
192
+ Enabled: true
193
+ EnforcedStyleForMultiline: comma
194
+
195
+ # Warn about "too many positional arguments in the parameter list" when more than 5.
196
+ # Warn about "too many positional arguments with optional values" when more than 3.
197
+ Metrics/ParameterLists:
198
+ Enabled: true
199
+ CountKeywordArgs: false
200
+ Max: 5
201
+ MaxOptionalParameters: 3
202
+
203
+ # https://docs.rubocop.org/rubocop/cops_lint.html#lintparenthesesasgroupedexpression
204
+ Lint/ParenthesesAsGroupedExpression:
205
+ Enabled: true
206
+
207
+ # https://docs.rubocop.org/rubocop/cops_lint.html#lintuselessassignment
208
+ Lint/UselessAssignment:
209
+ Enabled: true
210
+
211
+ # https://docs.rubocop.org/rubocop/cops_layout.html#layoutindentationwidth
212
+ Layout/IndentationWidth:
213
+ Enabled: true
214
+
215
+ # https://docs.rubocop.org/rubocop/cops_layout.html#layoutindentationconsistency
216
+ Layout/IndentationConsistency:
217
+ Enabled: true
218
+
219
+ # https://docs.rubocop.org/rubocop/cops_layout.html#layoutclassstructure
220
+ # https://github.com/barsoom/devbook/tree/master/styleguide#use-a-consistent-class-layout
221
+ # AutoCorrect: false because it has a tendency to make private delegates public, and it messes up whitespace
222
+ Layout/ClassStructure:
223
+ Enabled: true
224
+ AutoCorrect: false
225
+ ExpectedOrder:
226
+ - module_inclusion
227
+ - constants
228
+ - association
229
+ - public_attribute_macros
230
+ - public_delegate
231
+ - macros
232
+ - public_class_methods
233
+ - initializer
234
+ - public_methods
235
+ - protected_attribute_macros
236
+ - protected_methods
237
+ - private_attribute_macros
238
+ - private_delegate
239
+ - private_methods
240
+
241
+ # https://docs.rubocop.org/rubocop/cops_style.html#stylehashsyntax
242
+ Style/HashSyntax:
243
+ Enabled: true
244
+ EnforcedStyle: ruby19
245
+
246
+ Security/IoMethods: # new in 1.22
247
+ Enabled: true
@@ -0,0 +1,3 @@
1
+ # Configuration for the RuboCop Rails extension. See README.
2
+
3
+ # TODO: Add something useful!
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+ require "barsoom_utils/exception_notifier"
3
+ require "honeybadger"
4
+ require "fixme"
5
+
6
+ RSpec.describe BarsoomUtils::ExceptionNotifier do
7
+ before do
8
+ allow(Honeybadger).to receive(:notify)
9
+ end
10
+
11
+ describe ".notify" do
12
+ let(:ex) { StandardError.new("boom") }
13
+
14
+ it "passes an exception to Honeybadger" do
15
+ BarsoomUtils::ExceptionNotifier.notify(ex)
16
+
17
+ expect(Honeybadger).to have_received(:notify).with(ex, context: {})
18
+ end
19
+
20
+ it "can pass through context info" do
21
+ BarsoomUtils::ExceptionNotifier.notify(ex, context: { foo: "bar" })
22
+
23
+ expect(Honeybadger).to have_received(:notify).with(ex, context: { foo: "bar" })
24
+ end
25
+
26
+ it "complains if given a non-exception" do
27
+ expect {
28
+ BarsoomUtils::ExceptionNotifier.notify({ foo: "bar" })
29
+ }.to raise_error(/Expected an exception but got:.*foo.*bar/)
30
+
31
+ expect(Honeybadger).not_to have_received(:notify)
32
+ end
33
+ end
34
+
35
+ describe ".message" do
36
+ it "passes a message to Honeybadger" do
37
+ BarsoomUtils::ExceptionNotifier.message("Boom!")
38
+
39
+ expect(Honeybadger).to have_received(:notify).with(
40
+ error_class: "Boom!",
41
+ error_message: "(no message)",
42
+ context: {},
43
+ )
44
+ end
45
+
46
+ it "can take a custom details string" do
47
+ BarsoomUtils::ExceptionNotifier.message("Boom!", "Details!")
48
+
49
+ expect(Honeybadger).to have_received(:notify).with(
50
+ error_class: "Boom!",
51
+ error_message: "Details!",
52
+ context: {},
53
+ )
54
+ end
55
+
56
+ it "can take a context hash" do
57
+ BarsoomUtils::ExceptionNotifier.message("Boom!", foo: "bar")
58
+
59
+ expect(Honeybadger).to have_received(:notify).with(
60
+ error_class: "Boom!",
61
+ error_message: "(no message)",
62
+ context: { foo: "bar" },
63
+ )
64
+ end
65
+
66
+ it "can take a custom details string and a context hash" do
67
+ BarsoomUtils::ExceptionNotifier.message("Boom!", "Details!", foo: "bar")
68
+
69
+ expect(Honeybadger).to have_received(:notify).with(
70
+ error_class: "Boom!",
71
+ error_message: "Details!",
72
+ context: { foo: "bar" },
73
+ )
74
+ end
75
+ end
76
+
77
+ describe ".run_with_context" do
78
+ it "updates the context just within the block" do
79
+ allow(Honeybadger).to receive(:context).and_call_original
80
+ Honeybadger.context({ my_old_context: "hello" })
81
+
82
+ context_in_block = nil
83
+
84
+ expect {
85
+ BarsoomUtils::ExceptionNotifier.run_with_context({ my_new_context: "what up" }) do
86
+ context_in_block = Honeybadger.get_context
87
+ raise "boom"
88
+ end
89
+ }.to raise_error /boom/
90
+
91
+ # Adds the new context while running the code.
92
+ expect(context_in_block).to eq({ my_old_context: "hello", my_new_context: "what up" })
93
+
94
+ # Resets the old context, without keeping the new.
95
+ expect(Honeybadger.get_context).to eq({ my_old_context: "hello" })
96
+ end
97
+ end
98
+
99
+ describe ".developers_working_on_this_feature_are_responsible_for_errors_until" do
100
+ before do
101
+ # We need some value, and this value means we don't have to rescue exceptions in some tests.
102
+ set_rails_production(true)
103
+ end
104
+
105
+ it "notifies Honeybadger with a 'wip' tag" do
106
+ ex = StandardError.new
107
+
108
+ BarsoomUtils::ExceptionNotifier.developers_working_on_this_feature_are_responsible_for_errors_until("9999-01-01") do
109
+ raise ex
110
+ end
111
+
112
+ expect(Honeybadger).to have_received(:notify).with(ex, context: { tags: "wip" })
113
+ end
114
+
115
+ it "does not raise the error in production" do
116
+ set_rails_production(true)
117
+
118
+ ex = StandardError.new
119
+
120
+ expect {
121
+ BarsoomUtils::ExceptionNotifier.developers_working_on_this_feature_are_responsible_for_errors_until("9999-01-01") do
122
+ raise ex
123
+ end
124
+ }.not_to raise_error
125
+ end
126
+
127
+ it "raises the error in non-production" do
128
+ set_rails_production(false)
129
+
130
+ ex = StandardError.new
131
+
132
+ expect {
133
+ BarsoomUtils::ExceptionNotifier.developers_working_on_this_feature_are_responsible_for_errors_until("9999-01-01") do
134
+ raise ex
135
+ end
136
+ }.to raise_error(ex)
137
+ end
138
+
139
+ it "sets a FIXME raise" do
140
+ set_rails_production(false)
141
+
142
+ expect {
143
+ BarsoomUtils::ExceptionNotifier.developers_working_on_this_feature_are_responsible_for_errors_until("1999-01-01") do
144
+ raise "hola"
145
+ end
146
+ }.to raise_error(Fixme::UnfixedError, /Fix by 1999-01-01: WIP error-handling/)
147
+ end
148
+
149
+ private
150
+
151
+ def set_rails_production(bool)
152
+ string_env = bool ? "production" : "test"
153
+ stub_const("Rails", double(:rails, env: double(:env, production?: bool, to_s: string_env)))
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+ require "barsoom_utils/feature_toggle"
3
+
4
+ RSpec.describe BarsoomUtils::FeatureToggle, ".turn_on" do
5
+ it "sets the feature as not disabled" do
6
+ redis = double
7
+ expect(redis).to receive(:srem).with("disabled_feature_toggles", "foo")
8
+ BarsoomUtils::FeatureToggle.turn_on(:foo, redis: redis)
9
+ end
10
+ end
11
+
12
+ RSpec.describe BarsoomUtils::FeatureToggle, ".turn_off" do
13
+ it "sets the feature as disabled" do
14
+ redis = double
15
+ expect(redis).to receive(:sadd).with("disabled_feature_toggles", "foo")
16
+ BarsoomUtils::FeatureToggle.turn_off(:foo, redis: redis)
17
+ end
18
+ end
19
+
20
+ RSpec.describe BarsoomUtils::FeatureToggle, ".on?, .off?" do
21
+ let(:redis) { double(sismember: false) }
22
+
23
+ context "by default" do
24
+ specify { expect(BarsoomUtils::FeatureToggle.on?(:foo, redis: redis)).to be true }
25
+ specify { expect(BarsoomUtils::FeatureToggle.off?(:foo, redis: redis)).to be false }
26
+ end
27
+
28
+ context "when disabled" do
29
+ before do
30
+ allow(redis).to receive(:sismember).with("disabled_feature_toggles", "foo").and_return(true)
31
+ end
32
+
33
+ specify { expect(BarsoomUtils::FeatureToggle.on?(:foo, redis: redis)).to be false }
34
+ specify { expect(BarsoomUtils::FeatureToggle.off?(:foo, redis: redis)).to be true }
35
+ end
36
+
37
+ context "with a param override" do
38
+ it "overrides the value in Redis" do
39
+ expect(BarsoomUtils::FeatureToggle.on?(:foo, redis: redis)).to be true
40
+
41
+ controller = double(params: { "ft_foo" => "false" })
42
+ expect(BarsoomUtils::FeatureToggle.on?(:foo, controller, redis: redis)).to be false
43
+ expect(BarsoomUtils::FeatureToggle.off?(:foo, controller, redis: redis)).to be true
44
+
45
+ controller = double(params: { "ft_foo" => "true" })
46
+ expect(BarsoomUtils::FeatureToggle.on?(:foo, controller, redis: redis)).to be true
47
+ expect(BarsoomUtils::FeatureToggle.off?(:foo, controller, redis: redis)).to be false
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+ require "barsoom_utils/ping_health_check"
3
+
4
+ RSpec.describe BarsoomUtils::PingHealthCheck, ".call" do
5
+ around do |example|
6
+ ENV["ENABLE_HEALTH_CHECKS"] = "true"
7
+ example.run
8
+ ENV["ENABLE_HEALTH_CHECKS"] = nil
9
+ end
10
+
11
+ it "does nothing on a 200 OK response" do
12
+ expect(HTTParty).to receive(:get).with("https://hchk.io/foo").and_return(double(:response, code: 200, headers: {}))
13
+
14
+ BarsoomUtils::PingHealthCheck.call("foo")
15
+ end
16
+
17
+ it "includes CloudFlare request ID header information in failure message" do
18
+ expect(HTTParty).to receive(:get).with("https://hchk.io/foo").and_return(double(:response, code: 503, headers: { "cf-request-id" => "023aa754ae0000517ab8829200000001" }))
19
+ expect(BarsoomUtils::ExceptionNotifier).to receive(:message).with(anything, /cf-request-id header: 023aa754ae0000517ab8829200000001/)
20
+
21
+ BarsoomUtils::PingHealthCheck.call("foo")
22
+ end
23
+
24
+ it "silently reports an error to devs if it fails with a bad response code" do
25
+ expect(HTTParty).to receive(:get).with("https://hchk.io/foo").and_return(double(:response, code: 404, headers: {}))
26
+ expect(BarsoomUtils::ExceptionNotifier).to receive(:message).with(anything, /foo/)
27
+
28
+ BarsoomUtils::PingHealthCheck.call("foo")
29
+ end
30
+
31
+ it "silently reports an error to devs if it fails with an exception" do
32
+ expect(HTTParty).to receive(:get).with("https://hchk.io/foo").and_raise("fail")
33
+ expect(BarsoomUtils::ExceptionNotifier).to receive(:message).with(anything, /foo/)
34
+
35
+ BarsoomUtils::PingHealthCheck.call("foo")
36
+ end
37
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+ require "barsoom_utils/spec/debug_helpers"
3
+
4
+ RSpec.describe BarsoomUtils::Spec::DebugHelpers do
5
+ before { module Rails; end }
6
+
7
+ let(:page) { double(:page, source: "wow such html") }
8
+ let(:helper) { HelperTestClass.new(page) }
9
+
10
+ it "writes the page to the filesystem" do
11
+ allow(Rails).to receive(:root).and_return([])
12
+
13
+ # writes the capybara page contents to the file system
14
+ expect(File).to receive(:write).with("", page.source)
15
+
16
+ # asserts that we expect a hostname to be defined
17
+ expect { helper.show_page }.to raise_error(/Implement me/)
18
+ end
19
+
20
+ class HelperTestClass
21
+ include BarsoomUtils::Spec::DebugHelpers
22
+ attr_accessor :page
23
+
24
+ def initialize(page)
25
+ @page = page
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+ RSpec.configure do |config|
3
+ config.disable_monkey_patching!
4
+ config.order = "random"
5
+ end
metadata ADDED
@@ -0,0 +1,210 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: barsoom_utils
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Tomas Skogberg
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-11-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: httparty
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: lolcat
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: attr_extras
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: redis
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: honeybadger
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: fixme
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rubocop
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ description:
154
+ email:
155
+ - tomas.skogberg@gmail.com
156
+ executables: []
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - ".gitignore"
161
+ - ".rspec"
162
+ - ".rubocop.yml"
163
+ - Gemfile
164
+ - README.md
165
+ - Rakefile
166
+ - barsoom_utils.gemspec
167
+ - lib/barsoom_utils.rb
168
+ - lib/barsoom_utils/exception_notifier.rb
169
+ - lib/barsoom_utils/feature_toggle.rb
170
+ - lib/barsoom_utils/ping_health_check.rb
171
+ - lib/barsoom_utils/spec/debug_helpers.rb
172
+ - lib/barsoom_utils/version.rb
173
+ - script/test
174
+ - shared_rubocop.yml
175
+ - shared_rubocop_rails.yml
176
+ - spec/exception_notifier_spec.rb
177
+ - spec/feature_toggle_spec.rb
178
+ - spec/ping_health_check_spec.rb
179
+ - spec/spec/debug_helpers_spec.rb
180
+ - spec/spec_helper.rb
181
+ homepage: http://barsoom.se
182
+ licenses:
183
+ - MIT
184
+ metadata:
185
+ rubygems_mfa_required: 'true'
186
+ post_install_message:
187
+ rdoc_options: []
188
+ require_paths:
189
+ - lib
190
+ required_ruby_version: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ required_rubygems_version: !ruby/object:Gem::Requirement
196
+ requirements:
197
+ - - ">="
198
+ - !ruby/object:Gem::Version
199
+ version: '0'
200
+ requirements: []
201
+ rubygems_version: 3.2.31
202
+ signing_key:
203
+ specification_version: 4
204
+ summary: Various helpful utils
205
+ test_files:
206
+ - spec/exception_notifier_spec.rb
207
+ - spec/feature_toggle_spec.rb
208
+ - spec/ping_health_check_spec.rb
209
+ - spec/spec/debug_helpers_spec.rb
210
+ - spec/spec_helper.rb