Search code examples
ruby-on-railsdoorkeeperactivesupport-concern

Ruby on Rails upgrade 7 to 8 my mixins for Doorkeeper not recognized


I have a Rails 7 app with Doorkeeper 5.8.1. In my app I have mixins for DoorKeeper Application, AccessToken, and AccessGrant using extend ActiveSupport::Concern. As an example

# app/models/concerns/my_mixin.rb
module MyMixin
   extend ActiveSupport::Concern

   included do
     has_one :other_obj, foreign_key: :id, class_name: "OtherObj"

     validates :api_key, uniqueness: true, allow_nil: true
     validates_length_of :api_key, minimum: 32, allow_blank: true
   end

   def my_method(input) do
     # do something with input.
     other_obj.process(input)
   end
end
Doorkeeper::Application.include MyMixin

When I upgraded to Rails 8, unit tests for DoorKeeper::Application.my_method(test_input) fail with

NoMethodError: undefined method 'my_method' for an instance of Doorkeeper::Application

Did something change between Rails 7 and 8 that I'm missing?


Solution

  • A much better approach than monkeypatching is to just configure Doorkeeper to use your own classes:

    # config/initializers/doorkeeper.rb
    Doorkeeper.configure do
      # ...
      # You can use your own model classes if you need to extend (or even override) default
      # Doorkeeper models such as `Application`, `AccessToken` and `AccessGrant.
      #
      # By default Doorkeeper ActiveRecord ORM uses its own classes:
      #
      # access_token_class "Doorkeeper::AccessToken"
      # access_grant_class "Doorkeeper::AccessGrant"
      application_class "MyApplication"
      # ...
    end
    
    class MyApplication < Doorkeeper::Application
      has_one :other_obj, foreign_key: :id, class_name: "OtherObj"
      validates :api_key, uniqueness: true, allow_nil: true
      validates_length_of :api_key, minimum: 32, allow_blank: true
      def my_method(input) do
        # do something with input.
        other_obj.process(input)
      end
    end
    

    Using modules to monkeypatch classes that are not yours should always be a last resort.

    Did something change between Rails 7 and 8 that I'm missing?

    Maybe. But it could have been that the monkeypatch worked when the code was eager loaded or that another test had previously caused the module to be required.

    If you're gonna monkeypatch do it explicitly (in an initializer) instead of having the autoloader do it implicitly when the file is loaded. It's also a good practice to separate the file that defines the monkeypatch from the "injection" as it lets you test it without side effects.