STI subclassing

Hi, all. I'm wrestling with subclassing models in AR. Wondering if i'm running up against a limitation on sub-subclassing, or if the syntax has gone awry here.

My app has BlogPosts, which are a sub of Articles. It's got Writers < RegisteredUsers < Users.

When I try to call registered_user on a BlogPost, I get nil.

Any & all help appreciated.

class Article < ActiveRecord::Base   etc....

end

class BlogPost < Article   validates_presence_of :section_id, :title, :body   belongs_to :registered_user

end

class NewsArticle < Article belongs_to :writer end

require "digest/md5" class User < ActiveRecord::Base etc... end

class RegisteredUser < User    validates_uniqueness_of :name    has_many :comments    has_many :events, :class_name => "Article", :foreign_key => "user_id"    has_many :blog_posts, :class_name => "Article", :foreign_key => "user_id" end

class Writer < RegisteredUser   has_many :news_articles, :class_name => "Article", :foreign_key => "user_id" end

@post=BlogPost.find(:first, :conditions=>'user_id=1')

=> #<BlogPost id: 505, title: "Cubs outlook", type: "BlogPost", user_id: 1>

User.find(1)

=> #<Writer id: 1, type: "Writer">

@post.registered_user

=> nil

magic_hat wrote:

My app has BlogPosts, which are a sub of Articles. It's got Writers < RegisteredUsers < Users.

When I try to call registered_user on a BlogPost, I get nil.

Any & all help appreciated.

class Article < ActiveRecord::Base   etc....

end

class BlogPost < Article   validates_presence_of :section_id, :title, :body   belongs_to :registered_user

end

class NewsArticle < Article belongs_to :writer end

require "digest/md5" class User < ActiveRecord::Base etc... end

class RegisteredUser < User    validates_uniqueness_of :name    has_many :comments    has_many :events, :class_name => "Article", :foreign_key => "user_id"    has_many :blog_posts, :class_name => "Article", :foreign_key => "user_id" end

class Writer < RegisteredUser   has_many :news_articles, :class_name => "Article", :foreign_key => "user_id" end

@post=BlogPost.find(:first, :conditions=>'user_id=1')

=> #<BlogPost id: 505, title: "Cubs outlook", type: "BlogPost", user_id: 1>

User.find(1)

=> #<Writer id: 1, type: "Writer">

@post.registered_user

=> nil

You should instead have:

belongs_to :registered_user, :foreign_key => :user_id

A Rails bug meant that you didn't get an error message.

That works. Thanks.

One other issue that's cropping up.

The STI inheiritance has some weird loading behavior. Writers are not found with RegisteredUser.find until after they've been loaded with Writer.find:

RegisteredUser.find_by_name('John Doe')

=> nil

Writer.find_by_name('John Doe')

=> #<Writer id: 1, name: "John Doe">

RegisteredUser.find_by_name('John Doe')

=> #<Writer id: 1, name: "John Doe">

Any idea what that's all about?

magic_hat wrote:

The STI inheiritance has some weird loading behavior. Writers are not found with RegisteredUser.find until after they've been loaded with Writer.find:

RegisteredUser.find_by_name('John Doe')

=> nil

Writer.find_by_name('John Doe')

=> #<Writer id: 1, name: "John Doe">

RegisteredUser.find_by_name('John Doe')

=> #<Writer id: 1, name: "John Doe">

Any idea what that's all about?

Yes, STI subclasses aren't added to find conditions until they're loaded.

One way to force loading of subclasses is to call require_dependency at the bottom of model files. e.g. require_dependency 'writer'.

Another way is to name each leaf STI class in environment.rb, which forces loading of all classes up the tree.

You may have to use method 2 for production mode and method 1 for other modes.

There's also this plugin, which I haven't yet tried:    http://agilewebdevelopment.com/plugins/has_types