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.