I am trying to develop a simple card game console application in ruby as a pet project. One of the interactions I would like to add to this card game is something along the lines of "During your turn, you can buy cards as if they cost X less". Now, I thought about modelling this behaviour with the help of mixin decorators. Assuming the following class:
class Card
attr_reader :cost
def initialize(cost)
self.cost = cost
end
def play(*_args)
raise NotImplementedError, "Override me!"
end
private
attr_writer :cost
end
I thought a simple solution would be something like this:
class CardThatDiscounts < Card
def play(*_args)
@mod = Module.new do
def cost
super - 1
end
end
Card.prepend(@mod)
end
end
This would give me the flexibility to target either the class itself, or specific instances of the cards. However, I am unsure of how to reverse this effect, at the end of the turn. I saw a few questions similar to this one, the answers of which said something along the lines of:
- Remove the defined method manually.
This approach won't really work for me, since I am decorating existing methods instead of adding new ones. I could maybe rename the existing method when I prepend the module, and then return it back, but what happens in case I want to chain multiple such decorators?
- Use gem mixology
Mixology is an old gem, and seems no longer maintained. Though, according to the documentation, it is exactly what I need. However, the implementation mostly deals with ruby internals, that I unfortunately do not understand. Is it not possible to modify a class ancestor chain from within Ruby, or do I need c/Java extension for this?
I considered other alternatives, such as SimpleDelegator or DelegateClass, but both of these require that I instantiate new objects, and then somehow replace references to existing Card objects with these new wrapped objects, and back again, at the end of turn. Which seemed a bit more involved than modifying the ancestry chain directly.
I suppose my question has 2 parts. Is it possible to remove a specific ancestor from a class's ancestor chain using pure ruby (since mixology gem already suggests it is possible to do with c/Java extensions)? If not, what would be a suitable workaround to get a similar behaviour?