Project

detailed

0.0
No commit activity in last 3 years
No release in over 3 years
An ActiveRecord module giving you the power of multiple table inheritance while still resorting to an API resembling single table inheritance
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies
 Project Readme

detailed

Compromise between single and multiple table inheritance with ActiveRecord. This gem doesn't depend on Rails at all (that's, in fact, how it originated).

With this gem you can have a table hierarchy like this:

user (id, type (class), login, password)
|- user_client_details (client_id, billing_address, phone_number)
|- user_worker_details (worker_id, wage, bank_account)
`- user_supplier_details (supplier_id, company_name)

And models hierarchy like this:

ActiveRecord::Base
|- User
|  |- Client
|  |- Worker
|  `- Supplier
|- UserWorkerDetail
|- UserClientDetail
`- UserSupplierDetail

All detail classes would look like this (nothing more is needed):

UserWorkerDetail < ActiveRecord::Base
  belongs_to :worker
end

The main class should be in this form:

class User < ActiveRecord::Base
  # ... your code here ...

  include Detailed
end

The subclasses should be in this form:

class Client < User
  request_details

  # ... your code here ...
end

To add this package to your environment, add the following line to your Gemfile:

gem "detailed"

This gem started from this post on StackExchange: http://stackoverflow.com/a/1634734/3256901 .

Access to detailed properties

With the tableset above, we can access our variables directly, like

client = new Client
client.login = "teh1234"              # a field of user
client.billing_address = "Zgoda 18/2" # a field of user_client_details
client.save

Be aware, that you can't issue find/where with extended fields at the current time. You need to resort to the regular:

Client.find("details_of_client.billing_address" => "Zgoda 18/2")

Subclasses' associations

Your subclasses can have relations directly, without touching the detail models (even though their tables will carry the foreign keys).

Let's suppose, that your Worker has one avatar...

class Worker < User
  ...
  request_details
  has_one :avarar
  ...
end

On the way back, this is a bit more complicated:

class Avatar < ActiveRecord::Base
  ...
  belongs_to :worker, foreign_key: "user_worker_details.avatar_id"
  ...
end

Please be aware, that the "dot notation" of the foreign key is an extension to ActiveRecord provided by this gem.

NB.: Other sorts of associations than has_one are currently untested.

N+1 query problem

This is an optimization problem. When you want to list all your users, casting them to appropriate models and fetching their details, you can do:

User.all

Unfortunately, this method causes you to do N+1 queries (one for all users, and each another one for details). This is because ActiveRecord doesn't know a class of a given record in advance.

On the other hand, you can instruct ActiveRecord, that all details will be needed by issuing this call:

User.all_with_details

ActiveRecord will now run 1+m queries, where m is the number of subclasses, so for our users it will be 4 queries.

Subclasses without details

Sometimes you are going to subclass the main model without the need for additional fields. This is easy, you just do

class BasicUser < User
end

No further code needed, detailed won't go into your way.

Subclasses of subclasses

This is currently not tested nor supported, though possible with small changes to this code. Patches are welcome.

License

This code is licensed under the MIT license. See file COPYING for details.