Tricky Inheritence

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.

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

On Friday, 11 July 2014 13:36:32 UTC-5, Ruby-Forum.com User wrote:

validates :name, uniqueness: true
end

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’

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 J.

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.

I have uploaded the source code of model and migration at below link.

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)

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.

Thanks.

On Monday, 14 July 2014 16:30:03 UTC-4, Ruby-Forum.com User wrote:

base class transaction_type which extends ActiveRecord::Base.

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 J.

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

end

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

–Matt J.