Mechanism of overridding methods in a Ruby class - ruby

Suppose I want to override a method of an existing class Array as below.
class Array
def to_s
self.join(',')
end
end
So my question is - How does this overriding work? Does this add this definition of method to_s to the class Array? I mean if the class contained 'n' method definitions, it would now contain 'n+1' method definitions. Also, is it that there are two definitions of to_s method and the one that is added last is the one that would work?
Thanks.

You aren't overriding the method, you are re-defining it. The one that was there is gone, replaced with what you put in. This is a risky thing to do w/the standard libraries, you don't know what behavior other code is relying upon.
You can try it w/your own class.
class Foo
def bar
puts 'One'
end
end
class Foo
def bar
puts 'Two'
end
end
Foo.new.bar
# Two
class Foo
def bar
puts 'Three'
super
end
end
Foo.new.bar
# Three
# test.rb:18:in `bar': super: no superclass method `bar'
# for #<Foo:0x007fd642029278> (NoMethodError)

Related

Namespace collision with top-level methods

I have the following Ruby namespace problem:
I have a number of top-level methods in a "library", among these I have a get_name(param) method.
The problem is that I want to use this get_name(param) method inside a class that has its own get_name() method (without param!).
Of course this results in a problem as the 2 methods are different and the new one in the class overrides the top-level one.
Is there a way to do something like this:
def get_name(param)
puts param
end
class Foo
def get_name()
puts "name"
end
def bar()
self.get_name() #should work and print name, i.e. it uses the get_name method of Foo
get_name("abc") #should work and print abc, i.e. it uses the top-level method
end
end
I would like to know if there is an easy way to achieve this without having to alter the name or scope of the top-level methods. (The goal is to have those available without modification as global methods, but still be able to call instance methods of Foo without errors.
If the library methods are really top-level methods and are not part of any class or module, then, they will get defined as private methods of Object class.
You can do following to invoke it:
def bar()
self.get_name()
Object.send(:get_name, "abc") # Will print "abc" to console
end
The so called "top level method" is only a method of main object, which is only an Object. If you want that method to be able to be called (almost) anywhere, you should make it a kernel method.
module Kernel
def get_name(param)
puts param
end
end
Note that Kernel is almost at the tail of any classes ancestors chain (before BasicObject), which means almost all the objects are a Kernel. So in your Foo class, you can override it like this:
class Foo
def get_name(*args)
return super if args.any?
puts 'name'
end
end
Edit
If you can't make those top level methods kernel methods, you can store the main object in a constant or a global variable, and call those top level methods on it.
$main = self
class Foo
def get_name(*args)
return $main.send(:get_name, *args) if args.any?
puts 'name'
end
end
First off: there is no such thing as a "top-level method". There is exactly one kind of method in Ruby: instance methods.
The method you are referring to is a private instance method of Object. Since Object is a superclass of almost any other class (excluding Object and its superclasses), if you are sure that there are no other methods with the same name somewhere else in the inheritance hierarchy, you could use Method#super_method to get access to the method and then call it:
def get_name(param)
puts param
end
class Foo
def get_name
puts 'name'
end
def bar
get_name #should work and print name, i.e. it uses the get_name method of Foo
method(:get_name).super_method.('abc') #should work and print abc, i.e. it uses the top-level method
end
end
Foo.new.bar
# name
# abc
Alternatively, if you don't know whether there are any other methods by the same name within the inheritance hierarchy, you could grab a reference to the UnboundMethod directly from Object and then bind and call it:
class Foo
def bar
get_name #should work and print name, i.e. it uses the get_name method of Foo
Object.instance_method(:get_name).bind(self).('abc') #should work and print abc, i.e. it uses the top-level method
end
end
Foo.new.bar
# name
# abc

Determining method's visibility on the fly

I am writing a method that will define an instance method inside a class; something similar to attr_accessor:
class Foo
custom_method(:foo)
end
I have implemented that by adding custom_method function to the Module module, and defining the method with define_method, which works fine. But I cannot figure out how to take into account visibility attributes from the class. For example, in the following class
class Foo
custom_method(:foo)
private
custom_method(:bar)
end
the first generated method (foo) must be public, and the second one (bar) must be private. How do I do that? Or, how do I find the context in which my custom_method is called: private, public, or protected?
Thanks!
After experimenting with this for a bit, I'm completely baffled. Initially, I thought that Ruby took the default visibility (public, private, or protected) into account when you call Module#define_method. It turns out though that on Ruby versions <= 2.0, that's not the case:
class Foo
private
define_method :foo do
puts "Foo called!"
end
end
Foo.new.foo # Prints "Foo called!"
On Ruby 2.1+, it's even more confusing. Module#define_method seems to take default method visibility into account:
class Foo
private
define_method :foo do
puts "Foo called!"
end
end
Foo.new.foo # NoMethodError: private method `foo' called for #<Foo:0x8cb75ac>
But it only works when you are calling define_method from directly inside the class. Calling a method which then calls define_method doesn't work:
class Foo
def self.hello_on name
define_method name do
puts "Hello, #{name}!"
end
end
private
hello_on :foo
end
Foo.new.foo # Prints "Hello, foo!"
Dang it Ruby! Why?
Okay, this calls for desperate measures...
module DefaultMethodVisibilityAccessor
attr_reader :current_default_method_visibility
def public(*args)
#current_default_method_visibility = :public if args.empty?
super
end
def protected(*args)
#current_default_method_visibility = :protected if args.empty?
super
end
def private(*args)
#current_default_method_visibility = :private if args.empty?
super
end
end
class Module
prepend DefaultMethodVisibilityAccessor
end
module MethodDefiner
def hello_on name
define_method name do
puts "Hello, #{name}!"
end
case current_default_method_visibility
when :public
public name
when :protected
protected name
when :private
private name
end
end
end
Usage:
class Foo
extend MethodDefiner
hello_on :foo
private
hello_on :bar
end
Foo.new.foo # Prints "Hello, foo!"
Foo.new.bar # NoMethodError: private method `bar' called for #<Foo:0x8ec18fc>
There, fixed!
I think this is impossible, because the scope visibility level set by Module.private is managed at the C virtual machine level and not exposed to Ruby.
EDIT: and it's only available in the same syntactical scope that it is called, so when you call custom_method it loses the visibility level set inside the class declaration.
It's set in set_visibility(), and used in vm_define_method(), but I can't find any reference to the corresponding variable being available from Ruby.
I suggest using some kind of custom parameter to specify the visibility level of your methods.
You can use Module#private_method_defined? to verify if a method is defined as private

Call a function and method with the same name in Ruby

Forgive me for my english.
I am a php programmer and now i want to learn Ruby.
In php if you want to call function "foo" within a class, you simply call foo(), and if you want to call method "foo" you call this->foo().
The question is, is it possible to call function and method with the same name in Ruby?
For example:
def foo
puts "In foo function"
end
class A
def call_foo
foo
#How can i call foo function, not a method?
end
def foo
puts "In foo method"
end
end
a = A.new
a.call_foo #Prints "In foo method"
There is no such thing as a function in Ruby, only methods.
If you define a method at the top-level it is an instance method of Object.
If you define a class without a superclass, it's superclass is Object.
So, your A#foo simply overrides Object#foo. And if it overrides Object#foo, it should respect its contract. You should never need to call Object#foo on an A, if A#foo implements Object#foo's contract correctly (and it should, otherwise it would be a violation of the Liskov Substitution Principle). If you want to reuse Object#foo's implementation within A#foo, you can defer to the superclass implementation using super.
Note: what you want is possible using reflection, but the correct solution would be to fix your design:
def foo
puts "In foo function"
end
class A
def call_foo
self.class.superclass.public_instance_method(:foo).bind(self).()
end
def foo
puts "In foo method"
end
end
a = A.new
a.call_foo #Prints "In foo function"
The foo method outside of your class definition is bound to Object, which is an instance of Class. So to call your method you can do:
> Object.foo­
=> "In foo function"
But as it was pointed out before, you should rather declare this method in an appropiate class.
You can also declare this method as class method with:
class A
def self.foo
puts "In foo class method"
end
end
Now you can call it without creating an A instance:
> A.foo
=> puts "In foo class method"

When an instance method calls a class method, I have to use self.class_method?

I'm getting an error so I guess I have to reference a class method from inside of an instance method with self.class_method_name, but why is that?
Shouldn't it resolve this by itself? Confused.
def self.blah(string)
..
end
def some_method()
some_thing = blah("hello")
end
If you have
# This won't work
class Foo
def self.blah(string)
puts "self.blah called with a string of #{string}"
end
def some_method
# This won't work
self.blah("hello")
end
end
foo = Foo.new
foo.some_method
It won't work, because it'll look for the instance method Foo#blah. Instead, you're looking for Foo.bar.
To make some_method call Foo.bar, you have to make some_method refer to the Foo class, and then call blah on it.
class Foo
def self.blah(string)
puts "self.blah called with a string of #{string}"
end
def some_method
# This will work
self.class.blah("hello")
end
end
foo = Foo.new
foo.some_method
The reason you have def self.blah to define the method, but self.class.blah to call the method, is that in the former, self refers to the Foo class, while in the latter, self refers to the foo object, so you need self.class to refer to the Foo class.
It may be easier to think of self as part of the method name, that way it's clear that you never defined a blah method, you defined only a self.blah method. (To clarify: the previous sentence shouldn't be thought of too much, so please don't read into it, as it's not how things are actually working, just a sort of "layman's terms" attempt at describing why it doesn't work.)
Also, what if you had defined a blah instance method in addition to the class method? If calling blah was enough to access the class method, how would you call the instance method?
Finally, there really isn't any such thing as a class method in Ruby, "class methods" are really methods of the singleton class.

How to make a base class method non-overridable in ruby?

I have some base class A with a method that is not to be overridden.
class A
def dont_override_me
puts 'class A saying, "Thank you for not overriding me!"'
end
end
And another class B that extends A and tries to override the dont_override_me method.
class B < A
def dont_override_me
puts 'class B saying, "This is my implementation!"'
end
end
If I instantiate B and call dont_override_me, class B's instance method will be called.
b = B.new
b.dont_override_me # => class B saying, "This is my implementation!"
This is because of ruby's properties. Understandable.
However, how do I force the base class method dont_override_me to be non-overridable by it's derived classes? I could not find a keyword like final in java for ruby. In C++, the base class methods can be made non-virtual so that they become non-overridable by the derived classes. How do I achieve this in ruby?
You can do it, by hooking the change event and changing it back, but it seems a bit smelly to me:
http://scie.nti.st/2008/9/17/making-methods-immutable-in-ruby
It's one of those things that sort of defines Ruby, so fighting against it seems a little pointless imo. If someone redefines something so it breaks horribly.. that's their problem ;-)
Here's a way to do it:
http://www.thesorensens.org/2006/10/06/final-methods-in-ruby-prevent-method-override/
This has also been packaged into a gem called "finalizer" (gem install finalizer)
This makes use of the method_added callback and compares the new method name with a list of methods that you wish to make final.
I recommend:
class A #This is just as you've already defined it.
def dont_override_me
puts 'class A saying, "Thank you for not overriding me!"'
end
end
module BehaviorForB
def dont_override_me
puts 'class B saying, "This is my implementation!"'
end
def greet
"Hello, Friend."
end
end
class B < A
include BehaviorForB
end
b = B.new
b.dont_override_me #=> class A saying, "Thank you for not overriding me!"
b.greet #=> Hello, Friend.
By keeping B's methods tucked away in an mixin, you get exactly what you desire. Any method of B's methods that are not already in A will be available. Methods that are already in A will not be overridden.
One way to prevent a method from being overridden by a subclass (but not recommend) :
class Class
def frozen_method(method)
if class_variable_defined?(:##__frozen_methods__)
add= class_variable_get(:##__frozen_methods__) | [method]
class_variable_set(:##__frozen_methods__,add)
else
class_variable_set(:##__frozen_methods__,[method])
end
class << self
def inherited(child)
def method_added(method)
if class_variable_get(:##__frozen_methods__).include? method
send(:remove_method, method)
error="Cannot change method #{method} because it's not overridde"
raise TypeError, error
end
end
end
end
end
end
class Foo
def hello
'hello'
end
def foo
'foo'
end
frozen_method :foo
end
class Bar < Foo
def foo
'new foo'
end
end
#=> TypeError: Cannot change method foo because it's not overridde
Bar.new.foo #=> 'foo'
Warning: this example is not complete. If you add frozen_method for a previously defined method in the subclass, when this method will be modified in the subclass, it will lose its implementation.

Resources