Re-opening an existing module and changing a method

“I hear and I forget; I see and I remember; I do and I understand.”
Wouldn’t it be nice if it were that simple - clearly they did not know
about the subtle art of debugging.

So, I’m trying to hack at the math module.
Expected:

Math.sqrt(2)
=> sqrt(2)
Actual result, mileage does not vary:

Math.sqrt(2)
=> 1.4142135623731

require ‘mathn’
module Math
alias :old_sqrt :sqrt
def sqrt x
result = old_sqrt x
if result.is_a? Float
“sqrt(#{x})”
else
result
end
end
end

puts Math.sqrt(2)

I had originally not attempted an alias, I just did “result = super x”
but it didn’t really amount to much, either.
Where am I thinking about this wrong?

Aldric G. wrote:

“I hear and I forget; I see and I remember; I do and I understand.”
Wouldn’t it be nice if it were that simple - clearly they did not know
about the subtle art of debugging.

So, I’m trying to hack at the math module.
Expected:

Math.sqrt(2)
=> sqrt(2)
Actual result, mileage does not vary:
Math.sqrt(2)
=> 1.4142135623731

require ‘mathn’
module Math
alias :old_sqrt :sqrt
def sqrt x
result = old_sqrt x
if result.is_a? Float
“sqrt(#{x})”
else
result
end
end
end

puts Math.sqrt(2)

I had originally not attempted an alias, I just did “result = super x”
but it didn’t really amount to much, either.
Where am I thinking about this wrong?

Looks fine to me. Does old_sqrt actually return a Float or some other
numeric type?

Actually, why use type checking at all? Why not change the condition to
use kind_of or simply test result.to_i - result == 0 ?

Best,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]

On Thu, Nov 19, 2009 at 6:27 PM, Aldric G. [email protected]
wrote:

=> 1.4142135623731
end
end
end

puts Math.sqrt(2)

I had originally not attempted an alias, I just did “result = super x”
but it didn’t really amount to much, either.
Where am I thinking about this wrong?

The problem is that mathn is using the module_function method to
convert sqrt into an method of the Math module.
According to http://ruby-doc.org/core/classes/Module.src/M001642.html:

“Module functions are copies of the original, and so may be changed
independently”

So I think you are not redefining the module function, but the
original, which doesn’t have any effect when you call Math.sqrt (this
is calling the version created by module_function). Doing this:

irb(main):042:0> module Math
irb(main):043:1> def sqrt x
irb(main):044:2> result = super x
irb(main):045:2> p [result, result.class]
irb(main):046:2> result
irb(main):047:2> end
irb(main):048:1> module_function :sqrt
irb(main):049:1> end
=> Math
irb(main):050:0> Math.sqrt 2
NoMethodError: super: no superclass method sqrt' from (irb):44:in sqr

allows you to actually override the version created by
module_function, but I don’t know how to then call the original, since
neither the original alias you had nor super are working. But maybe
this points you in the right direction.

Jesus.

Marnen Laibow-Koser wrote:

Looks fine to me. Does old_sqrt actually return a Float or some other
numeric type?

Actually, why use type checking at all? Why not change the condition to
use kind_of or simply test result.to_i - result == 0 ?

Well… Not that it’s particularly meaningful, but here’s my benchmark
for that question:

require ‘benchamrk’
num = 5.5
n = 5_000_000
Benchmark.bmbm do |x|
x.report(“kind_of?”) { n.times do ; n.kind_of? Float ; end }
x.report(“is_a?”) { n.times do ; n.is_a? Float ; end }
x.report(“to_i”) { n.times do ; n.to_i - n == 0 ; end }
end

Rehearsal --------------------------------------------
kind_of? 1.719000 0.000000 1.719000 ( 1.720000)
is_a? 1.641000 0.000000 1.641000 ( 1.642000)
to_i 2.750000 0.000000 2.750000 ( 2.751000)
----------------------------------- total: 6.110000sec

           user     system      total        real

kind_of? 1.734000 0.000000 1.734000 ( 1.735000)
is_a? 1.703000 0.000000 1.703000 ( 1.720000)
to_i 2.688000 0.000000 2.688000 ( 2.689000)


You know how metaprogramming is all about the self, according to Yehuda
Katz’s latest blog post? :slight_smile: I, er, forgot to do self.sqrt …

module Math
alias_method :old_sqrt, :sqrt
def self.sqrt x
result = 5.5 #self.old_sqrt(x)
if result.is_a? Float
“sqrt(#{x})”
else
result
end
end
end

puts Math.sqrt(2)
=> sqrt(2)

puts Math.methods.find { |i| i[0…0] == “o”}
=> object_id

… So I’m not sure how to do an alias_method on a method that’s got a
“self.” in front of it, I guess…
puts Math.methods.find { |i| i[0…0] == “o”}

2009/11/19 Jesús Gabriel y Galán [email protected]:

Math.sqrt(2)
result
The problem is that mathn is using the module_function method to
irb(main):042:0> module Math
from (irb):44:in `sqr

allows you to actually override the version created by
module_function, but I don’t know how to then call the original, since
neither the original alias you had nor super are working. But maybe
this points you in the right direction.

Got it:

irb(main):001:0> require ‘mathn’
=> true
irb(main):002:0> module Math
irb(main):003:1> class << self
irb(main):004:2> alias :old_sqrt :sqrt
irb(main):005:2> end
irb(main):006:1> def sqrt x
irb(main):007:2> result = old_sqrt x
irb(main):008:2> p [result, result.class]
irb(main):009:2> result
irb(main):010:2> end
irb(main):011:1> module_function :sqrt
irb(main):012:1> end
=> Math
irb(main):013:0> Math.sqrt 2
[1.4142135623731, Float]
=> 1.4142135623731

Jesus.

On Thu, Nov 19, 2009 at 7:45 PM, Aldric G. [email protected]
wrote:

Jesús Gabriel y Galán wrote:

irb(main):003:1> class << self
irb(main):004:2> alias :old_sqrt :sqrt
irb(main):005:2> end

Ah-ha! My version is underneath. So, what the bit of code I quoted does
is… It reopens “the class of the module” to make a change in the alias?

Yes, module_function creates a method in the singleton class of the
module.
So you need to enter that singleton class via class << self to be in a
place where self is that singleton class, in order to be able to alias
that method correctly.

 "sqrt(#{x})"

else
result
end
end
end

puts Math.sqrt(2)
=> sqrt(2)

Jesus.

Jesús Gabriel y Galán wrote:

irb(main):003:1> class << self
irb(main):004:2> alias :old_sqrt :sqrt
irb(main):005:2> end

Ah-ha! My version is underneath. So, what the bit of code I quoted does
is… It reopens “the class of the module” to make a change in the alias?

module Math

class << self
alias :old_sqrt :sqrt
end

def self.sqrt x
result = old_sqrt(x)
if result.is_a? Float
“sqrt(#{x})”
else
result
end
end
end

puts Math.sqrt(2)
=> sqrt(2)

Julian L. wrote:

You’re doing instance methods when you really want class methods.

Good time to google the difference if you don’t already know.

Is this a new insight, or is this just putting words to what Jesus
already helped me discover (and hack) ?
Instance method : Customer.new.phone # (probably yields an error : phone
number not set)
Class method : Customer.find(:first) # (Rails-style)

Right ?

You’re doing instance methods when you really want class methods.

Good time to google the difference if you don’t already know.

Julian

Blog: http://random8.zenunit.com/
Twitter: http://twitter.com/random8r
Learn: http://sensei.zenunit.com/
New video up now at http://sensei.zenunit.com/ real fastcgi rails
deploy process! Check it out now!

On Fri, Nov 20, 2009 at 1:47 PM, Aldric G. [email protected]
wrote:

Julian L. wrote:

You’re doing instance methods when you really want class methods.

Good time to google the difference if you don’t already know.

Is this a new insight, or is this just putting words to what Jesus
already helped me discover (and hack) ?

Well, people usually call class methods to methods that are defined in
the singleton class of a class.
In this case it could be a bit confusing, since we are not talking
about a class but a Module.
So I would call it a module method (or module function).

Instance method : Customer.new.phone # (probably yields an error : phone
number not set)
Class method : Customer.find(:first) # (Rails-style)

Right ?

Right, but it would be more clear to say module method, in my opinion.

Jesus.

Jesús Gabriel y Galán wrote:

Instance method : Customer.new.phone # (probably yields an error : phone
number not set)
Class method : Customer.find(:first) # (Rails-style)

Right ?

Right, but it would be more clear to say module method, in my opinion.

Well –

Math.sqrt(5) : Module method, because it’s defined in a module
Customer.find(:first) : Class method, because it’s defined in the class

Right? :slight_smile: