Discreet Cosine Transform

A few thoughts on ruby, video and other things

Python, Ruby and Dart Part 2: Find All Sublclasses

Continuing on with my previous post, I have found it useful sometimes to be able to programmatically discover all the classes that inherit from a specific class. I have used this as a light form of Inversion of Control where I autodiscover all the objects that implement a specific interface. Its particularly useful if you don’t care what order you then call each object: for instance running a bunch of diagnostic tests on a system (and it doesn’t matter the order the tests are run in), or loading up a bunch of filters for log lines that can be tested against a line in any order.

Ruby

This is easily done in Ruby with the ObjectSpace class, the example below would find every class that inherits from a class named Job, then instantiate an object from it and add it to an array:

1
2
3
4
5
6
7
all_jobs = []

ObjectSpace.each_object(Class) do |possible_job|
  if possible_job.superclass == Job then
      all_jobs << possible_job.new
  end
end

A Note on Namespaces

If the classes you are working with are inside modules, when using modules as namespaces in Ruby, you would indeed need to look for them via the Module::Class syntax (which is obvious, or else what good would the namespace do you, but I felt the need to mention it here since Python also has some particulars when it comes to namespacing).

A Note on Later Generations

This example would not find generations beyond the first. You could fairly easily adapt a recursive form of it that would. For my example use cases above, that is just not important.

Python

I am using the following form in Python, but I am not 100% sure yet this is what everyone would agree is the preferred form. I gather that some built-in methods like __subclasses__ are sometimes not preferred over other forms added to the language later.

Anyway, this works, basically the same example as above:

1
2
3
4
all_jobs = []

for cls in globals()['Job'].__subclasses__():
    all_jobs.append(cls())

However, depending on if you are doing this inside a module or outside of a module, you might want a different global function than globals(). There are a few options that look like they differ based on your current scope and intent.

Dart

Dart has a complete reflection API in dart:mirrors, and after a little reading about it (and some help from stack overflow of course, though looks like a few parts of this answer are now changed in Dart), I was able to piece together the code below. Note this example is a bit more complete (it shows the class declarations) because I wanted something you could actually run in the dart interpreter (again, with no cli REPL, its a little harder to just try things out).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import 'dart:mirrors';

class Job{}
class CheckRam extends Job{}
class CheckDrive extends Job{}

main() {
  final ms = currentMirrorSystem();
  var all_jobs = [];

  ms.isolate.rootLibrary.declarations.forEach((symbol, declarationMirror) {
      if (declarationMirror is ClassMirror) {
          final parentClassName = MirrorSystem.getName(declarationMirror.superclass.simpleName);
          if (parentClassName == 'Job') {
              all_jobs.add(declarationMirror.newInstance(const Symbol(''), []).reflectee);
          }
      }
  });

  print(all_jobs);


}

There was quite a bit I was not familiar with myself here. One was working with dart’s Symbol class, which is familiar from Ruby but unfortunately is implemented as a class rather than a type so you need some extra syntax to work with them. Also, that newIntance returns an ObjectMirror not the object itself (though that is solved with the reflectee property).

This example will look in the root library, as above in both Ruby and Python, if you intend to search only a specific library it would take some modification to do so.

Comments