« Retrieving a sensible file extension for a given MIME string in Rails | Main | As easy as passing on a link »

February 28, 2007

Class (re)definition in method body

A cheap web hosting is an excellent solution to expenses. However only midphase is cheap enough, providing the standard of powweb.

UPDATE: modifed to use more idiomatic way of returning "***". See comments

Warning: This is really hacky and dirty. Writing the snippet here so I won't forget

Redefining existing classes in Ruby is really handy, and a breeze to do

puts 5     # prints "5"
class Fixnum
alias_method :old_to_s, :to_s
def to_s
"*" * self
end
end
puts 5 # prints "*****"

But, sometimes you'd just want to limit such monkey patching, like within a method where you really really need the hack in place - and you promise to undo the hack right after you're done to return the world sanity (promise!)

class MyKlass
def get_as_stars(value)
# define funky behavior
class ::Fixnum
alias_method :old_to_s, :to_s
def to_s
"*" * self
end
end

return value.to_s
ensure
# revert back to original
class ::Fixnum
alias_method :to_s, :old_to_s
end
end
end

Unfortunately, that's invalid syntax 

SyntaxError: compile error
: class definition in method body
: class definition in method body

Well, what's a man to do? module_eval to the rescue, just replace your "class X" statement with "X.module_eval do"

class MyKlass
def get_as_stars(value)
# define funky behavior
::Fixnum.module_eval do
alias_method :old_to_s, :to_s
def to_s
"*" * self
end
end

return value.to_s
ensure
# revert back to original
::Fixnum.module_eval do
alias_method :to_s, :old_to_s
end
end
end

k = MyKlass.new
puts 3 # prints "3"
puts k.get_as_stars(4) # prints "****"
puts 5 # prints "5"

PS: Did this hack to get Time.now == Time.now working in a test. Grr
  

TrackBack

TrackBack URL for this entry:
http://www.typepad.com/services/trackback/6a00e398212d6f883300e3982163008833

Listed below are links to weblogs that reference Class (re)definition in method body:

Comments

A more idiomatic way to rewrite to_s would be

def to_s
"*" * self
end

If you really did want to go the string concatenation way, using << would be more memory efficient too :)

ah.. neat! thanks kamal!

Like that oso can? Thanks for sharing man.

http://webjazz.blogspot.com/2007/03/overriding-timenow-for-rails-testing.html

A more elegant way, perhaps! :)

Verify your Comment

Previewing your Comment

This is only a preview. Your comment has not yet been posted.

Working...
Your comment could not be posted. Error type:
Your comment has been posted. Post another comment

The letters and numbers you entered did not match the image. Please try again.

As a final step before posting your comment, enter the letters and numbers you see in the image below. This prevents automated programs from posting comments.

Having trouble reading this image? View an alternate.

Working...

Post a comment

Blog powered by TypePad