Looking for inverse of base_class (or other elegant solution)

Wondering if any of the AR experts out there can tell me if there's an inverse to base_class, i.e. what subclasses, if any a class has. Some of the classes I'm dealing with use STI. I could check to see if there's a type column, and then query that column to get the subclass names, but... eeeurgh! There's got to be a better way.

Tx
CT

Chris T wrote:

Wondering if any of the AR experts out there can tell me if there's an inverse to base_class, i.e. what subclasses, if any a class has. Some of the classes I'm dealing with use STI. I could check to see if there's a type column, and then query that column to get the subclass names, but... eeeurgh! There's got to be a better way.

Tx
CT

Just to answer my own post, have found a solution, in case anyone else needs it. There's an undocumented method in ActiveSupport called subclasses_of(class) that returns an array of the subclasses of a class if there are any; an empty array otherwise. It's a method of Object, so you just call it straight without a receiver, e.g. @subclasses = subclasses_of(MainClass).
Obviously as it's not in the public API there's no guarantee that it won't changed, but, hey, that's what we've got tests for, isn't it?

Hope this helps someone else...
CT

Out of curiosity, what drives this need of yours?

Hey Chris-

  Yeah subclasses_of will work for what you want but the ugly db query might be faster. subclasses_of uses ObjectSpace to iterate over all in memory objects and find the subclasses of the class you passed in which can be very expensive if you have a lot of objects in ObjectSpace(which rails does). It woudl be worth testing for but if it won't be used a lot in your app the by all means use the subclasses_of

-Ezra

When you're looping through a bunch of models, or sharing templates (and
actions) across models, some of which are vanilla classes, some of which
are base classes for STI subclasses, you need to be able to determine
programmatically whether you are dealing with a vanilla class (in which
case no prob), or an STI base-class, in which case what you need is the
subclasses.

It's complicated a bit more (or rather simplified), because I've got a
RESTified controller per classes, each of which inherits from a
MasterController. The MasterController defines all the actions ( the
usual index, show, new, create, edit, update and destroy) and views
(although they call in class-specific partials), which makes the whole
thing very nice, clean, simple and very DRY.

Works great now I've figured out the subclasses stuff.

Michael Campbell wrote:

Ezra Zygmuntowicz wrote:

Chris T wrote:
    

Wondering if any of the AR experts out there can tell me if
there's an
inverse to base_class, i.e. what subclasses, if any a class has. Some
of the classes I'm dealing with use STI. I could check to see if
there's a type column, and then query that column to get the subclass
names, but... eeeurgh! There's got to be a better way.

Tx
CT

Just to answer my own post, have found a solution, in case anyone else
needs it. There's an undocumented method in ActiveSupport called
subclasses_of(class) that returns an array of the subclasses of a
class
if there are any; an empty array otherwise. It's a method of
Object, so
you just call it straight without a receiver, e.g. @subclasses =
subclasses_of(MainClass).
Obviously as it's not in the public API there's no guarantee that it
won't changed, but, hey, that's what we've got tests for, isn't it?

Hope this helps someone else...
CT
    
Hey Chris-

  Yeah subclasses_of will work for what you want but the ugly db query
might be faster. subclasses_of uses ObjectSpace to iterate over all
in memory objects and find the subclasses of the class you passed in
which can be very expensive if you have a lot of objects in
ObjectSpace(which rails does). It woudl be worth testing for but if
it won't be used a lot in your app the by all means use the
subclasses_of

-Ezra

>

Thanks Ezra, I'll bear that in mind. At the moment I'm only using it for
new and edit, which isn't a prob, but was thinking of using it for some
of the main pages too. Hmm -- maybe need to re-think that. Don't want to
prematurely optimise, but...

Actually, thinking about it, the app class structure isn't going to
change once the app's been started, and when it does change, (in
production at least), the app will be restarted. So maybe I could
generate the class structure when the app's first run and bung it in a
constant. Makes sense?

Cheers
CT
p.s. Got a rough ETA on the book. About to enter the world of
deployment. Scary stuff!

This will fail if the model class hasn’t been loaded. However, your class-per-controller setup would permit something like

def model_class
self.class.name.sub(/Controller$/, ‘’).constantize
end
helper_method :model_class

jeremy

Jeremy Kemper wrote:

    When you're looping through a bunch of models, or sharing
    templates (and
    actions) across models, some of which are vanilla classes, some of
    which
    are base classes for STI subclasses, you need to be able to determine
    programmatically whether you are dealing with a vanilla class (in
    which
    case no prob), or an STI base-class, in which case what you need
    is the
    subclasses.

    It's complicated a bit more (or rather simplified), because I've
    got a
    RESTified controller per classes, each of which inherits from a
    MasterController. The MasterController defines all the actions ( the
    usual index, show, new, create, edit, update and destroy) and views
    (although they call in class-specific partials), which makes the
    whole
    thing very nice, clean, simple and very DRY.

    Works great now I've figured out the subclasses stuff.

This will fail if the model class hasn't been loaded. However, your
class-per-controller setup would permit something like
  def model_class
    self.class.name.sub(/Controller$/, '').constantize
  end
  helper_method :model_class

jeremy

>

Aren't all the model classes load when the app starts? Actually, though,
I can improve things using your code about (though have to singularize
the names, as the controller names are plural versions of the model
names). I'd got a simple model_name method defined in each subcontroller
to define the model name, but you're right, I could DRY it up further
and just infer this from the controller name in the MasterController.
Thanks for the tip.
CT

Jeremy Kemper wrote:

This will fail if the model class hasn’t been loaded. However, your
class-per-controller setup would permit something like
def model_class
self.class.name.sub(/Controller$/, ‘’).constantize

end
helper_method :model_class

Aren’t all the model classes load when the app starts?

Model classes are loaded when first referenced.

Actually, though,
I can improve things using your code about (though have to singularize
the names, as the controller names are plural versions of the model
names). I’d got a simple model_name method defined in each subcontroller

to define the model name, but you’re right, I could DRY it up further
and just infer this from the controller name in the MasterController.

Cool.
jeremy

I've been playing with what I think is some really fun stuff in that
vein for some weeks now. I have most of it bundled in a plugin called
resourcey_utils which you can find here:
http://sethrasmussen.com/svn/rails/plugins

There are some handy before_filters you can use with these same
assumptions we're discussing to prepare a new resource instance, or
collection, etc. They are pretty generic, but like I say, fun and onto
something, methinks. I'm sure I'll enhance it as time goes on. Using
these conventions, I've whittled a resource crud controller down to
something like this:

class BlogPostTypesController < ApplicationController
  before_filter :new_resource, :only => %w(new create)
  before_filter :find_resource, :only => %w(edit update destroy)
  before_filter :paginate_resource, :only => :index

  def create
    if create_resource
      redirect_back_or_default(blog_post_types_url)
    else
      render :action => :new
    end
  end

  def edit
    render :action => 'new'
  end
  def update
    if update_resource
      redirect_back_or_default(blog_post_types_url)
    else
      render :action => 'edit'
    end
  end

  def delete
    destroy_resource
    redirect_back_or_default(blog_post_types_url)
  end
end

If your controller could stay that generic, you could even abstract all
of those remaining methods into one or two class level calls or perhaps
something that is defined dynamically. I'll probably work on adding
that, even if just for fun, in the near future. :slight_smile: I am thinking about
creating some class methods to wrap before filters that create and fine
a resource for the appropriate cruddy actions, too, though this is
another case where standardizing a slightly less assuming approach
might be good.

All in all, making certain assumptions when you can definitely kicks
ass and makes for clean code that feels like a warm hug to read.

Seth Thomas Rasmussen wrote:

---snip-----

All in all, making certain assumptions when you can definitely kicks
ass and makes for clean code that feels like a warm hug to read.

+1 for that. Coming from a non-programming background, it's prob taking me a while to get there, but I'm enjoying the ride. I'm finding the REST stuff is forcing me to think more structurally and therefore write cleaner, better code.