Determining All of the Descendents of a Ruby Class

A Real World Example to Help Understand a Data Model and Possible Discrepency

In one of our projects, we have several different models that have slightly different properties. We have a :part and a :part_of. On the surface, we might be thinking of these as the same thing.

However according to their Resource Description Framework (RDF 📖) definition they are not the same property. The :part represents the property described by http://id.loc.gov/ontologies/bibframe/part. Whereas the :part_of represents the property described by http://id.loc.gov/ontologies/bibframe/partOf. Those Uniform Resource Locators (URLs 📖) are meant to be a pointer to the conceptual data dictionary entry.

The classes I cared about were all descendants of ActiveFedora::Base. See https://github.com/samvera/active_fedora for more details on that base class.

Below is the list of descendants:

subclasses = ActiveFedora::Base.descendants
#  [
#    GenericWork,
#    Image,
#    DogBiscuits::Work,
#    DogBiscuits::ConferenceItem,
#    ConferenceItem,
#    DogBiscuits::Dataset,
#    Dataset,
#    DogBiscuits::ExamPaper,
#    ExamPaper,
#    DogBiscuits::JournalArticle,
#    JournalArticle,
#    DogBiscuits::PublishedWork,
#    PublishedWork,
#    DogBiscuits::Thesis,
#    Thesis,
#    FileSet,
#    Collection
#  ]

To determine which ones used the part property, I ran the following:

subclasses.select { |k| k.properties.key?("part") }
#   [PublishedWork]

And likewise for the part_of property:

klasses.select { |k| k.properties.key?("part_of") }
#   [
#     GenericWork,
#     Image,
#     ConferenceItem,
#     JournalArticle,
#     Thesis,
#     Collection
#   ]

Most interesting to me is that there is no intersection between models that have the part_of and the part properties. Now, I wonder if this is intentional or a subtle bug in data modeling? In my experience, I give it 50/50 odds.

Postscript

When I first started writing this post, I remembered the .subclasses method. I wrote the following recursive Ruby 📖 function:

def descendants_of(klass)
  klass.subclasses.map do |subklass|
    [subklass, descendants_of(subklass)]
  end.flatten.compact
end

And then, as I was reviewing this post, I thought “Wait, there’s probably already a method that does this.” The descendants method already existed.

I include the above descendants_of as a means demonstrating a recursive function.

Post-postscript

At my first job, we made heavy usage of data dictionaries and entity relationship diagrams. We went through a rigorous modeling of data. This was all within a “closed” space for data; though one that began opening and broadening as we looked at digital integrations between providers and regulatory agencies.

Libraries sit in a different place, with a goal of sharing information publicly. Which, in its aspirational form means to describe things in a consistent manner. One of those manners is via RDF. On the surface, it looks brilliant. We use a URL to describe the meaning of the “field”. With the goal being that when two objects from different origins have a property with the same URL we can assume that “field” describes the same conceptual range.

But there are many different URLs that can be chosen to describe a “field” in similar manners. Also, each person’s bias in interpretting the meaning of a “field” can introduce drift from the intended/desired universality of the meaning of the “field.”

Assuming many folks of different perspectives can and will describe things consistently seems to be analogous to the high school Physics caveat “Assuming no wind resistance nor friction…”