How to prevent overwriting methods by accident?

S

Stefan Salewski

In Ruby we can add new methods to existing classes.
How can we ensure that the name of our new method does not already
exists -- I do not want to overwrite an existing method by accident. I
think there exists a way to list all currently existing methods of a
class, but the solution should work in future too( e.g. for Ruby 3.0
when predefined classes may have more predefined methods): I want
something like

class String
def very_useful_method
if already_defined(this_method)
puts 'We are overwriting an existing method by accident'
Process.exit
end
end
end

Best regards

Stefan Salewski
 
J

Jeremy Bopp

In Ruby we can add new methods to existing classes.
How can we ensure that the name of our new method does not already
exists -- I do not want to overwrite an existing method by accident. I
think there exists a way to list all currently existing methods of a
class, but the solution should work in future too( e.g. for Ruby 3.0
when predefined classes may have more predefined methods): I want
something like

class String
def very_useful_method
if already_defined(this_method)
puts 'We are overwriting an existing method by accident'
Process.exit
end
end
end

This should do the trick:

class String
unless method_defined?:)my_method) then
def my_method
end
else
puts 'We tried to overwrite an existing method'
Process.exit
end
end


-Jeremy
 
B

Brian Candler

Run ruby with the -w flag:

$ ruby -w -e 'class String; def length; end; end'
-e:1: warning: method redefined; discarding old length

I'm not sure if there's a way to make these warnings fatal.
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

But is there a way to call the original method instead of just quitting
out?
The super keyword does this, I'm not really sure what it is, a macro maybe?
I just think of it as a function I have available in overridden methods,
like block_given?

Be aware that unless you specify its args, it will pass whatever params the
method originally received.


class Parent
def self.ruby!(arg = nil)
puts "Parent.ruby!(#{arg.inspect})"
end
end

class Child1 < Parent
def self.ruby!(arg)
super
puts "Child1.ruby!(#{arg.inspect})"
end
end

class Child2 < Parent
def self.ruby!(arg)
super(12)
puts "Child2.ruby!(#{arg.inspect})"
end
end

class Child3 < Parent
def self.ruby!(arg)
super()
puts "Child1.ruby!(#{arg.inspect})"
end
end


Child1.ruby!( :abc )
puts
Child2.ruby!( 'abc' )
puts
Child3.ruby!( /abc/ )
 
B

Brian Candler

Josh said:
The super keyword does this

No it doesn't; not if you redefine a method in the same class.

You need to use alias.

class String
alias :eek:rig_length :length
def length
puts "Whee!"
orig_length
end
end
puts "abc".length
 
J

Jeremy Bopp

But is there a way to call the original method instead of just quitting
out?

If you want to know if you can conditionally define the method and skip
the exit, yes, you can. Just leave out the else clause of the example.
That way, your method is called only if the original one didn't already
exist. I used this once to implement Enumerable#none? only under
versions of Ruby where that method was not yet implemented.

If you want to have a kind of functionality similar to that provided by
the super keyword, you can do that too:

class String
if method_defined?:)my_method) then
alias :my_method_orig :my_method
end

def my_method
# Maybe do some extra stuff here.
...

# Call the original method if there is one.
if methods.include?("my_method_orig") then
my_method_orig
end

# Maybe do more extra stuff here.
...
end
end

This will alias the original instance of my_method, if there is one, to
my_method_orig. Your new my_method definition can call the alias if it
exists and skip it otherwise. I suppose this would be good for
instrumenting methods to add logging and the like, but I would take care
with using this generally since it can make some unnecessarily
complicated code.

-Jeremy
 
A

Alex Stahl

I noticed that too. Super works fine in cases of inheritance, but the
example wasn't inheriting, it was reopening.

Though my first instinct was to try to use the "this" object in the
"else..." statement to just use the current object to call the original
method. But then I remembered ruby doesn't have "this".
 
B

Brian Candler

Alex said:
Though my first instinct was to try to use the "this" object in the
"else..." statement to just use the current object to call the original
method. But then I remembered ruby doesn't have "this".

In Ruby, the current object is "self". Inside a 'class' definition, it's
the class object.
Foo
=> nil
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

No it doesn't; not if you redefine a method in the same class.

You need to use alias.

class String
alias :eek:rig_length :length
def length
puts "Whee!"
orig_length
end
end
puts "abc".length
Whoops. Thanks, my brain swapped override and overwrite.
 
J

Jesús Gabriel y Galán

If you want to know if you can conditionally define the method and skip
the exit, yes, you can. =A0Just leave out the else clause of the example.
=A0That way, your method is called only if the original one didn't alread= y
exist. =A0I used this once to implement Enumerable#none? only under
versions of Ruby where that method was not yet implemented.

If you want to have a kind of functionality similar to that provided by
the super keyword, you can do that too:

class String
=A0if method_defined?:)my_method) then
=A0 =A0alias :my_method_orig :my_method
=A0end

=A0def my_method
=A0 =A0# Maybe do some extra stuff here.
=A0 =A0...

=A0 =A0# Call the original method if there is one.
=A0 =A0if methods.include?("my_method_orig") then
=A0 =A0 =A0my_method_orig
=A0 =A0end

=A0 =A0# Maybe do more extra stuff here.
=A0 =A0...
=A0end
end

This will alias the original instance of my_method, if there is one, to
my_method_orig. =A0Your new my_method definition can call the alias if it
exists and skip it otherwise. =A0I suppose this would be good for
instrumenting methods to add logging and the like, but I would take care
with using this generally since it can make some unnecessarily
complicated code.

Shouldn't we be using respond_to? instead of method_defined? or
methods.include?.
This way, if the method is handled dynamically through method_missing
you can check that too, and not "override" the behaviour.

Jesus.
 
B

Brian Candler

Jesús Gabriel y Galán said:
Shouldn't we be using respond_to? instead of method_defined? or
methods.include?.

respond_to? only works on instances of objects, not when you're within
the class.

You could do "new.respond_to? ..." but if the initialize method needs
arguments, you'll have to provide them.
 
J

Jesús Gabriel y Galán

respond_to? only works on instances of objects, not when you're within
the class.

You could do "new.respond_to? ..." but if the initialize method needs
arguments, you'll have to provide them.

Ah, you are right, I missed that we were not in an instance...
Anyway, be careful then with this technique, cause if the instance
would be handling that method call with method_missing you will not
know it using the above.

Regards,

Jesus.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,046
Latest member
Gavizuho

Latest Threads

Top