rubyreflectioncop

Ruby reflection composition: call original method from redefined method


A bit of context first

I have a class Phone that defines a method advertise like this:

class Phone
  def advertise(phone_call)
    'ringtone'
  end
end

I would like to have some adaptations for this method. For example when the user is in a quiet environment, the phone should vibrate and not ring. To do so, I define modules like

module DiscreetPhone    
  def advertise_quietly (phone_call)
    'vibrator'
  end
end

Then my program can do

# add the module to the class so that we can redefine the method
Phone.include(DiscreetPhone) 
# redefine the method with its adaptation
Phone.send(:define_method, :advertise, DiscreetPhone.instance_method(:advertise_quietly ))

Of course for this example I hardcoded the class and module's name but they should be parameters of a function.

And so, an execution example would give:

phone = Phone.new
phone.advertise(a_call) # -> 'ringtone'
# do some adaptation stuff to redefine the method
...
phone.advertise(a_call) # -> 'vibrator'

Finally coming to my question

I want to have an adaptation that call the original function and append something to its result. I would like to write it like

module ScreeningPhone
  def advertise_with_screening (phone_call)
    proceed + ' with screening'
  end
end

But I don't know what the proceed call should do or even where should I define it.


Solution

  • In my opinion, this approach is way too complex, and an inappropriate use of Modules.

    I recommend thinking about a simpler way to implement this.

    One simple way is to just include all the methods in the Phone class.

    Or, you could use a hash as a lookup table for ring strategies:

    class Phone
    
        attr_accessor :ring_strategy
    
        RING_STRATEGIES = {
            ringtone:  -> { ring_with_tone },
            discreet:  -> { ring_quietly },
            screening: -> { ring_with_tone; ring_screening_too }
            # ...
        }
    
        def initialize(ring_strategy = :ringtone)
            @ring_strategy = ring_strategy
        end
    
        def ring
            RING_STRATEGIES[:ring_strategy].()
        end
    
    end