diff --git a/lib/rollbar.rb b/lib/rollbar.rb index da9d0e17..bf6157d6 100644 --- a/lib/rollbar.rb +++ b/lib/rollbar.rb @@ -10,6 +10,7 @@ end require 'rollbar/version' +require 'rollbar/plugins' require 'rollbar/json' require 'rollbar/js' require 'rollbar/configuration' @@ -17,7 +18,6 @@ require 'rollbar/logger_proxy' require 'rollbar/exception_reporter' require 'rollbar/util' -require 'rollbar/railtie' if defined?(Rails::VERSION) && Rails::VERSION::MAJOR >= 3 require 'rollbar/delay/girl_friday' if defined?(GirlFriday) require 'rollbar/delay/thread' require 'rollbar/truncation' @@ -812,6 +812,8 @@ class << self def_delegators :notifier, *PUBLIC_NOTIFIER_METHODS + attr_writer :plugins + # Similar to configure below, but used only internally within the gem # to configure it without initializing any of the third party hooks def preconfigure @@ -826,7 +828,7 @@ def configure yield(configuration) - prepare + plugins.load! reset_notifier! end @@ -854,52 +856,8 @@ def safely? configuration.safely? end - def prepare - prepare_js - require_hooks - require_core_extensions - end - - def prepare_js - ::Rollbar::Js.prepare if configuration.js_enabled - end - - def require_hooks - return if configuration.disable_monkey_patch - wrap_delayed_worker - - if defined?(ActiveRecord) - require 'active_record/version' - require 'rollbar/active_record_extension' if ActiveRecord::VERSION::MAJOR >= 3 - end - - require 'rollbar/sidekiq' if defined?(Sidekiq) - require 'rollbar/active_job' if defined?(ActiveJob) - require 'rollbar/goalie' if defined?(Goalie) - require 'rollbar/rack' if defined?(Rack) unless configuration.disable_rack_monkey_patch - require 'rollbar/rake' if defined?(Rake) - end - - def require_core_extensions - # This monkey patch is always needed in order - # to use Rollbar.scoped - require 'rollbar/core_ext/thread' - - return if configuration.disable_core_monkey_patch - - # Needed to avoid active_support (< 4.1.0) bug serializing JSONs - require 'rollbar/core_ext/basic_socket' if monkey_patch_socket? - end - - def monkey_patch_socket? - defined?(ActiveSupport::VERSION::STRING) - end - - def wrap_delayed_worker - return unless defined?(Delayed) && defined?(Delayed::Worker) && configuration.delayed_job_enabled - - require 'rollbar/delayed_job' - Rollbar::Delayed.wrap_worker + def plugins + @plugins ||= Rollbar::Plugins.new end def notifier @@ -984,3 +942,5 @@ def report_message_with_request(message, level = 'info', request_data = nil, per end end end + +Rollbar.plugins.require_all diff --git a/lib/rollbar/active_record_extension.rb b/lib/rollbar/active_record_extension.rb deleted file mode 100644 index 60cf2cef..00000000 --- a/lib/rollbar/active_record_extension.rb +++ /dev/null @@ -1,14 +0,0 @@ -module Rollbar - module ActiveRecordExtension - extend ActiveSupport::Concern - - def report_validation_errors_to_rollbar - self.errors.full_messages.each do |error| - Rollbar.log_info "[Rollbar] Reporting form validation error: #{error} for #{self.to_s}" - Rollbar.warning("Form Validation Error: #{error} for #{self.to_s}") - end - end - end -end - -ActiveRecord::Base.send(:include, Rollbar::ActiveRecordExtension) diff --git a/lib/rollbar/core_ext/basic_socket.rb b/lib/rollbar/core_ext/basic_socket.rb deleted file mode 100644 index 372a63bd..00000000 --- a/lib/rollbar/core_ext/basic_socket.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'socket' - -class BasicSocket - def as_json - to_s - end -end diff --git a/lib/rollbar/core_ext/thread.rb b/lib/rollbar/core_ext/thread.rb deleted file mode 100644 index 73b52103..00000000 --- a/lib/rollbar/core_ext/thread.rb +++ /dev/null @@ -1,9 +0,0 @@ -class Thread - def initialize_with_rollbar(*args, &block) - self[:_rollbar_notifier] ||= Rollbar.notifier.scope - initialize_without_rollbar(*args, &block) - end - - alias_method :initialize_without_rollbar, :initialize - alias_method :initialize, :initialize_with_rollbar -end diff --git a/lib/rollbar/goalie.rb b/lib/rollbar/goalie.rb deleted file mode 100644 index b87c9450..00000000 --- a/lib/rollbar/goalie.rb +++ /dev/null @@ -1,33 +0,0 @@ -module Goalie - class CustomErrorPages - alias_method :orig_render_exception, :render_exception - - private - - def render_exception(env, exception) - exception_data = nil - begin - controller = env['action_controller.instance'] - request_data = controller.rollbar_request_data rescue nil - person_data = controller.rollbar_person_data rescue nil - exception_data = Rollbar.scope(:request => request_data, :person => person_data).error(exception, :use_exception_level_filters => true) - rescue => e - Rollbar.log_warning "[Rollbar] Exception while reporting exception to Rollbar: #{e}" - end - - # if an exception was reported, save uuid in the env - # so it can be displayed to the user on the error page - if exception_data.is_a?(Hash) - env['rollbar.exception_uuid'] = exception_data[:uuid] - Rollbar.log_info "[Rollbar] Exception uuid saved in env: #{exception_data[:uuid]}" - elsif exception_data == 'disabled' - Rollbar.log_info "[Rollbar] Exception not reported because Rollbar is disabled" - elsif exception_data == 'ignored' - Rollbar.log_info "[Rollbar] Exception not reported because it was ignored" - end - - # now continue as normal - orig_render_exception(env, exception) - end - end -end diff --git a/lib/rollbar/js.rb b/lib/rollbar/js.rb index 3da532a7..086145c5 100644 --- a/lib/rollbar/js.rb +++ b/lib/rollbar/js.rb @@ -1,32 +1,4 @@ -require "rollbar/js/version" - module Rollbar module Js - extend self - - attr_reader :framework - attr_reader :framework_loader - - def prepare - @framework ||= detect_framework - @framework_loader ||= load_framework_class.new - - @framework_loader.prepare - end - - private - - def detect_framework - case - when defined?(::Rails::VERSION) - :rails - end - end - - def load_framework_class - require "rollbar/js/frameworks/#{framework}" - - Rollbar::Js::Frameworks.const_get(framework.to_s.capitalize) - end end end diff --git a/lib/rollbar/js/frameworks.rb b/lib/rollbar/js/frameworks.rb deleted file mode 100644 index edcc2fb5..00000000 --- a/lib/rollbar/js/frameworks.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Rollbar - module Js - module Frameworks - end - end -end diff --git a/lib/rollbar/js/frameworks/rails.rb b/lib/rollbar/js/frameworks/rails.rb deleted file mode 100644 index 541329db..00000000 --- a/lib/rollbar/js/frameworks/rails.rb +++ /dev/null @@ -1,49 +0,0 @@ -module Rollbar - module Js - module Frameworks - class Rails - attr_accessor :prepared - - alias prepared? prepared - - def prepare - return if prepared? - - if secure_headers? - insert_middleware_after_secure_headers - else - insert_middleware - end - - self.prepared = true - end - - def insert_middleware_after_secure_headers - instance = self - - Rollbar::Railtie.initializer 'rollbar.js.frameworks.rails', :after => 'secure_headers.middleware' do |_app| - instance.insert_middleware - end - end - - def insert_middleware - require 'rollbar/js/middleware' - - config = { - :options => Rollbar.configuration.js_options, - :enabled => Rollbar.configuration.js_enabled - } - rails_config.middleware.use(::Rollbar::Js::Middleware, config) - end - - def secure_headers? - defined?(::SecureHeaders) - end - - def rails_config - ::Rails.configuration - end - end - end - end -end diff --git a/lib/rollbar/js/version.rb b/lib/rollbar/js/version.rb deleted file mode 100644 index 2a37bc44..00000000 --- a/lib/rollbar/js/version.rb +++ /dev/null @@ -1,5 +0,0 @@ -module Rollbar - module Js - VERSION = "0.1.0" - end -end diff --git a/lib/rollbar/js/middleware.rb b/lib/rollbar/middleware/js.rb similarity index 99% rename from lib/rollbar/js/middleware.rb rename to lib/rollbar/middleware/js.rb index fbbedc9b..58768973 100644 --- a/lib/rollbar/js/middleware.rb +++ b/lib/rollbar/middleware/js.rb @@ -1,10 +1,9 @@ require 'rack' require 'rack/response' - module Rollbar - module Js - class Middleware + module Middleware + class Js attr_reader :app attr_reader :config diff --git a/lib/rollbar/plugin.rb b/lib/rollbar/plugin.rb new file mode 100644 index 00000000..dd319d52 --- /dev/null +++ b/lib/rollbar/plugin.rb @@ -0,0 +1,63 @@ +module Rollbar + # Represents a plugin in the gem. Every plugin can have multiple dependencies + # and multiple execution blocks. + # On Rollbar initialization, all plugins will be saved in memory and those that + # satisfy the dependencies will be loaded + class Plugin + attr_reader :name + attr_reader :dependencies + attr_reader :callables + attr_accessor :loaded + + private :loaded= + + def initialize(name) + @name = name + @dependencies = [] + @callables = [] + @loaded = false + end + + def configuration + Rollbar.configuration + end + + def load! + return unless load? + + begin + callables.each(&:call) + rescue => e + log_loading_error(e) + ensure + self.loaded = true + end + end + + private + + def dependency(&block) + dependencies << block + end + + def execute(&block) + callables << block + end + + def execute!(&block) + block.call if load? + end + + def load? + !loaded && dependencies.all?(&:call) + rescue => e + log_loading_error(e) + + false + end + + def log_loading_error(e) + Rollbar.log_error("Error trying to load plugin '#{name}': #{e.class}, #{e.message}") + end + end +end diff --git a/lib/rollbar/plugins.rb b/lib/rollbar/plugins.rb new file mode 100644 index 00000000..215c1362 --- /dev/null +++ b/lib/rollbar/plugins.rb @@ -0,0 +1,41 @@ +require 'rollbar/plugin' + +module Rollbar + # Stores the available plugin definitions and loads them + class Plugins + attr_reader :collection + + def initialize + @collection = [] + end + + def require_all + Dir.glob(plugin_files).each do |file| + require file.to_s + end + end + + def plugin_files + File.expand_path('../plugins/*.rb', __FILE__) + end + + def define(name, &block) + return if loaded?(name) + + plugin = Rollbar::Plugin.new(name) + collection << plugin + + plugin.instance_eval(&block) + end + + def load! + collection.each(&:load!) + end + + private + + def loaded?(name) + collection.any? { |plugin| plugin.name == name } + end + end +end diff --git a/lib/rollbar/active_job.rb b/lib/rollbar/plugins/active_job.rb similarity index 100% rename from lib/rollbar/active_job.rb rename to lib/rollbar/plugins/active_job.rb diff --git a/lib/rollbar/plugins/active_record.rb b/lib/rollbar/plugins/active_record.rb new file mode 100644 index 00000000..29e59bd4 --- /dev/null +++ b/lib/rollbar/plugins/active_record.rb @@ -0,0 +1,30 @@ +Rollbar.plugins.define('active_record') do + dependency { !configuration.disable_monkey_patch } + dependency { defined?(ActiveRecord) } + dependency do + require 'active_record/version' + + ActiveRecord::VERSION::MAJOR >= 3 + end + + execute do + module Rollbar + module ActiveRecordExtension + extend ActiveSupport::Concern + + def report_validation_errors_to_rollbar + errors.full_messages.each do |error| + Rollbar.log_info "[Rollbar] Reporting form validation error: #{error} for #{self}" + Rollbar.warning("Form Validation Error: #{error} for #{self}") + end + end + end + end + end + + execute do + ActiveRecord::Base.class_eval do + include Rollbar::ActiveRecordExtension + end + end +end diff --git a/lib/rollbar/plugins/basic_socket.rb b/lib/rollbar/plugins/basic_socket.rb new file mode 100644 index 00000000..a160135f --- /dev/null +++ b/lib/rollbar/plugins/basic_socket.rb @@ -0,0 +1,16 @@ +Rollbar.plugins.define('basic_socket') do + dependency { !configuration.disable_core_monkey_patch } + + # Needed to avoid active_support (< 4.1.0) bug serializing JSONs + dependency { defined?(ActiveSupport::VERSION::STRING) } + + execute do + require 'socket' + + class BasicSocket + def as_json + to_s + end + end + end +end diff --git a/lib/rollbar/plugins/delayed_job.rb b/lib/rollbar/plugins/delayed_job.rb new file mode 100644 index 00000000..5287aa24 --- /dev/null +++ b/lib/rollbar/plugins/delayed_job.rb @@ -0,0 +1,12 @@ +Rollbar.plugins.define('delayed_job') do + dependency { !configuration.disable_monkey_patch } + dependency do + defined?(Delayed) && defined?(Delayed::Worker) && configuration.delayed_job_enabled + end + + execute do + require 'rollbar/plugins/delayed_job/plugin' + + Rollbar::Delayed.wrap_worker + end +end diff --git a/lib/rollbar/plugins/delayed_job/job_data.rb b/lib/rollbar/plugins/delayed_job/job_data.rb new file mode 100644 index 00000000..9298cd72 --- /dev/null +++ b/lib/rollbar/plugins/delayed_job/job_data.rb @@ -0,0 +1,16 @@ +class JobData + attr_reader :job + + def initialize(job) + @job = job + end + + def to_hash + job_data = job.as_json + # Here job_data['handler'] is a YAML object comming + # from the storage backend + job_data['handler'] = job.payload_object.as_json + + job_data + end +end diff --git a/lib/rollbar/delayed_job.rb b/lib/rollbar/plugins/delayed_job/plugin.rb similarity index 77% rename from lib/rollbar/delayed_job.rb rename to lib/rollbar/plugins/delayed_job/plugin.rb index 8c62164b..0d59efa2 100644 --- a/lib/rollbar/delayed_job.rb +++ b/lib/rollbar/plugins/delayed_job/plugin.rb @@ -1,4 +1,5 @@ require 'delayed_job' +require 'rollbar/plugins/delayed_job/job_data' module Rollbar module Delayed @@ -6,23 +7,6 @@ class << self attr_accessor :wrapped end - class JobData - attr_reader :job - - def initialize(job) - @job = job - end - - def to_hash - job_data = job.as_json - # Here job_data['handler'] is a YAML object comming - # from the storage backend - job_data['handler'] = job.payload_object.as_json - - job_data - end - end - class RollbarPlugin < ::Delayed::Plugin callbacks do |lifecycle| lifecycle.around(:invoke_job, &Delayed::invoke_job_callback) diff --git a/lib/rollbar/plugins/goalie.rb b/lib/rollbar/plugins/goalie.rb new file mode 100644 index 00000000..9b2e9961 --- /dev/null +++ b/lib/rollbar/plugins/goalie.rb @@ -0,0 +1,46 @@ +Rollbar.plugins.define('goalie') do + dependency { !configuration.disable_monkey_patch } + dependency { defined?(Goalie) } + + execute do + module Rollbar + module Goalie + def render_exception_with_rollbar(env, exception) + exception_data = nil + + begin + controller = env['action_controller.instance'] + request_data = controller.rollbar_request_data rescue nil + person_data = controller.rollbar_person_data rescue nil + exception_data = Rollbar.scope(:request => request_data, :person => person_data).error(exception, :use_exception_level_filters => true) + rescue => e + Rollbar.log_warning "[Rollbar] Exception while reporting exception to Rollbar: #{e}" + end + + # if an exception was reported, save uuid in the env + # so it can be displayed to the user on the error page + if exception_data.is_a?(Hash) + env['rollbar.exception_uuid'] = exception_data[:uuid] + Rollbar.log_info "[Rollbar] Exception uuid saved in env: #{exception_data[:uuid]}" + elsif exception_data == 'disabled' + Rollbar.log_info "[Rollbar] Exception not reported because Rollbar is disabled" + elsif exception_data == 'ignored' + Rollbar.log_info "[Rollbar] Exception not reported because it was ignored" + end + + # now continue as normal + render_exception_without_rollbar(env, exception) + end + end + end + end + + execute do + Goalie::CustomErrorPages.class_eval do + include Rollbar::Goalie + + alias_method :render_exception_without_rollbar, :render_exception + alias_method :render_exception, :render_exception_with_rollbar + end + end +end diff --git a/lib/rollbar/plugins/rack.rb b/lib/rollbar/plugins/rack.rb new file mode 100644 index 00000000..3e35328e --- /dev/null +++ b/lib/rollbar/plugins/rack.rb @@ -0,0 +1,16 @@ +Rollbar.plugins.define('rack') do + dependency { !configuration.disable_monkey_patch } + dependency { !configuration.disable_rack_monkey_patch } + + execute do + if defined?(Rack::Builder) + require 'rollbar/middleware/rack/builder' + Rack::Builder.send(:include, Rollbar::Middleware::Rack::Builder) + end + + if defined?(Rack::Test::Session) + require 'rollbar/middleware/rack/test_session' + Rack::Test::Session.send(:include, Rollbar::Middleware::Rack::TestSession) + end + end +end diff --git a/lib/rollbar/plugins/rails.rb b/lib/rollbar/plugins/rails.rb new file mode 100644 index 00000000..a28488f5 --- /dev/null +++ b/lib/rollbar/plugins/rails.rb @@ -0,0 +1,69 @@ +Rollbar.plugins.define('rails32-errors') do + dependency { defined?(Rails::VERSION) && Rails::VERSION::MAJOR >= 3 } + dependency { Gem::Version.new(Rails::VERSION::STRING) >= Gem::Version.new('3.2') } + + execute! do + require 'rollbar/plugins/rails/railtie32' + end +end + +Rollbar.plugins.define('rails30-errors') do + dependency { defined?(Rails::VERSION) && Rails::VERSION::MAJOR >= 3 } + dependency { Gem::Version.new(Rails::VERSION::STRING) < Gem::Version.new('3.2') } + + execute! do + require 'rollbar/plugins/rails/railtie30' + end +end + +Rollbar.plugins.define('rails-rollbar.js') do + dependency { configuration.js_enabled } + + execute do + module Rollbar + module Js + module Frameworks + class Rails + def load + if secure_headers? + insert_middleware_after_secure_headers + else + insert_middleware + end + end + + def insert_middleware_after_secure_headers + instance = self + + Rollbar::Railtie.initializer 'rollbar.js.frameworks.rails', :after => 'secure_headers.middleware' do |_app| + instance.insert_middleware + end + end + + def insert_middleware + require 'rollbar/middleware/js' + + config = { + :options => Rollbar.configuration.js_options, + :enabled => Rollbar.configuration.js_enabled + } + rails_config.middleware.use(::Rollbar::Middleware::Js, config) + end + + def secure_headers? + defined?(::SecureHeaders) + end + + def rails_config + ::Rails.configuration + end + end + end + end + end + end + + execute do + Rollbar::Js::Frameworks::Rails.new.load + end +end diff --git a/lib/rollbar/rails/controller_methods.rb b/lib/rollbar/plugins/rails/controller_methods.rb similarity index 100% rename from lib/rollbar/rails/controller_methods.rb rename to lib/rollbar/plugins/rails/controller_methods.rb diff --git a/lib/rollbar/plugins/rails/railtie30.rb b/lib/rollbar/plugins/rails/railtie30.rb new file mode 100644 index 00000000..84de93f7 --- /dev/null +++ b/lib/rollbar/plugins/rails/railtie30.rb @@ -0,0 +1,17 @@ +require 'rails/railtie' +require 'rollbar/plugins/rails/railtie_mixin' + +module Rollbar + class Railtie < ::Rails::Railtie + include Rollbar::RailtieMixin + + initializer 'rollbar.middleware.rails' do |app| + require 'rollbar/middleware/rails/rollbar' + require 'rollbar/middleware/rails/show_exceptions' + + app.config.middleware.insert_after ActionDispatch::ShowExceptions, + Rollbar::Middleware::Rails::RollbarMiddleware + ActionDispatch::ShowExceptions.send(:include, Rollbar::Middleware::Rails::ShowExceptions) + end + end +end diff --git a/lib/rollbar/plugins/rails/railtie32.rb b/lib/rollbar/plugins/rails/railtie32.rb new file mode 100644 index 00000000..3266ea5a --- /dev/null +++ b/lib/rollbar/plugins/rails/railtie32.rb @@ -0,0 +1,18 @@ +require 'rails/railtie' +require 'rollbar/plugins/rails/railtie_mixin' + +module Rollbar + class Railtie < ::Rails::Railtie + include Rollbar::RailtieMixin + + initializer 'rollbar.middleware.rails' do |app| + require 'rollbar/middleware/rails/rollbar' + require 'rollbar/middleware/rails/show_exceptions' + + app.config.middleware.insert_after ActionDispatch::DebugExceptions, + Rollbar::Middleware::Rails::RollbarMiddleware + ActionDispatch::DebugExceptions.send(:include, Rollbar::Middleware::Rails::ShowExceptions) + end + end +end + diff --git a/lib/rollbar/plugins/rails/railtie_mixin.rb b/lib/rollbar/plugins/rails/railtie_mixin.rb new file mode 100644 index 00000000..646b9149 --- /dev/null +++ b/lib/rollbar/plugins/rails/railtie_mixin.rb @@ -0,0 +1,33 @@ +require 'rollbar' + +module Rollbar + module RailtieMixin + extend ActiveSupport::Concern + + included do + rake_tasks do + require 'rollbar/rake_tasks' + end + + initializer 'rollbar.configuration' do + config.after_initialize do + Rollbar.preconfigure do |config| + config.default_logger = proc { ::Rails.logger } + config.environment ||= ::Rails.env + config.root ||= ::Rails.root + config.framework = "Rails: #{::Rails::VERSION::STRING}" + config.filepath ||= ::Rails.application.class.parent_name + '.rollbar' + end + end + end + + initializer 'rollbar.controller_methods' do + ActiveSupport.on_load(:action_controller) do + # lazily load action_controller methods + require 'rollbar/plugins/rails/controller_methods' + include Rollbar::Rails::ControllerMethods + end + end + end + end +end diff --git a/lib/rollbar/plugins/rake.rb b/lib/rollbar/plugins/rake.rb new file mode 100644 index 00000000..208d1d67 --- /dev/null +++ b/lib/rollbar/plugins/rake.rb @@ -0,0 +1,45 @@ +Rollbar.plugins.define('rake') do + dependency { !configuration.disable_monkey_patch } + dependency { defined?(Rake) } + + module Rollbar + module Rake + def self.patch! + skip_patch && return unless patch? + + ::Rake::Application.class_eval do + alias_method :orig_display_error_message, :display_error_message + + def display_error_message(ex) + Rollbar.error(ex, :use_exception_level_filters => true) + orig_display_error_message(ex) + end + end + end + + def self.skip_patch + warn('[Rollbar] Rollbar is disabled for Rake tasks since your Rake version is under 0.9.x. Please upgrade to 0.9.x or higher.') + end + + def self.patch? + return false unless rake_version + + major, minor, = rake_version.split('.').map(&:to_i) + + major > 0 || major == 0 && minor > 8 + end + + def self.rake_version + if Object.const_defined?('RAKEVERSION') + return RAKEVERSION + elsif ::Rake.const_defined?('VERSION') + return ::Rake::VERSION + end + end + end + end + + execute do + Rollbar::Rake.patch! + end +end diff --git a/lib/rollbar/plugins/sidekiq.rb b/lib/rollbar/plugins/sidekiq.rb new file mode 100644 index 00000000..9bfdf781 --- /dev/null +++ b/lib/rollbar/plugins/sidekiq.rb @@ -0,0 +1,35 @@ +Rollbar.plugins.define('sidekiq >= 3') do + dependency { !configuration.disable_monkey_patch } + dependency { defined?(Sidekiq) } + dependency { Sidekiq::VERSION.split('.')[0].to_i >= 3 } + + execute do + require 'rollbar/plugins/sidekiq/plugin' + + Sidekiq.configure_server do |config| + config.server_middleware do |chain| + chain.add Rollbar::Sidekiq::ClearScope + end + + config.error_handlers << proc do |e, context| + Rollbar::Sidekiq.handle_exception(context, e) + end + end + end +end + +Rollbar.plugins.define('sidekiq < 3') do + dependency { !configuration.disable_monkey_patch } + dependency { defined?(Sidekiq) } + dependency { Sidekiq::VERSION.split('.')[0].to_i < 3 } + + execute do + require 'rollbar/plugins/sidekiq/plugin' + + Sidekiq.configure_server do |config| + config.server_middleware do |chain| + chain.add Rollbar::Sidekiq + end + end + end +end diff --git a/lib/rollbar/sidekiq.rb b/lib/rollbar/plugins/sidekiq/plugin.rb similarity index 73% rename from lib/rollbar/sidekiq.rb rename to lib/rollbar/plugins/sidekiq/plugin.rb index a7191422..9eda50ed 100644 --- a/lib/rollbar/sidekiq.rb +++ b/lib/rollbar/plugins/sidekiq/plugin.rb @@ -1,5 +1,3 @@ -# encoding: utf-8 - module Rollbar class Sidekiq PARAM_BLACKLIST = %w[backtrace error_backtrace error_message error_class] @@ -43,19 +41,3 @@ def call(worker, msg, queue) end end end - -Sidekiq.configure_server do |config| - if Sidekiq::VERSION.split('.')[0].to_i < 3 - config.server_middleware do |chain| - chain.add Rollbar::Sidekiq - end - else - config.server_middleware do |chain| - chain.add Rollbar::Sidekiq::ClearScope - end - - config.error_handlers << proc do |e, context| - Rollbar::Sidekiq.handle_exception(context, e) - end - end -end diff --git a/lib/rollbar/plugins/thread.rb b/lib/rollbar/plugins/thread.rb new file mode 100644 index 00000000..a0ff68b8 --- /dev/null +++ b/lib/rollbar/plugins/thread.rb @@ -0,0 +1,13 @@ +Rollbar.plugins.define('thread') do + execute do + Thread.class_eval do + def initialize_with_rollbar(*args, &block) + self[:_rollbar_notifier] ||= Rollbar.notifier.scope + initialize_without_rollbar(*args, &block) + end + + alias_method :initialize_without_rollbar, :initialize + alias_method :initialize, :initialize_with_rollbar + end + end +end diff --git a/lib/rollbar/rack.rb b/lib/rollbar/rack.rb deleted file mode 100644 index fbd345f5..00000000 --- a/lib/rollbar/rack.rb +++ /dev/null @@ -1,9 +0,0 @@ -if defined?(Rack::Builder) - require 'rollbar/middleware/rack/builder' - Rack::Builder.send(:include, Rollbar::Middleware::Rack::Builder) -end - -if defined?(Rack::Test::Session) - require 'rollbar/middleware/rack/test_session' - Rack::Test::Session.send(:include, Rollbar::Middleware::Rack::TestSession) -end diff --git a/lib/rollbar/railtie.rb b/lib/rollbar/railtie.rb deleted file mode 100644 index 1f1fd4dc..00000000 --- a/lib/rollbar/railtie.rb +++ /dev/null @@ -1,46 +0,0 @@ -require 'rails/railtie' -require 'rollbar' - -module Rollbar - class Railtie < ::Rails::Railtie - rake_tasks do - require 'rollbar/rake_tasks' - end - - initializer 'rollbar.middleware.rails' do |app| - if defined?(ActionDispatch::DebugExceptions) - # Rails 3.2.x+ - require 'rollbar/middleware/rails/rollbar' - require 'rollbar/middleware/rails/show_exceptions' - - app.config.middleware.insert_after ActionDispatch::DebugExceptions, - Rollbar::Middleware::Rails::RollbarMiddleware - ActionDispatch::DebugExceptions.send(:include, Rollbar::Middleware::Rails::ShowExceptions) - elsif defined?(ActionDispatch::ShowExceptions) - # Rails 3.0.x and 3.1.x - require 'rollbar/middleware/rails/rollbar' - require 'rollbar/middleware/rails/show_exceptions' - - app.config.middleware.insert_after ActionDispatch::ShowExceptions, - Rollbar::Middleware::Rails::RollbarMiddleware - ActionDispatch::ShowExceptions.send(:include, Rollbar::Middleware::Rails::ShowExceptions) - end - end - - config.after_initialize do - Rollbar.preconfigure do |config| - config.default_logger = proc { ::Rails.logger } - config.environment ||= ::Rails.env - config.root ||= ::Rails.root - config.framework = "Rails: #{::Rails::VERSION::STRING}" - config.filepath ||= ::Rails.application.class.parent_name + '.rollbar' - end - - ActiveSupport.on_load(:action_controller) do - # lazily load action_controller methods - require 'rollbar/rails/controller_methods' - include Rollbar::Rails::ControllerMethods - end - end - end -end diff --git a/lib/rollbar/rake.rb b/lib/rollbar/rake.rb deleted file mode 100644 index ff37523b..00000000 --- a/lib/rollbar/rake.rb +++ /dev/null @@ -1,40 +0,0 @@ -require 'rake' - -module Rollbar - module Rake - def self.patch! - skip_patch && return unless patch? - - ::Rake::Application.class_eval do - alias_method :orig_display_error_message, :display_error_message - - def display_error_message(ex) - Rollbar.error(ex, :use_exception_level_filters => true) - orig_display_error_message(ex) - end - end - end - - def self.skip_patch - warn('[Rollbar] Rollbar is disabled for Rake tasks since your Rake version is under 0.9.x. Please upgrade to 0.9.x or higher.') - end - - def self.patch? - return false unless rake_version - - major, minor, = rake_version.split('.').map(&:to_i) - - major > 0 || major == 0 && minor > 8 - end - - def self.rake_version - if Object.const_defined?('RAKEVERSION') - return RAKEVERSION - elsif ::Rake.const_defined?('VERSION') - return ::Rake::VERSION - end - end - end -end - -Rollbar::Rake.patch! diff --git a/spec/fixtures/plugins/dummy1.rb b/spec/fixtures/plugins/dummy1.rb new file mode 100644 index 00000000..52590aa8 --- /dev/null +++ b/spec/fixtures/plugins/dummy1.rb @@ -0,0 +1,5 @@ +require 'rollbar/plugins' + +Rollbar.plugins.define(:dummy1) do + dependency { true } +end diff --git a/spec/fixtures/plugins/dummy2.rb b/spec/fixtures/plugins/dummy2.rb new file mode 100644 index 00000000..61035c27 --- /dev/null +++ b/spec/fixtures/plugins/dummy2.rb @@ -0,0 +1,5 @@ +require 'rollbar/plugins' + +Rollbar.plugins.define(:dummy2) do + dependency { true } +end diff --git a/spec/rollbar/js/middleware_spec.rb b/spec/rollbar/middleware/js_spec.rb similarity index 98% rename from spec/rollbar/js/middleware_spec.rb rename to spec/rollbar/middleware/js_spec.rb index 1232ce6f..801a329c 100644 --- a/spec/rollbar/js/middleware_spec.rb +++ b/spec/rollbar/middleware/js_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' -require 'rollbar/js/middleware' +require 'rollbar/middleware/js' -describe Rollbar::Js::Middleware do +describe Rollbar::Middleware::Js do subject { described_class.new(app, config) } let(:env) { {} } diff --git a/spec/rollbar/plugin_spec.rb b/spec/rollbar/plugin_spec.rb new file mode 100644 index 00000000..3e829a3c --- /dev/null +++ b/spec/rollbar/plugin_spec.rb @@ -0,0 +1,147 @@ +require 'spec_helper' +require 'rollbar/plugin' + +describe Rollbar::Plugin do + describe '#load!' do + subject { described_class.new(:plugin) } + + before { subject.instance_eval(&plugin_proc) } + + context 'with true dependencies' do + let(:dummy_object) { '' } + let(:plugin_proc) do + dummy = dummy_object + + proc do + dependency do + true + end + + dependency do + 1 == 1.0 + end + + execute do + dummy.upcase + end + + execute do + dummy.downcase + end + end + end + + it 'calls the callables' do + expect(dummy_object).to receive(:upcase).once + expect(dummy_object).to receive(:downcase).once + + subject.load! + + expect(subject.loaded).to be_eql(true) + end + end + + context 'with dependencies failing' do + let(:dummy_object) { '' } + let(:plugin_proc) do + dummy = dummy_object + + proc do + dependency do + true + end + + dependency do + raise StandardError.new('the-error') + end + + execute do + dummy.upcase + end + + execute do + dummy.downcase + end + end + end + + it 'doesnt finish loading the plugin' do + expect(dummy_object).not_to receive(:upcase) + expect(dummy_object).not_to receive(:downcase) + expect(Rollbar).to receive(:log_error).with("Error trying to load plugin 'plugin': StandardError, the-error") + + subject.load! + + expect(subject.loaded).to be_eql(false) + end + end + + context 'with callables failing' do + let(:dummy_object) { '' } + let(:plugin_proc) do + dummy = dummy_object + + proc do + dependency do + true + end + + dependency do + 1 == 1.0 + end + + execute do + raise StandardError.new('the-error') + end + + execute do + dummy.downcase + end + end + end + + it 'doesnt finish loading the plugin' do + expect(dummy_object).not_to receive(:downcase) + expect(Rollbar).to receive(:log_error).with("Error trying to load plugin 'plugin': StandardError, the-error") + + subject.load! + + expect(subject.loaded).to be_eql(true) + end + end + + context 'with false dependencies' do + let(:dummy_object) { '' } + let(:plugin_proc) do + dummy = dummy_object + + proc do + dependency do + true + end + + dependency do + 1 == 2.0 + end + + execute do + dummy.upcase + end + + execute do + dummy.downcase + end + end + end + + it 'calls the callables' do + expect(dummy_object).not_to receive(:upcase) + expect(dummy_object).not_to receive(:downcase) + + subject.load! + + expect(subject.loaded).to be_eql(false) + end + end + end +end diff --git a/spec/rollbar/active_job_spec.rb b/spec/rollbar/plugins/active_job_spec.rb similarity index 97% rename from spec/rollbar/active_job_spec.rb rename to spec/rollbar/plugins/active_job_spec.rb index 4b33ad6a..ac23b824 100644 --- a/spec/rollbar/active_job_spec.rb +++ b/spec/rollbar/plugins/active_job_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' require 'active_support/rescuable' -require 'rollbar/active_job' describe Rollbar::ActiveJob do class TestJob diff --git a/spec/rollbar/plugins/active_record_spec.rb b/spec/rollbar/plugins/active_record_spec.rb new file mode 100644 index 00000000..b599689e --- /dev/null +++ b/spec/rollbar/plugins/active_record_spec.rb @@ -0,0 +1,11 @@ +require 'spec_helper' +require 'rollbar' + +Rollbar.plugins.load! + +describe Rollbar::ActiveRecordExtension do + it 'has the extensions loaded into ActiveRecord::Base' do + expect(ActiveRecord::Base.ancestors).to include(described_class) + expect(ActiveRecord::Base.instance_methods.map(&:to_sym)).to include(:report_validation_errors_to_rollbar) + end +end diff --git a/spec/rollbar/delayed_job/job_data.rb b/spec/rollbar/plugins/delayed_job/job_data.rb similarity index 100% rename from spec/rollbar/delayed_job/job_data.rb rename to spec/rollbar/plugins/delayed_job/job_data.rb diff --git a/spec/rollbar/delayed_job_spec.rb b/spec/rollbar/plugins/delayed_job_spec.rb similarity index 93% rename from spec/rollbar/delayed_job_spec.rb rename to spec/rollbar/plugins/delayed_job_spec.rb index 6890441d..024592ee 100644 --- a/spec/rollbar/delayed_job_spec.rb +++ b/spec/rollbar/plugins/delayed_job_spec.rb @@ -1,8 +1,10 @@ require 'spec_helper' +require 'rollbar' require 'delayed_job' -require 'rollbar/delayed_job' require 'delayed/backend/test' +Rollbar.plugins.load! + describe Rollbar::Delayed, :reconfigure_notifier => true do class FailingJob class TestException < Exception; end @@ -12,11 +14,6 @@ def do_job_please!(a, b) end end - before(:all) do - # technically, this is called once when rollbar hooks are required - Rollbar::Delayed.wrap_worker! - end - before do Delayed::Backend::Test.prepare_worker diff --git a/spec/rollbar/middleware/rack/builder_spec.rb b/spec/rollbar/plugins/rack_spec.rb similarity index 98% rename from spec/rollbar/middleware/rack/builder_spec.rb rename to spec/rollbar/plugins/rack_spec.rb index 590e321d..ef8d8643 100644 --- a/spec/rollbar/middleware/rack/builder_spec.rb +++ b/spec/rollbar/plugins/rack_spec.rb @@ -2,8 +2,9 @@ require 'rack' require 'rack/builder' require 'rack/mock' -require 'rollbar/middleware/rack/builder' +require 'rollbar' +Rollbar.plugins.load! describe Rollbar::Middleware::Rack::Builder, :reconfigure_notifier => true do class RackMockError < Exception; end diff --git a/spec/rollbar/js/frameworks/rails_spec.rb b/spec/rollbar/plugins/rails_js_spec.rb similarity index 90% rename from spec/rollbar/js/frameworks/rails_spec.rb rename to spec/rollbar/plugins/rails_js_spec.rb index 32cf868b..876305fd 100644 --- a/spec/rollbar/js/frameworks/rails_spec.rb +++ b/spec/rollbar/plugins/rails_js_spec.rb @@ -11,7 +11,7 @@ it 'renders the snippet and config in the response', :type => 'request' do get '/test_rollbar_js' - snippet_from_submodule = File.read(File.expand_path('../../../../../rollbar.js/dist/rollbar.snippet.js', __FILE__)) + snippet_from_submodule = File.read(File.expand_path('../../../../rollbar.js/dist/rollbar.snippet.js', __FILE__)) expect(response.body).to include("var _rollbarConfig = #{Rollbar::configuration.js_options.to_json};") expect(response.body).to include(snippet_from_submodule) diff --git a/spec/rollbar/rake_spec.rb b/spec/rollbar/plugins/rake_spec.rb similarity index 97% rename from spec/rollbar/rake_spec.rb rename to spec/rollbar/plugins/rake_spec.rb index 85ce6235..80ebc6f2 100644 --- a/spec/rollbar/rake_spec.rb +++ b/spec/rollbar/plugins/rake_spec.rb @@ -1,5 +1,6 @@ require 'spec_helper' -require 'rollbar/rake' + +Rollbar.plugins.load! describe Rollbar::Rake do let(:application) { Rake::Application.new } diff --git a/spec/rollbar/sidekiq_spec.rb b/spec/rollbar/plugins/sidekiq_spec.rb similarity index 99% rename from spec/rollbar/sidekiq_spec.rb rename to spec/rollbar/plugins/sidekiq_spec.rb index c2d4bac2..210c25da 100644 --- a/spec/rollbar/sidekiq_spec.rb +++ b/spec/rollbar/plugins/sidekiq_spec.rb @@ -2,9 +2,10 @@ unless RUBY_VERSION == '1.8.7' require 'sidekiq' - require 'rollbar/sidekiq' end +Rollbar.plugins.load! + describe Rollbar::Sidekiq, :reconfigure_notifier => false do describe '.handle_exception' do let(:msg_or_context) { ['hello', 'error_backtrace', 'backtrace', 'goodbye'] } diff --git a/spec/rollbar/plugins_spec.rb b/spec/rollbar/plugins_spec.rb new file mode 100644 index 00000000..3532a26a --- /dev/null +++ b/spec/rollbar/plugins_spec.rb @@ -0,0 +1,68 @@ +require 'spec_helper' +require 'rollbar/plugins' +require 'rollbar/plugin' + +describe Rollbar::Plugins do + let(:plugin_files_path) do + File.expand_path('../../fixtures/plugins/**/*.rb', __FILE__) + end + let!(:current_plugins) do + Rollbar.plugins + end + + let(:plugin1_proc) do + proc do + dependency { true } + end + end + + before do + Rollbar.plugins = nil + allow_any_instance_of(described_class).to receive(:plugin_files).and_return(plugin_files_path) + end + + after do + Rollbar.plugins = current_plugins + end + + describe '#require_all' do + it 'loads the plugins' do + expect(Rollbar.plugins).to receive(:define).with(:dummy1) + expect(Rollbar.plugins).to receive(:define).with(:dummy2) + + subject.require_all + end + end + + describe '#define' do + it 'evals the plugin DSL and adds it to the collection' do + expect_any_instance_of(Rollbar::Plugin).to receive(:dependency) + expect do + subject.define(:name, &plugin1_proc) + end.to change(subject.collection, :size).by(1) + end + + context 'with a plugin already defined' do + it 'doesnt load the plugin twice' do + subject.define(:name, &plugin1_proc) + + expect_any_instance_of(Rollbar::Plugin).not_to receive(:instance_eval) + expect do + subject.define(:name, &plugin1_proc) + end.to change(subject.collection, :size).by(0) + end + end + end + + describe '#load!' do + before do + subject.define(:plugin1, &plugin1_proc) + end + + it 'calls load! in the plugins' do + expect_any_instance_of(Rollbar::Plugin).to receive(:load!).once + + subject.load! + end + end +end diff --git a/spec/rollbar/sidekig/clear_scope_spec.rb b/spec/rollbar/sidekig/clear_scope_spec.rb index cc332278..a597f574 100644 --- a/spec/rollbar/sidekig/clear_scope_spec.rb +++ b/spec/rollbar/sidekig/clear_scope_spec.rb @@ -2,9 +2,10 @@ unless RUBY_VERSION == '1.8.7' require 'sidekiq' - require 'rollbar/sidekiq' end +Rollbar.plugins.load! + describe Rollbar::Sidekiq::ClearScope, :reconfigure_notifier => false do describe '#call' do let(:middleware_block) { proc{} } diff --git a/spec/rollbar_spec.rb b/spec/rollbar_spec.rb index 637411d6..40fc9136 100644 --- a/spec/rollbar_spec.rb +++ b/spec/rollbar_spec.rb @@ -7,6 +7,11 @@ require 'active_support/core_ext/object' require 'active_support/json/encoding' +begin + require 'rollbar/delay/sidekiq' +rescue LoadError +end + begin require 'sucker_punch' require 'sucker_punch/testing/inline'