Overriding methods in an ActiveSupport::Concern module which are defined by a class method in the same module
Asked Answered
R

1

10

I have an ActiveSupport::Concern module which looks roughly like the following:

module MyModel
  module Acceptance

    extend ActiveSupport::Concern

    included do
      enum status: [:declined, :accepted]
    end

    def declined!
      self.status = :declined
      # some extra logic
      self.save!
    end

    def accepted!
      self.status = :accepted
      # some extra logic
      self.save!
    end
  end
end

This is only ever going to be included into ActiveRecord classes, hence the use of enum. Basically, I'm overriding the declined! and accepted! methods that are created by ActiveRecord::Enum.enum with some extra, custom logic of my own.

The problem is, this doesn't work, because when I call @model.declined! it justs call the original implementation of declined! and ignores my custom method.

Looks like my custom methods are being included into the calling class before the included block is being run - meaning my custom methods are being overridden by the ones defined by enum, instead of the other way around.

There some easy workarounds in this particular situation (e.g. I could move the call enum back into the including class and make sure it's above the line include MyModel::Acceptance, but I'm wondering if there's a way I can solve this problem while keeping it all in the same module.

Is there any way I can call a class method within included that defines an instance method, then override that instance method from within the same Concern module?

Radiology answered 29/1, 2015 at 12:32 Comment(0)
L
8

I think you're looking for define_method.

module MyModel
  module Acceptance

    extend ActiveSupport::Concern

    included do
      enum status: [:declined, :accepted]

      define_method :declined! do
        self.status = :declined
        # some extra logic
        self.save!
      end

      define_method :accepted! do
        self.status = :accepted
        # some extra logic
        self.save!
      end

    end
  end
end
Landis answered 27/2, 2015 at 0:44 Comment(2)
Could you elaborate on why this works? In Rails 3.2 @GeorgeMillo's way works well. In Rails 4.2 it doen't. Did they change something internally about how the methods are added?Alleyne
I've just re-read the question and there's kind of an explanation there. But any extra info would be gratefully accepted :)Alleyne

© 2022 - 2024 — McMap. All rights reserved.