3

I tried to extend an existing Singleton class in Ruby, as an example the Matrix class.

My first quick&dirty solution was a monkey patch (reopen class and extend with functionality).

But I think, monkey patching in general isn't good, especially if someone tries to overwrite basic methods of core classes like in String, Integer, ...

Next step was to find out, how I can get a real hard copy of the Matrix class with a new name (like MatrixExt) which behaves as a standalone singleton.

MatrixExt = Matrix

didn't the job, as it results in:

MatrixExt.scalar(2,0)
=> Matrix[[0, 0], [0, 0]]

So I only get multiple names for same singleton. Not, what I want.

Same result with the clone and the dup methods.

Also class inheritance won't work:

class MatrixExt < Matrix
  # patches ...
end

MatrixExt.scalar(2,0)
=> Matrix[[0, 0], [0, 0]]

And that was the most confusing part, because in self defined classes it is possible to get an inherited class. (So, why the core/std lib classes work different?)

My current solution is to have a module with extension and then explicitly use .extend after initializing, like:

m = Matrix.scalar(2,0).extend(MatrixExtModule)

That is okay for now, but my question is:

IS there another solution and -when yes- how to do it?

(No, copying the matrix.rb is not a good way, of course. ;o)

What I do wrong or where I think in a wrong way?

Thanks in advance for any solution and/or food for thoughts!

Marc-André Lafortune
  • 78,216
  • 16
  • 166
  • 166
asaaki
  • 1,970
  • 18
  • 22

2 Answers2

2

This is a bug.

I've created an issue on redmine.ruby-lang.org, which is the recommended thing to do to get these fixed.

I fixed the library, but I'm afraid it won't be available until Ruby 1.9.4.

Marc-André Lafortune
  • 78,216
  • 16
  • 166
  • 166
1

If you look at how Matrix is implemented, you'll notice that all the methods like scalar, diagonal etc. call the private new method, which will always return a new Matrix object (you don't override the methods, so Ruby will go look at the superclass implementation, where the implicit receiver of new is self, viz the Matrix class).

I guess your best bet is to wrap all your patches in a module and monkey patch Matrix that way.

Michael Kohl
  • 66,324
  • 14
  • 138
  • 158
  • Do you mean something like: `class Matrix; extend MatrixExt; end` – asaaki May 20 '11 at 11:59
  • (Mean: `... include MatrixExt ...`) – asaaki May 20 '11 at 12:39
  • If you did `class MyObject; end; MyObject.new` you'd end up with a object with a class of `MyObject` rather than `Object`, even though you haven't implemented `MyObject.new`. How come that's different? – Andrew Grimm May 22 '11 at 23:25
  • 1
    If you look at the C source of `Class#new` you'll see `obj = rb_obj_alloc(klass);`, meaning the returned object will be created with the current class before `initialize` gets called with `rb_obj_call_init(obj, argc, argv);`. In the `Matrix` example `new` is a private method which cannot be called with an explicit receiver, so `self` will always be `Matrix` when other methods like `diagonal` call it. – Michael Kohl May 23 '11 at 06:45