Order methods when inheriting in Ruby - ruby

When using class inheritance, is there a way to order the methods? I want them ordered, because I am inheriting from a class that executes all public methods, in order.
For example:
class Foo
def action_two
puts "action 2"
end
end
class Bar < Foo
def action_one
puts "action 1"
end
end
Bar.instance_methods # => [:action_two, :action_one, ...]
...and I would like it to return [:action_one, :action_two, ...]

The documentation doesn't say anything about the order and I actually get them in the order you want when I use Ruby 2.1.2. If you want them to come out in a specific order then I think you'll have to do it yourself. You could use the include_super parameter to instance_methods to get just the current class's methods and then ask superclass for its methods:
class Bar < Foo
#...
def self.instance_methods
super(false) + superclass.instance_methods
end
end
If you're going to be dealing with BasicObject as a possible superclass then you'll want to add a superclass.nil? check before calling superclass.instance_methods.
If you're inheritance hierarchy is deep then you should be able to put this hackery into the lowest base class (Foo in this case) and let the subclasses pick it up through inheritance.

In order to do that, you have to define Bar#action_one before inheriting Foo into Bar. Since class inheritance is determined at the creation time of the class that inherits, you cannot do that as is.
A way to overcome this is to make Foo a module instead of a class, and include it into Bar after Bar#action_one is defined.
module Foo
def action_two
puts "action 2"
end
end
class Bar
def action_one
puts "action 1"
end
include Foo
end
Bar.instance_methods
# => [:action_one, :action_two, ...]

Bar.ancestors.each_with_object([]) { |a, o| o << a.instance_methods }.flatten.uniq

Related

Mechanism of overridding methods in a Ruby class

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)

How to properly destroy a class

In Ruby, I have a DAO class, which is extended by a class that makes managing the connections easier, which is extended by a class that represents and manipulates data in a DB, which is further extended by another class. To use an animal metaphor it would look like this:
class Animal
...
end
class Mammal < Animal
...
end
class Feline < Mammal
...
end
class Cat < Feline
...
end
class Lion < Cat
...
end
...
In PHP, there is __destruct method that runs when you destroy/delete a class. And should that class extend another class, you simply add parent::__destruct() to the class's __destruct method like this:
public function __destruct() {
// Clean up code for this class here
...
// Execute clean up code for Parent class
parent::__destruct();
}
I could have a similar method for all the classes except Animal. Since it doesn't extend anything, the parent::__destruct(); line is no longer valid.
However, as I understand it, Ruby doesn't have a method like this for its objects. A finalizer can be set, but I decided to just put in a cleanup method I can call whenever I want to destroy/delete a class. That would take care of anything that needed doing prior to my setting the class to nil.
This raises a new problem though. If the method is always named cleanup and I call lion_instance.cleanup, I assume it calls the Lion#cleanup. How then to get it to call the cleanup in class Cat and then Feline and on down the chain?
Or is this a wrong approach and you have a better idea?
The Ruby idiom for this is to yield to a block which does work, and when the block returns, do cleanup. Ruby's built-in "File.open" does this:
File.open("/tmp/foo") do |file|
file.puts "foo"
end
When the block ends, the file is closed for you, without you having to do anything. This is an excellent idiom. Here's how you might implement something like that:
class Foo
def self.open(*args)
foo = new(*args)
yield foo
foo.close
end
def initialize
# do setup here
end
def close
# do teardown here
end
end
And to use it:
Foo.open do |foo|
# use foo
end
Foo#close will be caused automatically after the end
This will work with subclassing as well. That's because class methods are inherited just as are instance methods. Here's the superclass:
class Superclass
def self.open(*args)
o = new(*args)
yield o
o.close
end
def initialize
# common setup behavior
end
def close
# common cleanup behavior
end
end
and two derived classes:
class Foo < Superclass
def initialize
super
# do subclass specific setup here
end
def close
super
# do subclass specific teardown here
end
end
class Bar < Superclass
def initialize
super
# do subclass specific setup here
end
def close
super
# do subclass specific teardown here
end
end
to use:
Foo.open do |foo|
# use foo
end
Bar.open do |bar|
# use bar
end
If you really need to make sure that cleanup happens no matter what, then use an ensure clause in the class method:
def self.open(*args)
foo = new(*args)
begin
yield foo
ensure
foo.close
end
end
This way, cleanup happens even if there is an exception in the block.
You can use ObjectSpace.define_finalizer
Something like:
class Animal
def initialize
ObjectSpace.define_finalizer(self, proc { # your code })
end
end
Well since no one answered your question about the method moving its way up the inheritance chain...
class Cat
def rawr
puts "rawr"
end
end
class Kitty < Cat
def rawr
puts "meow"
super
end
end
Cat.new.rawr
"Rawr"
Kitty.new.rawr
"rawr"
"meow"
Within a method, you can access the superclass's method of the same name by calling super.

Best way to add methods to a Class in execution time

I have to add methods to Class in execution time.
class ExtendableClass
end
The methods to add are declared in independent Classes.
module ExtensionClassOne
def method_one
end
end
module ExtensionClassTwo
def method_two
end
end
I'm looking for an (elegant) mechanism to add all the extension class methods into the ExtendableClass.
Approach 1
I'm thinking in explicily include the extension classes like:
ExtendableClass.send( :include, ExtensionClassOne )
ExtendableClass.send( :include, ExtensionClassTwo )
but it looks a little forced to have to call this private method every time I define a new extension class.
Approach 2
So I was looking for an automatic way to include this methods into my ExtendableClass class.
I'm thinking in declare an specific ancestor for this extension classes:
class ExtensionClassOne < Extension
def method_one
end
end
and then I'd need a mechanism to know all the childs of a class... something like the oposite of ancestors.
Once I have this list I can easily ExtendableClass.include all the list of classes. Even if I have to call to the private method here.
Approach 3
Also inheriting from the Extension class and detect in declaration time when this class is used as ancestor. In the way that the ActiveSupport.included method works, like an event binding. Then make the include there.
Any solution for implement approach 2 or approach 3? Do you recommend approach 1? New approachs?
#fguillen, you are right that the "explicit way is the cleanest approach". Since that is so, why don't you use the most "explicit" code which could be imagined:
class Extendable
end
class Extendable
def method_one
puts "method one"
end
end
class Extendable
def method_two
puts "method two"
end
end
...In other words, if you are defining a module which will be automatically included in a class as soon as it is defined, why bother with the module at all? Just add your "extension" methods directly to the class!
Approach 4 would be to define a macro on class level in Object
class Object
def self.enable_extension
include InstanceExtension
extend ClassExtension
end
end
and calling this macro in all your classes you want to be extended.
class Bacon
enable_extension
end
Car.enable_extension
This way,
you don't have to use #send to circumvent encapsulation (Approach 1)
you can inherit from any Class you want, because everything inherits from Object anyway (except 1.9's BasicObject)
the usage of your extension is declarative and not hidden in some hook
Downside: you monkeypatch build-in Classes and may break the world. Choose long and decriptive names.
Edit: Given your answer to my comment on the question I suppose this is not what you wanted. I see no problem with your "Approach 1" in this case; it's what I'd do. Alternatively, instead of using send to bypass the private method, just re-open the class:
class ExtendableClass
include ExtensionOne
end
Assuming I understand what you want, I'd do this:
module DelayedExtension
def later_include( *modules )
(#later_include||=[]).concat( modules )
end
def later_extend( *modules )
(#later_extend||=[]).concat( modules )
end
def realize_extensions # better name needed
include *#later_include unless !#later_include || #later_include.empty?
extend *#later_extend unless !#later_extend || #later_extend.empty?
end
end
module ExtensionOne
end
module ExtensionTwo
def self.included(klass)
klass.extend ClassMethods
end
module ClassMethods
def class_can_do_it!; end
end
end
class ExtendableClass
extend DelayedExtension
later_include ExtensionOne, ExtensionTwo
end
original_methods = ExtendableClass.methods
p ExtendableClass.ancestors
#=> [ExtendableClass, Object, Kernel, BasicObject]
ExtendableClass.realize_extensions
p ExtendableClass.ancestors
#=> [ExtendableClass, ExtensionOne, ExtensionTwo, Object, Kernel, BasicObject]
p ExtendableClass.methods - original_methods
#=> [:class_can_do_it!]
The included method is actually a hook. It is called whenever you are inherited from:
module Extensions
def someFunctionality()
puts "Doing work..."
end
end
class Foo
def self.inherited(klass)
klass.send(:include, Extensions) #Replace self with a different module if you want
end
end
class Bar < Foo
end
Bar.new.someFunctionality #=> "Doing work..."
There is also the included hook, which is called when you are included:
module Baz
def self.included(klass)
puts "Baz was included into #{klass}"
end
end
class Bork
include Baz
end
Output:
Baz was included into Bork
A very tricky solution, I think too much over-engineering, would be to take the inherited hook that #Linux_iOS.rb.cpp.c.lisp.m.sh has commented and keep all and every child class in a Set and combined it with the #Mikey Hogarth proposition of method_missing to look for all this child class methods every time I call a method in the Extendable class. Something like this:
# code simplified and no tested
# extendable.rb
class Extendable
##delegators = []
def self.inherited( klass )
##delegators << klass
end
def self.method_missing
# ... searching in all ##delegators methods
end
end
# extensions/extension_one.rb
class ExtensionOne < Extendable
def method_one
end
end
But the logic of the method_missing (and respond_to?) is gonna be very complicate and dirty.
I don't like this solution, just let it here to study it like a possibility.
After a very interesting propositions you have done I have realized that the explicit way is the cleanest approach. If we add a few recommendations taking from your answers I think I'm gonna go for this:
# extendable.rb
class Extendable
def self.plug( _module )
include( _module )
end
end
# extensions/extension_one.rb
module ExtensionOne
def method_one
puts "method one"
end
end
Extendable.plug( ExtensionOne )
# extensions/extension_two.rb
module ExtensionTwo
def method_two
puts "method two"
end
end
Extendable.plug( ExtensionTwo )
# result
Extendable.new.method_one # => "method one"
Extendable.new.method_two # => "method two"

How do I call a super class method

I have two classes A, and B. Class B overrides the foo method of class A. Class B has a bar method where I want to call the foo method of the super class. What is the syntax for such a call?
class A
def foo
"hello"
end
end
class B < A
def foo
super + " world"
end
def bar
# how to call the `foo` method of the super class?
# something similar to
super.foo
end
end
For class methods I can call the methods up the inheritance chain by explicitly prefixing the class name. I wonder if there is a similar idiom for instance methods.
class P
def self.x
"x"
end
end
class Q < P
def self.x
super + " x"
end
def self.y
P.x
end
end
Edit
My use case is general. For a specific case I know I can use alias technique. This is a common feature in Java or C++, so I am curious to know if it is possible to do this without adding extra code.
In Ruby 2.2, you can use Method#super_method now
For example:
class B < A
def foo
super + " world"
end
def bar
method(:foo).super_method.call
end
end
Ref: https://bugs.ruby-lang.org/issues/9781#change-48164 and https://www.ruby-forum.com/topic/5356938
You can do:
def bar
self.class.superclass.instance_method(:foo).bind(self).call
end
In this particular case you can just alias :bar :foo before def foo in class B to rename the old foo to bar, but of course you can alias to any name you like and call it from that. This question has some alternative ways to do it further down the inheritance tree.
You can alias old_foo foo before redefining it to keep the old implementation around under a new name. (Technically it is possible to take a superclass's implementation and bind it to an instance of a subclass, but it's hacky, not at all idiomatic and probably pretty slow in most implementation to boot.)
Based on #Sony's answer.
In case when you want to call the method method on some my_object and it's already overriden somewhere several classes higher (like for the Net::HTTPRequest#method), instead of doing .superclass.superclass.superclass use the:
Object.instance_method(:method).bind(my_object)
Like this:
p Object.instance_method(:method).bind(request).call(:basic_auth).source_location

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