thedudedude
thedudedude

Reputation: 11

Understanding the Ruby mixin's proxy class mechanism

I am refering to the follwing article, paragaph "Pitfall #1": http://definingterms.com/2013/03/23/pitfalls-of-ruby-mixins/

I've got the following setup, a slightly changed form of the one used in the article:

module EmailReporter
    def send_report
        #Sending a report ...
        puts self.class
    end
end

class Person
end

class Employee < Person
    include EmailReporter
end

As I understand, when I include EmailReporter in Employee, its methods are not just pasted into it, but a anonymous proxy class is created and put into the inheritance chain above Employee and under Person.

When I call Employee.new.send_report, I am not in the scope of Employee, but in the scope of the proxy class. Thus, I would not have access to constants or class variables of Employee, but I would have access to instance variables.

Questions:

  1. If I run Employee.new.send_report, the output is "Employee", but not something like "Employee__proxy_class". Why is that? Shouldn't "self" refer to the proxy class?

  2. The article suggest the proxy class delegates method calls to the Module EmailReport. But of course you cannot just call instance methods on it. So whom does the proxy class delegate its calls to?

Thanks to everyone trying to answer my question!

Upvotes: 1

Views: 549

Answers (1)

Cary Swoveland
Cary Swoveland

Reputation: 110675

I think your fundamental problem is in understanding the value of self. Often it's helpful to just add code that tells you what self is at different points of your code. Let's begin with a very simple example:

class Dad
  def hi
    puts "self in Dad#hi = #{self}"
  end
end

class Son < Dad
end

son = Son.new
  #=> #<Son:0x007fc1f2966b88>
son.methods.include?(:hi)
  #=> true
son.hi
  # self in Dad#hi = #<Son:0x007fc1f2966b88>

Just because:

son.method(:hi).owner
  #=> Dad

does not mean self is changed when hi is invoked on self. Now let's include a mixin:

module Helper
  def hi
    puts "self in Helper#hi = #{self}"
  end
end

Son.include Helper

son.hi
  # self in Helper#hi = #<Son:0x007fc1f29551d0>
son.method(:hi).owner
  #=> Helper 

I think you understand why Helper#hi was invoked here rather than Dad#hi.

Does this answer your first question? I don't understand the second question, but perhaps you can answer it yourself now that you've been enlightened on self. If not, please clarify #2 (with an edit to your question).

Upvotes: 1

Related Questions