0.12
No release in over 3 years
Low commit activity in last 3 years
This 'acts_as' extension provides multi-table inheritance for rails models.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

Runtime

 Project Readme

<img src=“https://badge.fury.io/rb/acts_as_relation.svg” alt=“Gem Version” /> <img src=“https://travis-ci.org/hzamani/acts_as_relation.svg?branch=master” alt=“Build Status” /> <img src=“https://coveralls.io/repos/hzamani/acts_as_relation/badge.png?branch=master” alt=“Coverage Status” /> <img src=“https://codeclimate.com/github/hzamani/acts_as_relation.png” />

Deprication¶ ↑

This gem is depricated, please use {active_record-acts_as}(github.com/hzamani/active_record-acts_as) which is a full refactor of this one.

ActsAsRelation¶ ↑

This gem helps implement multiple-table-inheritance (MTI) methods to your ActiveRecord models. By default, ActiveRecord only supports single-table inheritance (STI). MTI gives you the benefits of STI but without having to place dozens of empty fields into a single table.

Take a traditional e-commerce application for example… A product has common attributes (name, price, image …), while each type of product has its own attributes… pen has color, book has author and publisher and so on.

Installation¶ ↑

For Rails 4 installation add the following line to your Gemfile

gem 'acts_as_relation', '~> 1.0'

and

$ bundle install

If you are using Rails 3 you must use ‘~> 0.1’ version specifier in Gemfile.

Usage¶ ↑

acts_as_relation uses a polymorphic has_one association to simulate multiple-table inheritance. For the e-commerce example you would declare the product as a supermodel and all types of it as acts_as :product (if you prefer you can use their aliases is_a and is_a_superclass)

class Product < ActiveRecord::Base
  acts_as_superclass
end

class Pen < ActiveRecord::Base
  acts_as :product
end

class Book < ActiveRecord::Base
  acts_as :product
end

class Store < ActiveRecord::Base
  has_many :products
end

To make this work, you need to declare both a foreign key column and a type column in the model that declares superclass. To do this you can set :as_relation_superclass option to true on products create_table (or pass it name of the association):

create_table :products, :as_relation_superclass => true do |t|
  # ...
end

Or declare them as you do on a polymorphic belongs_to association, it this case you must pass name to acts_as and acts_as_superclass in :as option:

change_table :products do |t|
  t.integer :producible_id
  t.string  :producible_type
end

class Product < ActiveRecord::Base
  acts_as_superclass :as => :producible
end

class Pen < ActiveRecord::Base
  acts_as :product, :as => :producible
end

class Book < ActiveRecord::Base
  acts_as :product, :as => :producible
end

Now Pen and Book act as Product. This means that they inherit Product attributes, associations, validations and methods.

To see its functionality lets add some stuff to product:

class Product
  validates_presence_of :name, :price

  def to_s
    "#{name} $#{price}"
  end
end

now we can to things like this:

Pen.create :name => "Nice Pen", :price => 1.3, :color => "Red"
Pen.where "name = ?", "Some Pen"
pen = Pen.new
pen.valid?      # => false
pen.errors.keys # => [:name, :price]
Pen.first.to_s  # => "Nice Pen $1.3"

When you declare an acts_as relation, the declaring class automatically gains parent methods (includeing accessors) so you can access them directly.

On the other hand you can always access a specific object from its parent by calling specific method on it:

Product.first.specific # will return a specific product, a pen for example

In has_many case you can use subclasses:

store = Store.create
store.products << Pen.create
store.products.first # => <Pen: ...>

Options¶ ↑

The acts_as relation support these options:

  • :as

  • :auto_join

  • :class_name

  • :dependent

when :auto_join option set to true (which is by default), every query on child will automatically joins the parent table. For example:

Pen.where("name = ?", "somename")

will result in the following SQL:

SELECT "pens".* FROM "pens" INNER JOIN "products" ON "products"."as_product_id" = "pens"."id" AND "products"."as_product_type" = 'Pen' WHERE (name = 'somename')

All other options are same as has_one options.

Note that support for :conditions and :include has been removed. In their place, you can use +where()+ and +includes()+:

acts_as :product, -> { where(color: "yellow") }
acts_as :person, -> { includes(:friends) }