Reputation: 267150
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
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