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:

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.