Search code examples
rubytestinginclude

Reload class with include


In the following situation, where include_class is a preference, can I somehow dynamically change the value of include_class and reload Image such that it instead includes the new value of include_class?

module Foo
  included do
    @test_var = :foo
  end
end

module Bar
  included do
    @test_var = :bar
  end
end

Config.include_class = Foo

class Image
  include Config.include_class
end

# ... run tests with default configuration for Image, where Image.test_var = :foo

Config.include_class = OtherClass

# ... how can I reload or re-evaluate Image such that Image.test_var = :bar?

Context

I am trying to test whether a configuration option (that is normally set by an initializer) has the correct effects on the application. Because this is part of a test suite the the modules and classes might be loaded before and all configuration changes need to be reset after the test.


Solution

  • I'd suggest to use refinements instead, in this case class will be reset after the test (on any other stuff). I implemented an example without active_concern, but the idea should be clear. This example can be executed with plain ruby as a single script.

    # This part is just to keep close to your example
    class Config
      def self.include_class=(klass)
        @@include_class = klass
      end
    
      def self.include_class
        @@include_class ||= nil
      end
    end
    
    # Assuming Bar is a default module and Foo will be used in tests
    module Bar
      def print_something
        puts 'I am Bar'
      end
    end
    
    module Foo
      def print_something
        puts 'I am Foo'
      end
    end
    
    # Setting default module
    Config.include_class = Bar
    
    # Defining Image class
    class Image
      include Config.include_class
    end
    
    #Changing config
    Config.include_class = Foo
    
    # This is a refinement
    module ImagePatch
      # It will include current Config.include_class
      # Note that methods from Bar that are not present in Foo will not be removed
      refine Image do
        include Config.include_class
      end
    end
    
    # Here we will create module where refinement is activated
    module TestEnv
      # Activating the patch
      using ImagePatch
      Image.new.print_something #=>I am Foo
    end
    
    #Outside of the TestEnv module we enjoy the default Image class
    Image.new.print_something #=>I am Bar