Blankman
Blankman

Reputation: 267150

How does this gem hook into your error handling?

I'm trying to understand how gems work, and how you can create a gem to override or add additional functionality to a rails 3 application.

This is a error catching gem that posts errors via http to a server.

To install it you simply add the gem, and then it creates an initializer with your api key.

This part I get so far, but I need help understand the execution of the gem and how it hooks into rails.

The relevant file that I found are:

https://github.com/airbrake/airbrake/blob/master/lib/airbrake/rails.rb

Where it adds methods to the ActionController class:

  if defined?(ActionController::Base)
    ActionController::Base.send(:include, Airbrake::Rails::ActionControllerCatcher)
    ActionController::Base.send(:include, Airbrake::Rails::ErrorLookup)
    ActionController::Base.send(:include, Airbrake::Rails::ControllerMethods)
    ActionController::Base.send(:include, Airbrake::Rails::JavascriptNotifier)
  end

And at the bottom of the file it executes it:

Airbrake::Rails.initialize


1. You add the gem to your GEMFILE, and call bundle.
2. You run the generator which creates the initializer file:

So when my rails application starts up, it will go through the initializer.

Is this where the gem will get instantiated and then somewhere down the line run the Airbrake::Rails.initialize method when it finishes parsing the /lib/airbrake/rails.rb file?

I can see the initializer code in the railtie.rb file:

https://github.com/airbrake/airbrake/blob/master/lib/airbrake/railtie.rb

config.after_initialize do
  Airbrake.configure(true) do |config|
    config.logger           ||= ::Rails.logger
    config.environment_name ||= ::Rails.env
    config.project_root     ||= ::Rails.root
    config.framework        = "Rails: #{::Rails::VERSION::STRING}"
  end

  if defined?(::ActionController::Base)
    require 'airbrake/rails/javascript_notifier'
    require 'airbrake/rails/controller_methods'

    ::ActionController::Base.send(:include, Airbrake::Rails::ControllerMethods)
    ::ActionController::Base.send(:include, Airbrake::Rails::JavascriptNotifier)
  end
end

This seems to make sense now since it is running this code after the initializer runs and sets up the configuration, it sends the ControllerMethods, etc to the base class.

But this seems to duplicate the code I posted earlier when things were already being sent to the ApplicationController file in the call to Airbrake::Rails.initialize.

I am hoping someone can tell me how things execute and it what order as I just don't see how this process works in a clear way.

Upvotes: 3

Views: 877

Answers (1)

Benoit Garret
Benoit Garret

Reputation: 13675

The duplicated code is here to support different versions of rails.

Rails 2.X

Rails 2.X looks for rails/init.rb to determine if a gem is a rails plugin or not. The airbrake one contains a single line:

require 'airbrake/rails'

Your summary of the next step was spot-on:

Is this where the gem will get instantiated and then somewhere down the line run the Airbrake::Rails.initialize method when it finishes parsing the /lib/airbrake/rails.rb file?

Rails 3.X

Rails 3.X plugins often inherit from Rails::Railtie, to be able to define rake tasks, plug into the boot sequence or do a lot of other interesting things.

The following line in lib/airbrake.rb checks the availability of the Railtie class (equivalent to checking if you're running rails 3) and defines Airbrake::Railtie if it's the case:

require 'airbrake/railtie' if defined?(Rails::Railtie)

When defining a Railtie, you don't have to run it manually (the equivalent of Airbrake::Rails.initialize), subclassing Rails::Railtie is enough to create your plugin.

Additional reading

If you want to understand how rails 3 achieves its modularity, the following documentation will be quite helpful:

Upvotes: 4

Related Questions