I read that the code snippet below implements what is called partial class:
class A
def a
puts 'a'
end
end
class B < A
def ba1
puts 'ba1'
end
end
class A
def b
puts 'b'
end
end
class B < A
def ba2
puts 'ba2'
end
end
ob = B.new
ob.a
ob.b
ob.ba1
ob.ba2
There are two classes B
extended from two classes A
. I don't understand how ruby could know which A
the B
is extend from. When creating an instance of B
, how does ruby know which B
it is? The execution result is:
a
b
ba1
ba2
The initialized B
is an instance of two B
classes. Can someone explain this?
When you write a class in Ruby which is already declared, you do not override the class, but rather patch it.
This means that ruby does not forget the older definition, but rather add upon it.
There is only one B
class - it is the compound code in both class B
blocks.
For example:
class A
def a
puts 'a'
end
end
a = A.new
a.public_methods(false)
# => [:a]
class A
def b
puts 'b'
end
end
a.public_methods(false)
# => [:a, :b]
In this example, the class A
is changed after the instance of a
is created, but, as you can see, it knows that a new method was added (public_methods
lists the available methods for the instance).
There are things you cannot do though, for example - change the parent class of a class. If we try to take the above code, and declare another block:
class A < String
def c
puts 'c'
end
end
# TypeError: superclass mismatch for class A
You get an error saying you are trying to change the base class, which is impossible (if in the initial declaration there is no inheritance declaration, the class implicitly inherits from Object
)
There is the only B
class, inherited from A
. The trick is that you are free to declare B
in different places (which, apparently, permits you to monkeypatch existing classes.)
Once you have class B < A
written, the existing B
is being opened for extending, or a new one is being created, if there is no B
class yet defined.
Hope it helps.
There is no such thing as "partial classes" in Ruby. Partial classes are a feature of C#, not Ruby.
In Ruby, a class definition expression will execute whatever is inside of it in the context of the class. You can have as many class declaration expressions as you want, it doesn't matter. This does allow you to define your class piecewise, even in different files, even in different libraries. It also allows someone else to (re-)define your class later on.
In Python, this is called "monkey patching" and is often frowned upon, the Ruby community has adopted this term but with a neutral meaning, because, well, in Ruby actually all class definition is monkey patching!
So, what you have above will
A
if A
already exists or create a new class if it doesn't.B
if B
already exists or create a new class if it doesn't. (Should B
already exist but have a different superclass that is not A
, this would raise a TypeError "superclass mismatch"
.)A
(since A
is already defined).B
(since B
is already defined). We could have left out the superclass, in which case the existing superclass of B
(i.e. A
) would have been assumed.By the way: technically speaking, A
and B
are not classes. They are variables (more precisely, constants) that refer to classes. Classes are objects just like any other, they can be referenced by variables, passed as arguments to methods, returned from methods etc.
Also, the superclass declaration doesn't have to be a constant, it can be any arbitrary Ruby expression:
class Foo < if rand < 0.5 then String else Array end
end
is perfectly valid (and completely useless ;-) )
class Foo < Bar; end
def qux(sym); const_get(sym) end
class Foo < qux(:Bar); end
is also perfectly valid and will not generate a superclass mismatch, because the constant Bar
and the method call qux(:Bar)
both return the same object (which is a class).
# In Ruby writing
class A
def a; puts 'a' end
end
class A
def b; puts 'b' end
end
# is same as writing
class A
def a; puts 'a' end
def b; puts 'b' end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With