Classy Inheritance

"You stay classy, inheritance" - Gibson

Installing

$ sudo gem install classy-inheritance

# in environment.rb add:
require "classy-inheritance" 

What

Classy Inheritance provides two methods:
  • depends_on allows you to define requisite objects.
  • can_be allows you to associate the various ActiveRecord models your polymorphic class can represent.

More functionality coming for optional relationships.

When using depends_on for both polymorphic and belongs_to relationships you will get the following added to your calling class. For instance if you have defined:
1 class User < ActiveRecord::Base
2   depends_on :profile, :attrs => [:first_name, :last_name, :email]
3 end
You will get the following added to the User class:
 1 class User < ActiveRecord::Base
 2   validates_presence_of :profile
 3   validates_associated :profile
 4   before_save :save_requisite_profile
 5 
 6   #...
 7   def save_requisite_profile
 8     self.profile.save
 9   end
10 end

Polymorphic

1 class Picture < ActiveRecord::Base
2   depends_on :image, :attrs => [:filename, :height, :width], :as => "imageable" 
3 end

This will look for "imageable_type" and "imageable_id" on the images table add set them accordingly.

Polymorphic relationships have another feature. The above definition will add two methods to an image object:
 1   # automatically do :include => :image
 2   Picture.find_with_image
 3 
 4   # getters/setters for the attributes defined:
 5   @picture.height
 6   @picture.height = 100
 7   @picture.width
 8   @picture.width = 150
 9   @picture.filename
10   @picture.filename = "/some/path/file.ext" 
11 
12   # return @image.imageable_type == 'Picture'
13   @image.is_a_picture?
14 
15   # return Picture.find_with_image(@image.presentable_id)
16   @image.as_a_picture
17 
In addition, you can define the different types of models a polymorphic class can represent:
1 class Image < ActiveRecord::Base
2   can_be :picture, :as => "imageable" 
3 end
This will give you the following methods:
1   # return @image.imageable_type == 'Picture'
2   @image.is_a_picture?
3 
4   # return Picture.find_with_image(@image.presentable_id)
5   @image.as_a_picture

As you may have guessed, belongs_to will call can_be on the requisite class.

Note: If you are intending on calling Image.find (and you most likely are), it's best to manually add the can_be methods to Image. With class reloading in development mode, you won't get the methods from the Picture.depends_on call.

Belongs_To

You can define a standard belongs_to relationship:
1 class User < ActiveRecord::Base
2   depends_on :profile, :attrs => [:first_name, :last_name, :email]
3 end

Similar to the polymorphic use, you will get pass-through methods for each attribute:

1   @user.first_name
2   @user.last_name
3   @user.email
4   @user.first_name=
5   @user.last_name=
6   @user.email=

This means you can use these attributes on your form and in your controller instead of having to worry about creating/updating a separate Profile model.

For the above example, you'll also get a "find_with_profile" class method that will do the :include => :profile addition to your find call for you.

Additional options

Option: prefix
1 class Bakery < ActiveRecord::Base
2   depends_on :business, :attrs => [:name], :prefix => true
3 end
4 
5 #Will give you:
6 @bakery.business_name
1 class Bakery < ActiveRecord::Base
2   depends_on :business, :attrs => [:name], :prefix => "bakery" 
3 end
4 
5 #Will give you:
6 @bakery.bakery_business_name
Option: postfix
 1 Office.depends_on :billing_address, 
 2                   :attrs => [:line_one, :line_two, :city, :state_code, :postal_code],
 3                   :class_name => "Address", 
 4                   :foreign_key => "billing_address_id", 
 5                   :postfix => true
 6 #Will give you:
 7 @office.line_one_billing_address
 8 @office.line_two_billing_address
 9 @office.city_billing_address
10 @office.state_code_billing_address
11 @office.postal_code_billing_address
 1 Office.depends_on :billing_address, 
 2                   :attrs => [:line_one, :line_two, :city, :state_code, :postal_code],
 3                   :class_name => "Address", 
 4                   :foreign_key => "billing_address_id", 
 5                   :postfix => "billing" 
 6 #Will give you:
 7 @office.line_one_billing
 8 @office.line_two_billing
 9 @office.city_billing
10 @office.state_code_billing
11 @office.postal_code_billing

Option: Standard Rails Options

Introduced with release 0.4.0, you can now add the Rails options provided for the type of association. If you use the :as option, the association is a :has_one, otherwise it's a :belongs_to association.

This means you can do the following:
1 
2 class Document < ActiveRecord::Base
3   depends_on :content, :attrs => [:name], 
4              :as => :presentable, :dependent => :destroy
5   ...
6 end

Options: validates_presence_if, validates_associated_if

You can now use these options to determine whether or not the validates_presence_of and validates_associated methods are called. By default, both methods are called. You can pass in true, false, string/symbol (for methods reference) or a Proc.

Github

http://github.com/stonean/classy-inheritance/tree/master

git://github.com/stonean/classy-inheritance.git

Contact

Please use the forum to ask questions and the issue tracker to report problems or submit a pull request.

License

This code is free to use under the terms of the MIT license.

Copyright (c) 2008 Andrew Stone

Also available in: HTML TXT