mejores las gemas ruby-on-rails ruby-on-rails-3 gem applicationcontroller load-order

ruby-on-rails - las mejores gemas de ruby



¿Cómo puedo extender ApplicationController en una gema? (4)

Pensé que encontraría una forma elegante de extender ApplicationController en una gema Rails 3.x.

En lib/my_namespace/my_controller.rb mi gema, tuve:

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

pero cuando se carga la gema, la app/controllers/application_controller.rb aún no está cargada, por lo que falla:

/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)

Como solución temporal, había definido ApplicationController en lib/gem_namespace/application_controller.rb mi gema como:

class ApplicationController < ActionController::Base end

Asumí que aunque lo hubiera definido allí, se redefiniría en la app/controllers/application_controller.rb mi aplicación Rails 3, de modo que tanto los controladores de la aplicación que extendían ApplicationController como los controladores que extendían MyNamespace::MyController amplíe el ApplicationController definido en app/controllers/application_controller.rb .

Sin embargo, notamos que después de cargar la gema, los controladores que extienden ApplicationController no pudieron acceder a los métodos definidos en app/controllers/application_controller.rb . Además, el módulo ApplicationHelper (app/helpers/application_helper.rb) ya no estaba siendo cargado por otros módulos de ayuda.

¿Cómo puedo extender ApplicationController dentro del controlador en mi gema con el fin de definir un before_filter y after_filter y usar la initialize para acceder al nombre de la clase para determinar la clase del modelo asociado que luego podría almacenar y usar dentro de sus métodos?

Actualización 2012/10/22 :

Esto es lo que se me ocurrió:

En 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

y en 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


La verdad es mucho más simple y flexible.

Agregue a lib/engine.rb esto: class Engine < Rails::Engine; end class Engine < Rails::Engine; end

Y luego simplemente use:

ActionController::Base.class_eval do include SomethingFromMineGemModule # or: def hello_from_gem ''Hey people!'' end end


Para este tipo específico de funcionalidad, recomendaría crear un módulo en su gema e incluir ese módulo en su Controlador de aplicaciones

class ApplicationController < ActionController::Base include MyCoolModule end

Para agregar filtros anteriores, etc. (agregue esto a su módulo)

def self.included(base) base.send(:before_filter, my_method) end

Actualización: es posible que solo base.before_filter :my_method hacer base.before_filter :my_method que es más limpio.


Pude hacer referencia a ApplicationController con una devolución de llamada de inicializador.

código de gema que subclases / referencias ApplicationController:

class GemApplicationController < ApplicationController before_filter :method_to_call def method_to_call #your code here end end

código de llamada de devolución de llamada para crear un controlador subclasificado:

module GemName def self.load_gem_application_controller require "path/to/gem_application_controller" end end

rails_app / config / initializers / gem_name.rb

GemName.load_gem_application_controller

Luego, tenga controladores que usen esta subclase de funcionalidad GemApplicationController

class SpecialCaseController < GemApplicationController # this will inherit from the gem''s controller, # which inherits from the rails_app ApplicationController end


Aquí hay una Gist que muestra cómo acceder a la clase de la subclase y almacenarla en una variable de instancia y acceder a ella en los filtros de antes y después. Utiliza el método de inclusión.