0

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.
foobar443
  • 2,339
  • 3
  • 23
  • 31

2 Answers2

1

You can do this by prepending your module instead of including it.

Instead of using define_method as a sort of ersatz alias_method, just call the method advertise in your modules too.

Within your advertise method, you can call super to call up the inheritance hierarchy.

Frederick Cheung
  • 83,189
  • 8
  • 152
  • 174
0

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
Keith Bennett
  • 4,722
  • 1
  • 25
  • 35
  • Yes there is probably an easier way but I haven't found it yet and I actually need to use the reflection's features of Ruby in this project. I can not include all methods in the `Phone` class because I would like to still call the `advertise` method. It must be its behavior that changes, not the way I call it. – foobar443 May 08 '16 at 22:05
  • When you say "I actually need to use the reflection's features of Ruby in this project.", is the reason a technical one or some other one? If technical, can you share what it is? I'm editing my answer to include a lambda approach that might work well for you. – Keith Bennett May 08 '16 at 22:40
  • I've added a screening: entry that shows calling the "original" method plus added functionality. I also made some corrections. – Keith Bennett May 09 '16 at 01:01
  • The reason is simple: it is a school project about Reflection in Ruby :) Nice approach that you showed me here. But I'm not suppose to change the `Phone` class's file. However I could create an alias for the `advertise` method, then dynamically replace the `advertise` method with the one executing the `RING_STRATEGIES[:ring_strategy].()`. Of course, I should also add the Hash to the Phone class. – foobar443 May 09 '16 at 08:13
  • I just wish for you and your classmates that you view these assigments as tools for learning the Ruby language and not recommended approaches to problem solving. – Keith Bennett May 09 '16 at 10:18
  • I solved my problem with the inspiration you gave me. So I'll accept your answer. Thanks again. – foobar443 May 11 '16 at 10:38