Access class if class method is overwritten - ruby

Here is an external class which has its class method overwritten.
class Foo
def class
"fooo"
end
end
class Boo < Foo
end
class Moo < Foo
end
Now I have an instance of the subclass. Is it possible to find out which class it belongs to?
foo.class # currently returns 'fooo', I want get Boo or Moo.

You could use instance_method to grab the class method from somewhere safe (such as Object) as an UnboundMethod, bind that unbound method to your instance, and then call it. For example:
class_method = Object.instance_method(:class)
# #<UnboundMethod: Object(Kernel)#class>
class_method.bind(Boo.new).call
# Boo
class_method.bind(Moo.new).call
# Moo
class_method.bind(Foo.new).call
# Foo
Of course, if you've also replaced Object#class (or Kernel#class) then all bets are off and you're in a whole new world of pain and confusion.

An object's class also happens to be the superclass of its singleton_class:
Boo.new.singleton_class.superclass
#=> Boo
Moo.new.singleton_class.superclass
#=> Moo
Foo.new.singleton_class.superclass
#=> Boo

I prefer #Stefan’s and #muistooshort's solutions, but here's an alternative.
class Foo
def class
"foo"
end
end
class Boo < Foo
end
class Who < Boo
def class
"who"
end
end
boo = Boo.new
boo.method(:class).super_method.call
#=> Boo
who = Who.new
who.method(:class).super_method.call
#=> "foo"
who.method(:class).super_method.super_method.call
#=> Who
More generally:
def my_class(obj)
m = obj.method(:class)
until m.owner == Kernel do
m = m.super_method
end
m.call
end
my_class(boo)
#=> Boo
my_class(who)
#=> Who
See Method#super_method.

This solution is a little hacky, but overriding class is pretty hacky in itself, so when in Rome, do as Romans do:
class Foo
def class
'fooo'
end
end
class Boo < Foo
end
boo = Boo.new
=> #<Boo:0x00007fd2361feba8>
boo.class
=> "fooo"
boo.inspect
=> "#<Boo:0x00007fd2361feba8>"
klass = boo.inspect.split(':').reject(&:empty?)[0..-2].join('::').sub('#<', '')
=> "Boo"
boo.is_a?(Kernel.const_get(klass))
=> true
This also works for classes in a module:
module Bar
class Foo
def class
'fooo'
end
end
class Boo < Foo
end
end
boo = Bar::Boo.new
=> #<Bar::Boo:0x00007fe5a20358b0>
boo.class
=> "fooo"
boo.inspect
=> "#<Bar::Boo:0x00007fe5a20358b0>"
klass = boo.inspect.split(':').reject(&:empty?)[0..-2].join('::').sub('#<', '')
=> "Bar::Boo"
boo.is_a?(Kernel.const_get(klass))
=> true

Related

Ruby - What is difference between defining constant using 'const_set' and simple capitalize constant name in the class?

I am working on a Rails application where in one class, constants are defined using const_set constant_name, value.
I know we can define constants directly in class by just specifying the CAPITALIZE name of the constant with its value like following
class A
RANDOM_CONSTANT = 1
end
so what is the difference between defining constants using const_set and simply the way I have declared in class A ?
class A
RANDOM_CONSTANT = 1
end
is simpler to write and read. This should be the preferred way to set a constant.
constant_name = 'RANDOM_CONSTANT'
A.const_set(constant_name, 1)
will work when the constant name is dynamically generated, and thus more flexible. You'd typically use it only when you want to do some metaprogramming magic.
Other than that, they are equivalent.
I can add to the answer from #amadan that this method, like an others, for example const_get, method_missing, define_method etc is part of tools for meta-programming in ruby.
For example:
#const_get
Let's say we have such module Foo with list of constants: BOO, BAR, BAZ
module Foo
BAR = 1
BAZ = 2
BOO = 3
end
now we need to print all of it, we can do it dynamically or manually:
def print_dynamically
Foo.constants.each do |const|
p Foo.const_get(const)
end
end
def print_manually
p Foo::BAR
p Foo::BAZ
p Foo::BOO
end
> print_dynamically
=> 1
=> 2
=> 3
> print_manually
=> 1
=> 2
=> 3
#method_missing
Let's say we have such class Foo and we want to add some friendly error catching for NoMethodError
class Foo
def boo
puts 'boo'
end
def method_missing(method_name)
puts "sorry, method: ##{method_name} not defined."
puts "Here is a list of public methods:"
# here we take all defined public methods from class Foo except `#method_missing`
p public_methods(false).reject { |n| n == :method_missing }
end
end
> foo = Foo.new
> foo.boo
=> boo
> foo.bar
=> sorry, method: #bar not defined.
=> Here is a list of public methods:
=> [:boo]
#define_method
Let's say we need to define some list of simple(or not) methods, and we don't want to just copy-paste it:
instead of:
class Foo
def foo
puts "foo"
end
def boo
puts "boo"
end
def bar
puts "bar"
end
end
> foo.foo
=> foo
> foo.boo
=> boo
> foo.bar
=> bar
you can simply write it in his way:
class Foo
%i[foo boo bar].each do |method_name|
define_method(method_name) { puts "#{method_name}" }
end
end
> foo.foo
=> foo
> foo.boo
=> boo
> foo.bar
=> bar
Summary:
From examples below you can see that Ruby provide us a lot of tools for dynamically creation of methods, getting of it, getting of constants and setting of it.
P.S.
These is far not all the methods, it's just a few of them, but I think it's enough to show that you can do with meta-programming tools in ruby.
As far as I know, there's no difference, so this:
class A; end
A.const_set('RANDOM_CONSTANT', 1)
is equivalent to:
class A
RANDOM_CONSTANT = 1
end

Why does using class_eval cause toplevel class variable to be set?

I wrote some code to learn about the class_eval method. My understanding is that Foo.class_eval will set self to "Foo" and evaluate the block below in that context, just as saying "class Foo" will do. However, the code below shows that setting a class variable using class_eval will assign the class variable at the toplevel: to "Object". Why does this happen?
class Foo
end
class Foo
puts self
# => Foo
##class_var = "hello"
end
puts Object.class_variables
# => []
Foo.class_eval do
puts self
# => Foo
##class_var = "hi"
# => warning: class variable access from toplevel
end
puts Object.class_variables
# => ##class_var
As pointed out in comments a single # would do what you want, now if you were running the code from inside another class ## would work but it would set a class variable for both Foo, and whatever class you running from:
class Foo
puts self
# => Foo
##class_var = "hello"
end
class Bar
def self.main
puts Object.class_variables
# => []
Foo.class_eval do
puts self
# => Foo
##class_var = "hi"
end
end
end
Bar.main
p [Bar.class_variables]
#[[:##class_var]]
p [Foo.class_variables]
#[[:##class_var]]
That has to do with proc or block scope, I was expecting it to set only ##class_var only on Bar class but it actually sets on both, weird.

Using overridden class constants in class methods

To override class constants in a subclass one can do:
class Foo
CONST = [:foo, :baz]
def self.const
self::CONST
end
end
class Bar < Foo
CONST = [:foo, :bar]
end
print Foo.const # [:foo, :baz]
print Bar.const # [:foo, :bar]
This works as expected. The problem is when I try to call it from a class method, e.g when using define_method:
class Foo
CONST = [:foo, :baz]
def self.const
self::CONST
end
self.const.each do |c|
define_method("#{c}?") {
"#{c} exists"
}
end
end
foo = Foo.new
print foo.baz? # baz exists.
bar = Bar.new
print bar.bar? # undefined method `bar?'
How would one go about overriding the class constant so the right method bar? will get defined in this case, without having to duplicate the code in the subclass? Is there a DRY way of doing this without having to use class variables instead of class constants?
Because define_method is running in lexical scope, i.e. it's inline in the body of the Foo class definition so there is nothing to cause it to run in Bar.
class Foo
CONST = [:foo, :baz]
def self.define_const_methods(const)
const.each do |c|
define_method("#{c}?") { "#{c} exists" }
end
end
define_const_methods(CONST)
end
class Bar < Foo
CONST = [:foo, :bar]
define_const_methods(CONST)
end
That should do the trick. So you call define_const_methods at the end of the Foo class in it's lexical scope. And you also call it on any class that inherits from it. The inheriting class should find it's own version of that constant.
But this is pretty ugly, so you could dispense with the constants altogether and just use the define_const_methods to define them. Just like ActiveRecord does when it defines the association methods (has_one, has_many etc). So then you can whittle it down to;
class Foo
def self.define_my_methods(meths)
meths.each do |c|
define_method("#{c}?") { "#{c} exists" }
end
end
define_my_methods [:foo, :baz]
end
class Bar < Foo
define_my_methods [:foo, :bar]
end

Calling Super Methods in Ruby

I am trying to define some classes in Ruby that have an inheritance hierarchy, but I want to use one of the methods in the base class in the derived class. The twist is that I don't want to call the exact method I'm in, I want to call a different one. The following doesn't work, but it's what I want to do (basically).
class A
def foo
puts 'A::foo'
end
end
class B < A
def foo
puts 'B::foo'
end
def bar
super.foo
end
end
Probably, this is what you want?
class A
def foo
puts 'A::foo'
end
end
class B < A
alias bar :foo
def foo
puts 'B::foo'
end
end
B.new.foo # => B::foo
B.new.bar # => A::foo
A more general solution.
class A
def foo
puts "A::foo"
end
end
class B < A
def foo
puts "B::foo"
end
def bar
# slightly oddly ancestors includes the class itself
puts self.class.ancestors[1].instance_method(:foo).bind(self).call
end
end
B.new.foo # => B::foo
B.new.bar # => A::foo

"Personal" method in ruby

I'm looking for a way of making a method "personal" - note NOT PRIVATE to a class
here is an example - by "personal" I mean the behaviour of method "foo"
class A
def foo
"foo"
end
end
class B < A
def foo
"bar"
end
end
class C < B
end
a=A.new; b=B.new;c=C.new
I'm looking for a way of producing the following behaviour
a.foo #=> "foo"
b.foo #=> "bar"
c.foo #=> "foo" (ultimate base class method called)
Instead of creating 'personal' methods, change your inheritance structure.
It appears that you want the C class to have only some of the same functionality of the B class while not making changes to the A class.
class A
def foo
"foo"
end
end
class BnC < A
end
class B < BnC
def foo
"bar"
end
end
class C < BnC
end
a=A.new; b=B.new;c=C.new
There's no standard way of doing this. It circumvents how inheritance works. You could implement B's method to do the logic like this:
def foo
instance_of?(B) ? "bar" : super
end
And you could of course define a method on Class that would do this for you similar to public and private.
class Class
def personal(*syms)
special_class = self
syms.each do |sym|
orig = instance_method(sym)
define_method(sym) {|*args| instance_of?(special_class) ? orig.bind(self).call(*args) : super}
end
end
end
Then you can personal :foo in B just like you'd private :foo.
(This isn't at all optimized and I didn't implement the zero-argument behavior that public and private have because frankly it's a huge PITA to do right and even then it's a hack.)
Seems like it could be confusing, but here's one option:
class A
def foo
"foo"
end
end
class B < A
def initialize #when constructing, add the new foo method to each instance
def self.foo
"bar"
end
end
end
class C < B
def initialize #when constructing, do nothing
end
end
More generally, using a similar approach, you can always add a method to a given instance, which of course has no effect on inherited classes or indeed on other instances of the same class.
If you give us specifics of what you're ultimately trying to accomplish we can probably be more helpful.
Answering this is a bit tricky since I don't really see what you want to accomplish in practice, but you could try something like
class C < B
def foo
self.class.ancestors[-3].instance_method(:foo).bind(self).call
end
end
(The ancestors[-3] assumes that A inherits from Object and Kernel and your intent was to access the method from the topmost non-builtin class. Of course you could substitute self.class.ancestors[-3] with just A, or figure out the class from the Array ancestors yourself, etc.)
In practice it would be simpler to alias the original in class B if you can modify it (i.e. alias :foo_from_A :foo in class B < A before the new def foo, then you can call foo_from_A in C). Or just redefine what you want in C. Or design the whole class hierarchy differently.
You can write a shortcut function to handle personalizing methods.
def personalize(*methodNames)
old_init = instance_method(:initialize)
klass = self
modul = Module.new {
methodNames.each { |m|
define_method(m, klass.instance_method(m)) if klass.method_defined?(m)
}
}
methodNames.each { |m|
remove_method(m) if method_defined?(m)
}
define_method(:initialize) { |*args|
# I don't like having to check instance_of?, but this is the only way I
# could thing of preventing the extension of child classes. At least it only
# has to happen once, during initialization.
extend modul if instance_of?(klass)
old_init.bind(self).call(*args)
}
self
end
class A
def foo
"foo"
end
end
class B < A
def foo
"bar"
end
def bam
'bug-AWWK!'
end
personalize :foo, :bam, :nometh
end
class C < B
end
a=A.new; b=B.new; c=C.new
a.foo #=> "foo"
b.foo #=> "bar"
b.bam #=> "bug-AWWK!"
c.foo #=> "foo"
C.instance_method(:foo) # => #<UnboundMethod: C(A)#foo>
c.bam #throws NoMethodError
Sometimes you don't really want an "is a" (inheritance) relationship. Sometimes what you want is "quacks like a." Sharing code among "quacks like a" classes is easily done by using modules to "mix in" methods:
#!/usr/bin/ruby1.8
module BasicFoo
def foo
"foo"
end
end
class A
include BasicFoo
end
class B
def foo
"bar"
end
end
class C
include BasicFoo
end
p A.new.foo # => "foo"
p B.new.foo # => "bar"
p C.new.foo # => "foo"

Resources