3

I started looking into Ruby, since I am looking to a more dynamic alternative to Java. I like how you can modify a class in Ruby after it's definition, for example like this:

class A
  def print
    "A"
  end
end

class B < A
  def print
    super + "B"
  end
end

class A
  alias_method :print_orig, :print
  def print
    print_orig + "+"
  end
end

puts B.new.print # A+B

Now I try to do the same with mixins:

class A
  def print
    "A"
  end
end

class B < A
  def print
    super + "B"
  end
end

module Plus
  alias_method :print_orig, :print
  def print
    print_orig + "+"
  end
end

A.extend(Plus) # variant 1
B.extend(Plus) # variant 2
class A # variant 3
  include Plus
end
class B # variant 4
  include Plus
end
puts B.new.print

However none of the variants produce the expected result. BTW, the expected result is the following: I want to be able to 'patch' class A with a mixin, in order to modify its behavior. I want to use mixins, since I want to 'patch' several classes with the same behavior.

Is it possible to do what I want? If yes, how?

Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
Pal Szasz
  • 2,954
  • 3
  • 20
  • 18

2 Answers2

5

Your module code doesn't work because it is executed in wrong context. You need to execute it in context of A, but it is instead evaluated in context of Plus. This means, you need to change self from Plus to A.

Observe:

class A
  def print
    "A"
  end
end

class B < A
  def print
    super + "B"
  end
end

module Plus
  self # => Plus
  def self.included base
    self # => Plus
    base # => A
    base.class_eval do
      self # => A
      alias_method :print_orig, :print
      def print
        print_orig + "+"
      end
    end
  end
end

A.send :include, Plus
B.new.print # => "A+B"
Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
1

You can't really use Mixins in this way. You're generating a conflict between the class and its mixin. Mixins implicitly resolve the conflict by linearization. Bottom line is: In case of conflict, the class's method is preferred over the mixin. To fix that, you can use Sergio' Tulentsev's approach and have the mixin change its base class aggressively.

Or, you can add methods reflectively. Consider this example, which I've stolen from Mark's blog.

class Talker

  [:hello, :good_bye].each do |arg|
    method_name = ("say_" + arg.to_s).to_sym
    send :define_method, method_name do
      puts arg
    end
  end

end


t = Talker.new
t.say_hello
t.say_good_bye
Community
  • 1
  • 1
nes1983
  • 15,209
  • 4
  • 44
  • 64