I seem to recall some people asking about this, so I figured I'd share what 
I've been working on this weekend. Some notes:

- this has *not* been extensively tested. It works in my app, but it's not 
nearly well-baked enough to use without a good understanding of what to do when 
it breaks.

- it bends some of the rules for ActiveRecord security (notably, by exposing 
the inheritance_column attribute to mass-assignment). Not a big deal in my app, 
as it's invite-only and primarily for internal use. You may need to further 
examine the consequences of adding this if you've got a public-access app in 
the wild.

The goal here is to allow a single model_controller to CRUD objects of many 
subclasses. In my case, it's FeedController managing a bunch of subclasses that 
handle the particulars of different data sources (RSS, Flickr, etc). There are 
three issues with this setup in stock 1.x Hobo:

- the model router doesn't think subclasses are linkable

- object_url won't return the correct URL even if you coerce it into making one

- field-list won't display the inheritance_column, even if you tell it to

Here's what you'll need to change:

- the ModelRouter needs to do a recursive lookup of superclasses to make the 
right decision. This initializer should change that:

module Hobo
  class ModelRouter
    def self.linkable?(klass, action, options={})
      return false if klass == ActiveRecord::Base || klass == Object
      options[:method] ||= :get
      @linkable.member?(linkable_key(klass, action, options)) || 
linkable?(klass.superclass, action, options)
    end
  end
end

Essentially, if a model isn't linkable as-is we traverse the superclass chain 
until either it is or we run out of classes. The relevant method is in a 
different spot (Hobo::Routes) for 1.3, and may require some additional changes 
(@linkable has changed to @linkable_keys, for instance).

- to fix object_url, we need to add some code to the subclasses. Here's an 
example:

class RssFeed < Feed
  # don't say hobo_model here or you'll be sorry :)

  def to_url_path
    "#{self.class.superclass.to_url_path}/#{to_param}" unless new_record?
  end

  def self.to_url_path
    "#{superclass.name.underscore.pluralize}"
  end

end

These two functions are used to construct the URL path, and (by default) would 
build URLs like /rss_feeds/:id, which won't help. This is *not* particularly 
general (will fail if classes are nested more deeply) but will likely be 
sufficient for 95% of STI use-cases. DO NOT lift this into the superclass 
as-is, as you'll break URL generation.

- to make the create form behave correctly, we need to get field-list to show 
the type field. By default, this is protected by ActiveRecord and even 
declaring it attr_accessible won't help. In the parent class (Feed, in this 
case) add:

class Feed < ActiveRecord::Base
  hobo_model
   ...
  protected

  def attributes_protected_by_default
    super - ['type']
  end

end

You'll also likely want this permission method, also in Feed:

  def update_permitted?
    acting_user.administrator? && none_changed?(:type)
  end

As *editing* the type field post-creation is not typically a sensible operation.

Finally, you'll need to extend the form to show this field:

<extend tag="form" for="Feed">
  <old-form merge>
    <field-list: fields="type, ...more fields...">
      <type-view:>
        <select-input options="&Feed::FEED_TYPES" first-option="Select type" 
first-value="" />
      </type-view:>
    </field-list:>
  </old-form>
</extend>

I ended up having to add FEED_TYPES to explicitly declare the subclasses, as 
Feed.send(:subclasses) wasn't reliably returning the right answers in 
development mode (probably a reloading issue). FEED_TYPES is declared as a 
simple array of class names. Your application may want a fancier select menu.

Anyways, I'd love to get feedback from anybody who ends up using this - there's 
still some hard work ahead to figure out how to include this capability in the 
plugin without the gyrations shown here.  I'm also interested in any thoughts 
on the API we should use; maybe an option to model_controller to activate this 
behavior?

--Matt Jones

-- 
You received this message because you are subscribed to the Google Groups "Hobo 
Users" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/hobousers?hl=en.

Reply via email to