Search code examples
rubyclassinstance-methods

Extend return value of class instance method


I have a class that have an instance method, that returns a hash. I can't change the code of that class directly, but I can extend it with modules. I need to add some new keys to the returning hash of the method. Something like this:

class Processor
  def process
    { a: 1 }
  end
end

module ProcessorCustom
  def process
    super.merge(b: 2) # Not works :(
  end
end

Processor.send :include, ProcessorCustom

processor = Processor.new
processor.process  # returns { a: 1 }, not { a: 1, b: 2 }

How can I do that? Thanks.


Solution

  • You could call prepend instead of include:

    Processor.prepend(ProcessorCustom)
    
    processor = Processor.new
    processor.process
    #=> {:a=>1, :b=>2}
    

    prepend and include result in different ancestor order:

    module A; end
    module B; end
    module C; end
    
    B.ancestors     #=> [B]
    
    B.include(C)
    B.ancestors     #=> [B, C]
    
    B.prepend(A)
    B.ancestors     #=> [A, B, C]
    

    Alternatives

    Depending on your use-case, you could also extend a specific instance: (this doesn't affect other instances)

    processor = Processor.new
    processor.extend(ProcessorCustom)
    processor.process
    #=> {:a=>1, :b=>2}
    

    Or use SimpleDelegator to implement a decorator pattern:

    require 'delegate'
    class ProcessorCustom < SimpleDelegator
      def process
        super.merge(b: 2)
      end
    end
    
    processor = ProcessorCustom.new(Processor.new)
    processor.process  #=> {:a=>1, :b=>2}