Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is ruby partial class?

Tags:

ruby

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?

like image 861
zgcharley Avatar asked Jul 27 '14 06:07

zgcharley


4 Answers

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)

like image 152
Uri Agassi Avatar answered Oct 17 '22 02:10

Uri Agassi


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.

like image 35
Aleksei Matiushkin Avatar answered Oct 17 '22 01:10

Aleksei Matiushkin


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

  1. Amend the class definition of A if A already exists or create a new class if it doesn't.
  2. Amend the class definition of 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".)
  3. Amend the class definition of A (since A is already defined).
  4. Amend the class definition of 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).

like image 4
Jörg W Mittag Avatar answered Oct 17 '22 01:10

Jörg W Mittag


# 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
like image 2
Yousuf Memon Avatar answered Oct 17 '22 02:10

Yousuf Memon