Classes and Constants
Video transcript & code
Let's define a class to represent a point in 2D space.
point = Class.new
It would probably be good if that class had X and Y coordinates.
point = Class.new do attr_reader :x, :y def initialize(x, y) @x = x @y = y end end point.class # => Class
We can instantiate new instances:
p1 = point.new(5,7) # => #<#<Class:0x9fc06f8>:0x9ff789c @x=5, @y=7>
We can also inherit from this new class by passing the parent class to Class.new:
circle = Class.new(point) do attr_reader :radius def initialize(x, y, radius) super(x,y) @radius = radius end end c1 = circle.new(3,5,10) # => #<#<Class:0x974c620>:0x974c5a8 @x=3, @y=5, @radius=10>
Of course, if we want one of these classes to be available outside the current scope, we should assign it to a constant instead of a local variable. Constants, as you know, start with capital letters.
Point = Class.new do attr_reader :x, :y def initialize(x, y) @x = x @y = y end end
OK, so what is all this about? Well, in my experience many novice Ruby programmers don't fully understand is what is happening when a class is declared. What I'm trying to illustrate here is that in Ruby, a class is just an object. And that when we name a class, we're simply assigning that class to a constant, just as we might assign a number or a string to a constant.
UltimateAnswer = 42 Point = Class.new
Now, by convention we reserve camel-cased constants for classes and modules. But functionally there's no difference between an all-caps constant and one that's camel-cased.
Actually, it's not completely true that assigning a class to a constant is exactly the same as assigning any other value to a constant. There is one special rule for classes or modules. The very first time a class object is assigned to a constant, Ruby modifies the class to take the name of the constant it has just been assigned to. We can see that when we assign a class to a local variable, and then assign that variable to a constant, it takes on the name of the constant. Since both the local variable and the constant point to the same object, the change is reflected for both of them. This change of name is a one-time, permanent event. If we assign the class object to another constant, it retains its original name. And even if we reassign the constant it is named for, it still retains the name.
point = Class.new point.name # => nil point # => #<Class:0x98ad7d0> Point = point Point.name # => "Point" Point # => Point point.name # => "Point" point # => Point Point = nil point.name # => "Point"
Normally we don't have to think too hard about stuff like this. But knowing it can help us understand the code we read as well as write. For instance, remember in previous episodes how we've often used
Struct by assigning the result of
Struct.new to a constant?
Point = Struct.new(:x, :y)
That might make a little more sense now.
Of course, in most cases we can just stick with the usual way of declaring classes, using the class keyword. But now when you see it, you'll realize that it's pretty much just a shorthand for creating a new class object and assigning it to a constant.
class Point # ... end
That's it for now. Happy hacking!