Dynamically remove mixin ancestor from class - ruby

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?

What you are trying to achieve is a very bad pattern. You should almost never use prepend or include dynamically, but model your code around the concepts you (and people possibly reading your code) do understand.
What you probably want to do is to create (some kind of) a Delegator called CardAffectedByEnvironment - and then instead of doing Card.new(x), you will always do CardAffectedByEnvironment.new(Card.new(x), env), where env will keep all your state changes, or add a method real_cost that would calculate things based on your cost and environment and use this method.
Below is a code with CardAffectedByEnvironment that would maybe describe better how I would assume it would work:
class Environment
def initialize
#modifiers = []
end
attr_reader :modifiers
def next_round
#modifiers = []
end
def modifiers_for(method)
#modifiers.select { |i| i.first == method }
end
end
class CardAffectedByEnvironment
def initialize(card, env)
#card, #env = card, env
end
# Since Ruby 3.0 you can write `...` instead of `*args, **kwargs, &block`
def method_missing(method, *args, **kwargs, &block)
value = #card.public_send(method, *args, **kwargs, &block)
#env.modifiers_for(method).each do |_, modifier|
value = modifier.call(value)
end
value
end
end
class Joker
def cost
10
end
end
env = Environment.new
card = CardAffectedByEnvironment.new(Joker.new, env)
p card.cost # => 10
env.modifiers << [:cost, ->(i) { i - 1 }]
p card.cost # => 9
env.next_round
p card.cost # => 10

Related

Ruby: the role of `initialize`

I'm wondering if there's a convention / best practice for how initialize should be used when building Ruby classes. I've recently built a class as follows:
class MyClass
def initialize(file_path)
#mapped_file = map_file(file_path)
end
def map_file(file_path)
# do some processing and return the data
end
def run
#mapped_file.do_something
end
end
This uses initialize to do a lot of heavy lifting, before methods are subsequently called (all of which rely on #mapped_data).
My question is whether such processing should be handled outside of the constructor, with initialize used simply to store the instances' inputs. Would the following, for example, be preferable?
class MyClass
def initialize(file_path)
#file_path = file_path
end
def run
mapped_file.do_something_else do
etc_etc
end
end
def mapped_file(file_path)
#mapped_file ||= map_the_file_here
end
end
I hope this question isn't considered too opinion based, but will happily remove if it's deemed to be.
So, is there a 'correct' way to use initialize, and how would this fit with the scenarios above?
Any questions or comments, let me know.
As was mentioned in the comments, constructor is usually used to prepare the object, not do any actual work. More than a ruby convention, this a rule of thumb of almost all Object-Oriented languages.
What does "preparing the object" usually entail? Initializing members with default values, assigning passed arguments, calling the initializer of a super-class if such exists, etc.
In your case, this how I would rewrite your class:
class MyClass
def initialize(file_path)
#file_path = file_path
end
def map_file
#mapped_file ||= map_file_here(#file_path)
end
def run
map_file.do_something
end
end
Since run requires the file to be mapped, it always calls map_file first. But the internal map_file_here executes only once.

best way to organize a long piece of code in ruby refinement block

module Access
def last
self[-1]
end
def start_end
self[0] + last
end
end
module StringExt
refine String do
include Access
end
end
using StringExt
puts 'abcd'.last # => d
puts 'abcd'.start_end
When a class being refined with too many connected methods, I think it is better to extract them to a module. However, in above example which demos a problem when one method calls another(see the last statement), and it produces following error.
in 'start_end': undefined local variable or method 'last' for "abcd":String (NameError)
Similar issue was solved using a global variable, which also works for my example. But I'm seeking another better way to organize inter-called methods being refined and avoid a global thing.
How would advice a better way to organize those methods?
Here's a general pattern I ended up using. Basically I found no workaround for using global identifiers at some level. But this can be done fairly cleanly by making those globals classes/modules. This will be more clear as an example:
module StringPatches
def self.non_empty?(string)
!string.empty?
end
def non_empty?
StringPatches.non_empty?(self)
end
def non_non_empty?
!StringPatches.non_empty?(self)
end
refine String do
include StringPatches
end
end
class Foo
using StringPatches
puts "asd".non_empty? # => true
puts "asd".non_non_empty? # => false
end
The class methods on StringPatches don't get exported to using. But since classes/modules are constants (globals) they can be accessed from anywhere.

Inheritance caching patterns in ruby

Say I have a parent class:
class Stat
def val
raise "method must be implemented by subclass"
end
end
And a subclass:
class MyStat < Stat
def val
#performs costly calculation and returns value
end
end
By virtue of extending the parent class, I would like the subclass to not have to worry about caching the return value of the "val" method.
There are many patterns one could employ here to this effect, and I've tried several on for size, but none of them feel right to me and I know this is a solved problem so it feels silly to waste the time and effort. How is this most commonly dealt with?
Also, it's occurred to me that I may be asking the wrong questions. Maybe I should't be using inheritance at all but composition instead.
Any and all thoughts appreciated.
Edit:
Solution I went with can be summed up as follows:
class Stat
def value
#value ||= build_value
end
def build_value
#to be implemented by subclass
end
end
Typically I use a simple pattern regardless of the presence of inheritance:
class Parent
def val
#val ||= calculate_val
end
def calculate_value
fail "Implementation missing"
end
end
class Child < Parent
def calculate_val
# some expensive computation
end
end
I always prefer to wrap the complex and expensive logic in its own method or methods that have no idea that their return value will be memoized. It gives you a cleaner separation of concerns; one method is for caching, one method is for computing.
It also happens to give you a nice way of overriding the logic, without overriding the caching logic.
In the simple example above, the memoized method val is pretty redundant. But the pattern it also lets you memoize methods that accept arguments, or when the actual caching is less trivial, maintaining that separation of responsibilities between caching and computing:
def is_prime(n)
#is_prime ||= {}
#is_prime[n] ||= compute_is_prime
end
If you want to keep the method names same and not create new methods to put logic in, then prepend modules instead of using parent/child inheritance.
module MA
def val
puts("module's method")
#_val ||= super
end
end
class CA
def val
puts("class's method")
1
end
prepend MA
end
ca = CA.new
ca.val # will print "module's method" and "class's method". will return 1.
ca.val # will print "module's method". will return 1.

How do I switch implementation at instantiation time?

I have a class in Ruby that holds some stuff, I'll call FooBox:
class FooBox
...
end
I have two possible backing-data stores for FooBox called BoxA and BoxB with different characteristics but the same interface:
class BoxA
include Enumerable
def put_stuff(thing)
...
end
end
class BoxB
include Enumerable
def put_stuff(thing)
...
end
end
How can I instantiate a FooBox, and, based on a parameter, decide whether to back it with a BoxA or BoxB implementation? I do not want to pass in the implementation into the constructor; I only want to pass something to determine which kind to use.
class FooBox
def initialize(implementation_choice)
# ???
end
end
I usually do something like this:
class BoxA
def self.match? options
# figure out if BoxA can be used given options
end
end
# Implement BoxB (and other strategies) similarly to BoxA
class FooBox
STRATEGIES = [BoxA, BoxB]
def initialize options
#options = options
end
def strategy
#strategy ||= STRATEGIES.detect { |strategy| strategy.match? #options }
end
end
This keeps the responsibility of “knowing” if the strategy is able to be used within the strategy itself (rather than making the context class monolithic), and then just picks the first one in the list that says it can work.
I’ve used this pattern (and similar variations for slightly different problems) several times and have found it very clean.
The simple solution is create a mapping for the strategy's type and strategy class, just like #Andrew Marshall's solution
But to be better I would considering two things:
The strategies' holder (here is the FooxBox ) now need to know every box implenentation, and hard-coding their names to itself; this is not a flexiable
approach, considering one day you want to add another strategy, go to the code and add it? With ruby we can do it with a 'self registering' easily.
You don't want to strategies holder will return implementation wildly, I mean both 'BoxA' and 'BoxB' or someday's 'BoxXYZ' should belong to same strategy
concept, in Java, it maybe means all of them should implemente an interface, with ruby we generally do it with include SomeMoudle
In my application I use the following solution(just demo)
module Strategies
def self.strategies
##strategies ||= {}
end
def self.strategy_for(strategy_name)
##strategies[strategy_name]
end
end
module Strategy
def self.included(base)
base.class_eval do
def self.strategy_as(strategy_name)
Strategies.strategies[strategy_name] = self
end
end
end
end
class BoxA
include Strategy
strategy_as :box_a
def do_stuff
puts "do stuff in BoxA"
end
end
class BoxB
include Strategy
strategy_as :box_b
def do_stuff
p "do stuff in BoxB"
end
end
## test
Strategies.strategy_for(:box_a).new.do_stuff
Strategies.strategy_for(:box_b).new.do_stuff
If you want to detect strategy with match block, you can change strategy_as to accept a block. then use Strategies.strategy_for{...}.new.do_stuff

Optimal Design of a Ruby Data Structures Library

I am working with data structures fundamentals in Ruby for learning at the CS sophomore/junior level.
My question: Given the following code, does anyone see any design issues with this approach to a data structures library in Ruby? Especially the Module#abstract_method. Is it okay to do this in terms of duck typing philosophy? Does this make the code clearer for people from static languages and give some semblance of an interface?
class Module
def abstract_method(symbol)
module_eval <<-"end_eval"
def #{symbol.id2name}(*args)
raise MethodNotImplementedError
end
end_eval
end
end
class AbstractObject < Object
abstract_method :compare_to
protected :compare_to
class MethodNotImplementedError < StandardError; end
def initialize
super
end
include Comparable
def <=>(other)
if is_a?(other.class)
return compare_to(other)
elsif other.is_a?(self.class)
return -other.compare_to(self)
else
return self.class <=> other.class
end
end
end
# methods for insertion/deletion should be provided by concrete implementations as this behavior
# is unique to the type of data structure. Also, concrete classes should override purge to discard
# all the contents of the container
class Container < AbstractObject
include Enumerable
def initialize
super
#count = 0
end
attr_reader :count
alias :size :count
# should return an iterator
abstract_method :iter
# depends on iterator object returned from iter method
# layer of abstraction for how to iterate a structure
def each
i = iter
while i.more?
yield i.succ
end
end
# a visitor provides another layer of abstraction for additional
# extensible and re-usable traversal operations
def accept(visitor)
raise ArgumentError, "Argument must be a visitor" unless visitor.is_a?(Visitor)
each do |obj|
break if visitor.done?
visitor.visit(obj)
end
end
# expected to over-ride this in derived classes to clear container
def purge
#count = 0
end
def empty?
count == 0
end
def full?
false
end
def to_s
s = ""
each do |obj|
s << ", " if not s.empty?
s << obj.to_s
end
self.class + "{" + s + "}"
end
end
class List < Container
def initialize
super
end
def compare_to(obj)
"fix me"
end
end
A few remarks:
Defining a method that only raises a NotImplemented error is somewhat redundant, since Ruby will do that anyway if the method does not exist. The code you wrote there is just as useful as simply putting a comment to say "You must implement a method called compare_to". In fact that is what the Enumerable module in Ruby's standard library does - in the documentation it specifically says that in order to use the functionality in Enumerable you must define an each() method.
a compare_to method is also redundant, since that is precisely what the <=> operator is for.
Using an actual iterator object is a bit overkill in Ruby, since blocks tend to have a much more elegant and simple approach. Same goes for your visitor pattern - you don't need to use a visitor for "extensible and re-usable traversal operations" when you can just pass a block to a traverse method. For example you have many of them in Enumerable: each, each_with_index, map, inject, select, delete_if, partition, etc. All of these use a block in a different way to provide a different type of functionality, and other functionality can be added on in a fairly simple and consistent way (especially when you have open classes).
Regarding interfaces, in Ruby (and pretty much any other dynamic language, like Python) people usually use interfaces that are implicit, which means that you don't actually define the interface in code. Instead you typically rely on documentation and proper testing suites to ensure that code works well together.
I think that your code may be more coherent to someone coming from a Java world because it sticks to the "Java way" of doing things. However to other Ruby programmers your code would be confusing and difficult to work with since it doesn't really stick to the "Ruby way" of doing things. For example, an implementation of a select function using an iterator object:
it = my_list.iter
results = []
while it.has_next?
obj = it.next
results << obj if some_condition?
end
is much less clear to a Ruby programmer than:
results = my_list.select do |obj|
some_condition?
end
If you would like to see an example of a data structures library in Ruby, you can see the algorithms gem here: http://rubydoc.info/gems/algorithms/0.3.0/frames
Also take a look at what is provided by default in the Enumerable module: http://www.ruby-doc.org/core/classes/Enumerable.html. When you include Enumerable you receive all of these functions for free.
I hope this helps!

Resources