Tricky Inheritence

78 views
Skip to first unread message

Masc Prem

unread,
Jul 11, 2014, 2:36:32 PM7/11/14
to rubyonra...@googlegroups.com
I have created few models which are shown below.
Base models are TransactionType and TransactionItem
ExpenseType and IncomeType derives from TransactionType.
Expense and Income derives from TransactionItem.

class TransactionType < ActiveRecord::Base
scope :expense_types, -> { where(tran_type: 'ExpenseType') }
scope :income_types, -> { where(tran_type: 'IncomeType') }
self.inheritance_column = "tran_type"
validates :name, uniqueness: true
end

class ExpenseType < TransactionType
end

class IncomeType < TransactionType
end

class TransactionItem < ActiveRecord::Base
validates :note, length: { in: 2..255 }
end

class Expense < TransactionItem
belongs_to :expense_type
validates :expense_type, presence: true
end

class Income < TransactionItem
belongs_to :income_type
validates :income_type, presence: true
end


I can create objects for ExpenseType.
But, it throws error when Expense or Income object created.

<code>
ExpenseType.new(:name => "Grocceries").save!
ExpenseType.new(:name => "Travel").save!
IncomeType.new(:name => "Salary").save!
IncomeType.new(:name => "Bonus").save!
Expense.new(:note => "a soda", :expense_type => ExpenseType.first)

2.1.2 :006 > Expense.new(:note => "a soda", :expense_type =>
ExpenseType.first)
ExpenseType Load (0.1ms) SELECT "transaction_types".* FROM
"transaction_types" WHERE "transaction_types"."tran_type" IN
('ExpenseType') ORDER BY "transaction_types"."id" ASC LIMIT 1
ActiveModel::MissingAttributeError: can't write unknown attribute
`expense_type_id'
from

It would be great if someone shares your idea for solving this.

Thanks,
Masc

--
Posted via http://www.ruby-forum.com/.

Matt Jones

unread,
Jul 12, 2014, 1:50:43 PM7/12/14
to rubyonra...@googlegroups.com
I don't think this is an inheritance problem. What does the schema for your transaction_items table look like? The error message suggests that it *doesn't* have a column `expense_type_id`, which is what `belongs_to :expense_type` is going to be looking for. 

A wild guess: maybe you've got a `transaction_type_id` column instead? In that case, you should use this on `Expense`:

belongs_to :expense_type, foreign_key: :transaction_type_id

(and similar for `:income_type` on `Income`)

--Matt Jones

Masc Prem

unread,
Jul 14, 2014, 4:30:03 PM7/14/14
to rubyonra...@googlegroups.com
Hi Matt,

Schema looks like this.

sqlite> .schema transaction_items
CREATE TABLE "transaction_items" ("id" INTEGER PRIMARY KEY AUTOINCREMENT
NOT NULL, "transaction_type_id" integer, "note" varchar(255),
"transaction_date" datetime, "created_at" datetime, "updated_at"
datetime);

sqlite> .schema transaction_types
CREATE TABLE "transaction_types" ("id" INTEGER PRIMARY KEY AUTOINCREMENT
NOT NULL, "name" varchar(255), "tran_type" varchar(255), "created_at"
datetime, "updated_at" datetime);

There is no specific table for expense_type and income_type.
transaction_types table's tran_type column decides whether it is a
income / expense type.

My idea is to write common code for IncomeType and ExpenseType in it's
base class transaction_type which extends ActiveRecord::Base.

Thanks.

Masc Prem

unread,
Jul 14, 2014, 7:33:31 PM7/14/14
to rubyonra...@googlegroups.com
I have uploaded the source code of model and migration at below link.

https://drive.google.com/file/d/0B6uWxuw822j9c1Z1X2F2eGxCclU/edit?usp=sharing

Anyone can create simple rails application, copy model & migration files
on place.
Run rails console and execute below commands to reproduce error.

ExpenseType.new(:name => "Grocceries").save!
ExpenseType.new(:name => "Travel").save!
IncomeType.new(:name => "Salary").save!
IncomeType.new(:name => "Bonus").save!
Expense.new(:note => "a soda", :expense_type => ExpenseType.first)

Matt Jones

unread,
Jul 15, 2014, 7:55:27 PM7/15/14
to rubyonra...@googlegroups.com
Couple things:

* the code you posted has a single transaction_items table, but two classes (Income and Expense) that both descend from ActiveRecord::Base. That definitely won't work. If you really want a common transaction_items table, you'll need to include a column to put the STI type in and a base class of TransactionItem rather than a module.

* as noted previously, if you have a column on transaction_items called `transaction_type_id` but want to refer to the corresponding association as `expense_type` or `income_type`, you'll need to pass the `foreign_key` option to `belongs_to`.

--Matt Jones

Masc Prem

unread,
Jul 17, 2014, 12:59:30 PM7/17/14
to rubyonra...@googlegroups.com
Hi Matt,

Thanks for your input. Actually I made it working without using STI. Not
sure this is the right way of doing it. But, this is what I wanted.

class TransactionItem < ActiveRecord::Base
validates :note, length: { in: 2..255 }
validates :transaction_date, presence: true
end

class Expense < TransactionItem
belongs_to :expense_type, :class_name => "TransactionType",
:foreign_key => "transaction_type_id"
validates :expense_type, presence: true
end

class Income < TransactionItem
belongs_to :income_type, :class_name => "TransactionType",
:foreign_key => "transaction_type_id"
validates :income_type, presence: true
end

Let me know your thoughts.

Matt Jones

unread,
Jul 18, 2014, 8:04:16 AM7/18/14
to rubyonra...@googlegroups.com


On Thursday, 17 July 2014 12:59:30 UTC-4, Ruby-Forum.com User wrote:
Hi Matt,

Thanks for your input. Actually I made it working without using STI. Not
sure this is the right way of doing it. But, this is what I wanted.

class TransactionItem < ActiveRecord::Base
  validates :note, length: { in: 2..255 }
  validates :transaction_date, presence: true
end

class Expense < TransactionItem
  belongs_to :expense_type, :class_name => "TransactionType",
:foreign_key => "transaction_type_id"
  validates :expense_type, presence: true
end

class Income < TransactionItem
  belongs_to :income_type, :class_name => "TransactionType",
:foreign_key => "transaction_type_id"
  validates :income_type, presence: true
end


Not sure what you mean by "without using STI". Do you have a `type` column on the transaction_items table?

--Matt Jones
Reply all
Reply to author
Forward
0 new messages