As a little hobby project, I'm trying to build up my own object system. I was wondering if there is a way of changing the default inheritance of all classes from Object to my base class Entity, so that whenever I create a new class I don't have to explicitly say class Thing < Entity; ideally, I would just be able to say class Thing and have its default superclass be my Entity class.
Sure you could do this by modifying the relevant part of the Ruby source and recompiling Ruby:
VALUE
rb_define_class_id(ID id, VALUE super)
{
VALUE klass;
if (!super) super = rb_cObject; // <-- where the default is set
klass = rb_class_new(super);
// ...
But that’s a huge hassle and requires patching and running a custom Ruby and probably has a lot of gotchas and things that are hard-coded to assume Object is the default.
And, on top of that, what’s the point? If you replace Object with something else as the default superclass, every class—including those in Ruby core—will now inherit from this new default superclass. You could get the same effect (just without the different name) far more easily and without needing a custom Ruby by just changing Object itself. That’s the beauty of being able to reopen classes! For example:
class Object
def foo
'bar!'
end
end
class A; end
A.new.foo #=> 'bar!'
If you wanted to be kind you might even just put all the relevant methods in an Entity module instead of a class and then include it into Object.
No, unfortunately this is not possible in Ruby. Ruby does not have a Meta-Object Protocol like e.g. CLOS that would allow you to manipulate the core semantics of the Object Model. It would be nice, though!
Related
Coming from C# world I am used to thinking classes are immutable definitions of objects and that every object has fixed class.
I am trying to open my mind to possibilities of using
class << some_object
def something_unique_to_this_object
# code
end
end
I am not talking about class << self.
I am talking about changing one or several object's class definition, but not all of them like class << self does.
In several months or almost a year of using ruby I have never found a situation when I thought oh good I can open this objects eigenclass and change it and leave majority of other objects of same class unchanged. Please provide real world examples when you used this.
You say "not like class << self". Well, guess what - class/module methods are implemented exactly this way. There is a reason for the similarity in syntax. self inside a class definition refers to the class itself. Any class/module methods that you define are actually methods of the eigenclass of that class/module. Your specific class is just one instance of the class Class.
For other examples, look at something like rspec. How would you implement a double and add some methods to it dynamically? How would you stub a method of an existing object? Eingenclasses are an easy and perfect fit for it.
Other than more meta uses, I also sometimes find it comfortable while debugging. Like I can put a breakpoint, alter the behaviour of some object and continue after the breakpoint to see what happens. You might not want to affect all objects of that class or the object might be an instance of an anonymous class.
I was thinking wouldn't it be cool to have a print method defined in the Ruby Object class? Consider the following:
class Object
def print
puts self.to_s
end
end
23.times &:print
Is there any issue in having something like this? Seems like a good feature to have. It also appears easy to read.
There's already Object#inspect defined. Plus, there's already Kernel#print defined as private method in Object class and every class that inherits from it.
This method already exists in the Ruby standard library. However, it has a different name: display.
23.times &:display
# 012345678910111213141516171819202122
As you can see, it does not write a newline after the object's string representation; it is ill-suited for object inspection.
The main issue with adding methods to Object is that they become universal and may clash with similarly named methods in other libraries or in your project.
There are already multiple simple ways to output data or convert to string form in Ruby core, so the risk of a clash (on a very useful method name) likely outweighs any benefits from nicer syntax even in your own code.
If you have a smaller set of classes in your own project, where you feel this would be a useful feature to have, then this is an ideal use case for mix-ins.
Define a module:
module CanPrintSelf
def print
puts self.to_s
end
end
And include it in any class you want to have the feature:
class MyClass
include CanPrintSelf
end
my_object = MyClass.new
my_object.print
So you can have this feature if you like it, and you don't need to modify Object.
Currently reading a Ruby style guide and I came across an example:
def no_op; end
What is the purpose of empty body methods?
There are a number of reasons you might create an empty method:
Stub a method that you will fill in later.
Stub a method that a descendant class will override.
Ensure a class or object will #respond_to? a method without necessarily doing anything other than returning nil.
Undefine an inherited method's behavior while still allowing it to #respond_to? the message, as opposed to using undef foo on public methods and surprising callers.
There are possibly other reasons, too, but those are the ones that leapt to mind. Your mileage may vary.
There may be several reasons.
One case is when a class is expected to implement a specific interface (virtually speaking, given that in Ruby there are no interfaces), but in that specific class that method would not make sense. In this case, the method is left for consistency.
class Foo
def say
"foo"
end
end
class Bar
def say
"bar"
end
end
class Null
def say
end
end
In other cases, it is left as a temporary placeholder or reminder.
There are also cases where the method is left blank on purpose, as a hook for developers using that library. The method it is called somewhere at runtime, and developers using that library can override the blank method in order to execute some custom callback. This approach was used in the past by some Rails libraries.
This class takes in a hash, and depending on the input, it converts temperatures.
class Temp
def initialize(opt={})
if opt.include?(:cold)
#colddegree=opt[:cold]
end
end
def self.from_cold(cel)
Temp.new(:cold => cel) <= instance of class created in class method
end
end
An instance of a class is created inside a class method. Why is it necessary to do so, and what it does it do, what is the reasoning behind it?
Why would we need to create an instance of a class inside the class instead of the main?
Why would it be used inside a class method? Can there be a time when it would be required inside a regular object methods?
What is it calling and what is happening when it is creating an instance inside a class method? what difference does it make?
Rubyists don't always use the word, but self.from_cold is a factory. This allows you to expose a Temp.from_cold(-40) method signature that programmers consuming your API can understand readily without having to concern themselves with the boilerplate of, say, learning that you have an implicitly required parameter named :cold.
It becomes extra useful when you have a work-performing object that needs to be initialized and then invoked, such as TempConverter.new(cel: -40).to_fahrenheit. Sometimes it's cleaner to expose a TempConverter.cel_to_fahr(-40) option to be consumed by other libraries. It's mostly just a way of hiding complexity inside of this class so that other classes with temp conversion needs don't have to violate the Law of Demeter.
An important thing to understand is that unlike C#, JavaScript, or C++, new is not a keyword in Ruby. It's just a message which objects of class Class understand. The default behaviour is to allocate and initialize a new object of that class, but there's nothing stopping you overriding it, for example:
class Foo
def self.new
puts "OMG i'm initializing an object"
super
end
end
So to answer your last question, it makes no difference where Temp.new is called. In all cases, it sends the message new to the object Temp (which is also a class, but remember that almost everything in Ruby is an object, including classes), which creates and returns a new instance.
I'm not going to attempt to answer your other two questions, because the other answer already does.
I'm reading 'metaprogramming in ruby'
its such an EXCELLENT book. Seriously, it talks about stuff that I never hear mentioned elsewhere.
I have a few specific questions however about objects (I'm in the first few chapters)
I understand that the RubyGems gem installs the method 'gem' to the module Kernel so that it shows up on every object. Is there a reason they didnt put it into the Object class?
He talks about how when ruby looks for the method it always goes right then up. What exactly does 'up' mean? I see it in the diagram, its just that I dont really understand the purpose of 'up'. he doesnt explain that part much.
What is the point of the Object class? How come those methods cant be just placed into Class? If every object belongs to a class (even if its Class), then what is the point of object, basicobject, and kernel?
String, Array, blah blah are obviously an instance of Class. Class is also an instance of itself. So if Class is an instance of Class.... how does it also inherit from Object? Where in the code does it relates to BOTH Class and Object?
I know kernel contains methods such as puts that can be used everywhere, and this relates to question 1, but why cant they just condense it and put it all into Object... where it would seem everything inherits from object anyway
Both would work, but typically methods on Object should only be methods that deal with a particular object. Puting things in the Kernel module are less about about object and more global.
I assume it means "up the inheritance chain". So it looks for the method on the child class, then on that classes parent class until it finds one or runs out of parent classes.
Object is the base class of all objects, naturally (For ruby 1.8 at least). The crazy part is that a class is actually an instance of the Class class. (you follow that?) So adding instance methods to Class would add methods to class objects, but not instances of those classes.
Nearly everything in ruby is an object. Class.superclass is actually Module (which is like a class you can't instantiate) and Module.superclass returns Object. So Class < Module < Object is the inheritance chain if the Class class. (For ruby 1.8 at least)
More convention than anything. Since Object can get rather HUGE, it's customary to put things into modules and then combine those modules later. If the method doesn't deal with an instance of an object directly as self then the method doesn't belong directly in Object. More global non-object instance methods like gem go in the Kernel module to signify that they are simply methods available everywhere.
Some more about class objects and inheritance...
class Foo < Bar
def hi
puts 'Hi!'
end
end
What this does is really quite awesome. It defines a class object, of course. Now this class object is configured to have a name Foo, a parent class Bar and a method hi. This info is sort of like this class object's meta data.
Now the class object Foo itself is an instance of Class. But Foo defines a class that inherits from Bar. The Class class defines a data structure to store this meta data about a class.
You can think of the Class class sorta kinda being defined like this:
class Class < Module
# fictional method called on class creation
def set_meta_data(name, superclass, methods)
#name = name
#superclass = superclass
#methods = methods
end
# fictional way in which an instance might be created
def new
instance = Object.new
instance.superclass = #superclass
instance.addMethods(#methods)
instance
end
end
So a class object itself would inherit from Class but it would create objects that do not.
Thinking of classes as objects can be a bit mind bending in this way, but this also why ruby is awesome.
For 1 and 5, pseudo-keyword commands tend to go into Kernel rather than Object.
For 2, it makes sense for sub-classes to be "down" relative to their parent class (sub literally meaning "beneath"). Therefore if you're heading for a parent class and its ancestors, you have to go "up".
For 3, an object object is not an instance of Class, it is an instance of Object.
For 4, what's wrong with something being an instance of Class and inheriting from Object? All classes inherit from Object.