seza443 seza443 - 1 month ago 10
Ruby Question

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.


  • I'm using Ruby 2.3.0 on Windows.

  • proceed
    could be replaced by something else but I'd like to keep it as clean as possible in the module that defines the adaptation.


Answer

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