Skip to content

Commit

Permalink
Upd configuration and notifier
Browse files Browse the repository at this point in the history
  • Loading branch information
palkan committed Feb 14, 2018
1 parent 2493374 commit bbca74a
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 54 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -10,3 +10,4 @@
*.gem
gemfiles/*.lock
Gemfile.local
.rspec_status
20 changes: 10 additions & 10 deletions lib/isolator.rb
@@ -1,10 +1,9 @@
# frozen_string_literal: true

require "pry"
require "anyway"
require "uniform_notifier"

require "isolator/version"
require "isolator/configuration"
require "isolator/adapter_builder"
require "isolator/guard"
require "isolator/notifier"
Expand All @@ -23,8 +22,17 @@
require "isolator/adapters/background_jobs/active_job" if defined?(ActiveJob::Base)
require "isolator/adapters/background_jobs/sidekiq" if defined?(Sidekiq::Client)

# Isolator detects unsafe operations performed within DB transactions.
module Isolator
class << self
def config
@config ||= Configuration.new
end

def configure
yield config
end

def notify(klass:, backtrace: [])
Notifier.new(klass, backtrace).call
end
Expand All @@ -41,12 +49,4 @@ def enabled?
Thread.current[:isolator] == true
end
end

def self.configuration
@configuration ||= Configuration.new
end

def self.configure
yield(self.configuration)
end
end
31 changes: 29 additions & 2 deletions lib/isolator/configuration.rb
@@ -1,5 +1,32 @@
# frozen_string_literal: true

module Isolator
# Isolator configuration:
#
# - `raise_exceptions` - whether to raise an exception in case of offense;
# defaults to true in test env and false otherwise.
# NOTE: env is infered from RACK_ENV and RAILS_ENV.
#
# - `logger` - logger instance (nil by default)
#
# - `send_notifications` - whether to send notifications (through uniform_notifier);
# defauls to false
class Configuration
attr_accessor :raise_errors, :logger
attr_accessor :raise_exceptions, :logger, :send_notifications

def initialize
@logger = nil
@raise_exceptions = test_env?
@send_notifications = false
end

alias raise_exceptions? raise_exceptions
alias send_notifications? send_notifications

private

def test_env?
ENV["RACK_ENV"] == "test" || ENV["RAILS_ENV"] == "test"
end
end
end
end
30 changes: 21 additions & 9 deletions lib/isolator/notifier.rb
@@ -1,35 +1,47 @@
# frozen_string_literal: true

module Isolator
# Wrapper over different notifications methods (exceptions, logging, uniform notifier)
class Notifier
attr_reader :object, :backtrace

def initialize(object, backtrace = [])
def initialize(object, backtrace = caller)
@object = object
@backtrace = backtrace
end

def call
raise(exception_klass, exception_message, filtered_backtrace) if raise_exceptions?
log_exception
send_notifications if send_notifications?
raise(exception_klass, exception_message, filtered_backtrace) if raise_exceptions?
end

private

def raise_exceptions?
Isolator.config.raise_exceptions
Isolator.config.raise_exceptions?
end

def send_notifications?
Isolator.config.send_notifications
Isolator.config.send_notifications?
end

def exception_klass
@exception ||= if object.respond_to?(:isolator_exception)
object.isolator_exception
else
Isolator::UnsafeOperationError
end
@exception ||=
if object.respond_to?(:isolator_exception)
object.isolator_exception
else
Isolator::UnsafeOperationError
end
end

def log_exception
return unless Isolator.config.logger
Isolator.config.logger.warn(
"[ISOLATOR EXCEPTION]\n" \
"#{exception_message}\n" \
" ↳ #{filtered_backtrace.first}"
)
end

def send_notifications
Expand Down
52 changes: 39 additions & 13 deletions spec/isolator/notifier_spec.rb
Expand Up @@ -2,35 +2,61 @@

require "spec_helper"

RSpec.describe Isolator::Notifier do
describe Isolator::Notifier do
describe "#call" do
let(:object) { double }
let(:exception_class) { Isolator::NetworkRequestError }

subject(:notifier) { described_class.new(object) }
let(:object) do
double(isolator_exception: exception_class)
end

let(:uniform_notifier) do
double(out_of_channel_notify: nil)
end

before do
allow(UniformNotifier).to receive(:active_notifiers) { [uniform_notifier] }
end

subject { described_class.new(object).call }

context "when sending notifications without raising exception " do
before do
allow(notifier).to receive(:send_notifications?).and_return(true)
allow(notifier).to receive(:raise_exceptions?).and_return(false)
Isolator.configure do |config|
config.send_notifications = true
config.raise_exceptions = false
end
end

specify do
expect(UniformNotifier).to receive(:active_notifiers).and_return []
subject

notifier.call
expect(uniform_notifier).to have_received(
:out_of_channel_notify
).with(
exception_class.exception.message
)
end
end

context "when raising exceptions" do
let(:object) { double(isolator_exception: Isolator::NetworkRequestError) }
context "when raising exceptions and not sending notifications" do
before do
Isolator.configure do |config|
config.raise_exceptions = true
config.send_notifications = false
end
end

specify do
expect { notifier.call }.to raise_error(Isolator::NetworkRequestError)
specify(aggregate_failures: true) do
expect { subject }.to raise_error(Isolator::NetworkRequestError)
expect(uniform_notifier).to_not have_received(:out_of_channel_notify)
end

context "when object has no isolator_exception" do
specify do
expect { notifier.call }.to raise_error(Isolator::UnsafeOperationError)
let(:object) { double }

it "raises UnsafeOperationError" do
expect { subject }.to raise_error(Isolator::UnsafeOperationError)
end
end
end
Expand Down
25 changes: 5 additions & 20 deletions spec/isolator_spec.rb
@@ -1,22 +1,7 @@
require 'spec_helper'
# frozen_string_literal: true

RSpec.describe Isolator do
subject { described_class }

describe '.configure' do
before do
described_class.configure do |configuration|
configuration.raise_errors = true
configuration.logger = true
end
end
require "spec_helper"

it 'sets raise_errors option properly' do
expect(described_class.configuration.raise_errors).to eq true
end

it 'sets logger option properly' do
expect(described_class.configuration.logger).to eq true
end
end
end
describe Isolator do
subject { described_class }
end
9 changes: 9 additions & 0 deletions spec/spec_helper.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true

ENV["RAILS_ENV"] ||= "test"

require "active_record"
require "sidekiq"
require "delayed_job_active_record"
Expand All @@ -15,6 +17,8 @@

require "isolator"

ActiveJob::Base.logger = Logger.new(IO::NULL)

begin
require "pry-byebug"
rescue LoadError # rubocop:disable Lint/HandleExceptions
Expand All @@ -36,4 +40,9 @@
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end

config.after(:each) do
Isolator.remove_instance_variable(:@config) if
Isolator.instance_variable_defined?(:@config)
end
end

0 comments on commit bbca74a

Please sign in to comment.