How can I extend ApplicationController in a gem?
applicationcontroller, load-order, ruby-on-rails, ruby-on-rails-3, rubygems
Solution
Here is a Gist that shows how to access the class of the subclass and store it in an instance variable and access it in the before and after filters. It uses the include method.
Problem
I thought I'd come up with a slick way to extend ApplicationController in a Rails 3.x gem. In my gem's `lib/my_namespace/my_controller.rb`, I had: ``` class MyNamespace::MyController < ApplicationController before_filter :some_method after_filter :another_method def initialize # getting classname of the subclass to use for lookup of the associated model, etc. # and storing the model_class in an instance variable # ... end # define :some_method, :another_method, etc. # ... private attr_accessor :subclass_defined_during_initialize # etc. # etc. end ``` but when the Gem is loaded, `app/controllers/application_controller.rb` is not yet loaded, so it fails: ``` /path/to/rvm/gemset/gems/activesupport-3.2.6/lib/active_support/dependencies.rb:251: in `require': cannot load such file -- my_gem_name/application_controller (LoadError) ``` As a workaround, I had defined ApplicationController in my gem's `lib/gem_namespace/application_controller.rb` as: ``` class ApplicationController < ActionController::Base end ``` I assumed that even though I had defined it there, it would be redefined in my Rails 3 application's `app/controllers/application_controller.rb`, such that both controllers in the application that extended `ApplicationController` and controllers that extended `MyNamespace::MyController` would directly or indirectly extend the ApplicationController defined in `app/controllers/application_controller.rb`. However, we noticed that after loading the gem, controllers that extend `ApplicationController` were unable to access methods defined in `app/controllers/application_controller.rb`. Also, the `ApplicationHelper` `(app/helpers/application_helper.rb)` module was no longer being loaded by other helper modules. How can I extend `ApplicationController` within the controller in my gem for the purpose of defining a `before_filter` and `after_filter` to and use `initialize` to access the class's name to determine the associated model's class that it could then store and use within its methods? Update 2012/10/22: Here's what I came up with: In `lib/your_gem_name/railtie.rb`: ``` module YourGemsModuleName class Railtie < Rails::Railtie initializer "your_gem_name.action_controller" do ActiveSupport.on_load(:action_controller) do puts "Extending #{self} with YourGemsModuleName::Controller" # ActionController::Base gets a method that allows controllers to include the new behavior include YourGemsModuleName::Controller # ActiveSupport::Concern end end end ``` and in `lib/your_gem_name/controller.rb`: ``` module YourGemsModuleName module Controller extend ActiveSupport::Concern # note: don't specify included or ClassMethods if unused included do # anything you would want to do in every controller, for example: add a class attribute class_attribute :class_attribute_available_on_every_controller, instance_writer: false end module ClassMethods # notice: no self.method_name here, because this is being extended because ActiveSupport::Concern was extended def make_this_controller_fantastic before_filter :some_instance_method_available_on_every_controller # to be available on every controller after_filter :another_instance_method_available_on_every_controller # to be available on every controller include FantasticStuff end end # instance methods to go on every controller go here def some_instance_method_available_on_every_controller puts "a method available on every controller!" end def another_instance_method_available_on_every_controller puts "another method available on every controller!" end module FantasticStuff extend ActiveSupport::Concern # note: don't specify included or ClassMethods if unused included do class_attribute :class_attribute_only_available_on_fantastic_controllers, instance_writer: false end module ClassMethods # class methods available only if make_this_controller_fantastic is specified in the controller def some_fanastic_class_method put "a fantastic class method!" end end # instance methods available only if make_this_controller_fantastic is specified in the controller def some_fantastic_instance_method puts "a fantastic instance method!" end def another_fantastic_instance_method puts "another fantastic instance method!" end end end end ```