How to approach keeping `ApplicationController` agnostic while having all controllers in a package inherit from it?

This message was imported from the Ruby/Rails Modularity Slack server. Find more info in the import thread.

Hi all. I have a rails_shims package as proposed by Stephan in his book… I’ve been able to extract pretty generic ApplicationRecord and ApplicationJob classes there… but ApplicationController is a on a different level… heh.

Our current ApplicationController still lives on the main root app and it has maybe a dozen include X for helpers and concerns, plus a bunch of before_action and rescue_from calls, among some other stuff.

We want all the controllers on packages to inherit from this one so they all continue executing this stuff… but at the same time I want to keep the rails_shims package as agnostic as possible from our own app’s code (that’s a best-practice, right?).

Any advice on how to approach this problem?

One thing I did when moving our ApplicationJob from app/jobs/applicaiton_job.rb to packs/rails_shims/app/jobs/application_job.rb was to put some of our customizations on a config/initializers/librato_job_enqueuer.rb file like this:

module LibratoJobEnqueuer
  def self.prepended(mod)
    mod.after_enqueue :report_delayed_job_metrics
  end

  def report_delayed_job_metrics
    return if ENV["LIBRATO_USER"].blank? || ENV["LIBRATO_TOKEN"].blank?

    LibratoSupport::AsyncDelayedJobMetricsReporter.call
  end
end

Rails.application.reloader.to_prepare do
  ApplicationJob.prepend LibratoJobEnqueuer
end

But it felt like cheating. Thoughts?

The amount of custom code we have in our ApplicationController is way higher than what we had on ApplicationJob so I want to feel more confident with my decision this time.

Message originally sent by slack user U71TN2WF04X

As for the include X, what I have been working on is including that stuff on the controller that actually uses it instead of adding that functionality to every controller.

For the before_action and rescue_from might be a bit tougher to deal with, may be a module that takes care depending on the business logic? :thinking_face:

Module.prepend is “cheating” - undetectable cheating at that (. I wouldn’t do it. Accept the dependency violation for the time being seems very much preferable to me. To achieve something very similar but without the metaprogramming, create a dependency injection hook inside ApplicationJob that allows for registration of arbitrary methods to be called after an enqueue happened. Then inject report_delayed_job_metrics from the initializer.

Alternatively, could the rails_shims package depend on the package that contains AsyncDelayedJobMetricsReporter ?