Reputation: 45943
class Person
include Voice
include Beep
end
module Voice
def speak
puts 'speaking from module'
end
def original_speak
# ???
end
end
module Beep
def speak
puts 'beep beep'
end
end
How to call Voice#speak
? For example:
Person.new.voice_speak
I want to add this code to the Voice module, not the Person class or the Beep module.
Use case:
A module that does soft delete:
module Undeletable
def delete
# mark document as deleted. This creates a deletion document.
end
def restore
# Delete (for real) the deletion.
end
def obliterate
restore # because we don't want orphaned deletions.
real_delete # This should call Mongoid#delete
end
end
def Foo
include Mongoid::Document
include Undeletable
end
So, in general, when we call foo.delete, we want to soft delete. However, in rare cases, we want to do a real delete. The module should support both methods.
Upvotes: 0
Views: 84
Reputation: 110685
Readers: I posted this answer to the original question. The question was subsequently changed. If interested, check the edit history for context.
The steps are as follows.
Voice
that is invoked when the module is included in Person
. The (class) method for that is Module#included, which has one argument, the class including the module.Included
creates an alias original_speak
of Person.speak
(in Person
). This must be done first, while Person#speak
is the method speak
originally defined on Person
. Included
then removes the (original) instance method Person#speak
, using Module#remove_method (not Module#undef_method), causing Person#speak
to become the instance method Voice#speak
, which was in effect, right "behind" the original Person#speak
prior to the latter's removal.module Voice
def self.included(klass)
klass.send(:alias_method, :original_speak, :speak)
klass.send(:remove_method, :speak)
end
def speak
puts 'speaking from module'
end
end
class Person
def speak
puts 'speaking from class'
end
include Voice
end
Person.instance_method(:speak).owner
#=> Voice
Person.instance_method(:original_speak).owner
#=> Person
Person.ancestors
#=> [Person, Voice, Object, Kernel, BasicObject]
person = Person.new
person.original_speak
speaking from class
person.speak
speaking from module
Upvotes: 1
Reputation: 26768
I see the easiest way as being to add a parameter to the function which overwrites delete
. For example:
module Undeletable
def delete(call_super=false)
return super if call_super
return "some custom response"
end
def obliterate
delete(true)
end
end
The super
keyword is important here. If a method is overwritten by another, the original can be called by super
. It's also possible to pass arguments to super
.
Upvotes: 1