rock_n_rolla
rock_n_rolla

Reputation: 357

How to append to a string on an ActiveRecord column in Rails

I have a concept of a Share which represents a specific stock owned by a particular shareholder. What I am trying to add now is functionality that supports tracking the "history" of this Share over time, given it can be transferred to other shareholders etc.

In my database, for each Share record there is currently a column t.string "transaction_ids". I should probably change the column type I am assuming to something easier to work with. Open to suggestions here.

The two relevant methods are when creating new shares and then transferring ownership of the shares.

Here is how the Share is created initially.

@number_of_share_numbers_to_create.times do |i|
      Share.create(
        owner_id: params[:buying_shareholder_id], 
        captable_id: @transaction.captable.id, 
        company_id: @transaction.company.id, 
        share_number: @latest_share += 1,
        transaction_ids: @transaction.id #Save transaction id to DB
      )
end

Then when a Share is transferred, I have the following method.

@share_numbers_on_cap.each do |share|
      share.update_attribute(:owner_id, @buying_shareholder.id)
      # TODO Capture share history by pushing @transaction.id into the transaction_ids column. 
      # Perhaps I could do something like share.transaction_ids >> @transaction.id or something?
end

I know this current solution is not optimal, and I'd love some guidance on how to solve this in a more scalable way. Perhaps I should use a different column type and build an array? Would love some push in the right direction.

Thanks!

Upvotes: 3

Views: 1293

Answers (2)

MrYoshiji
MrYoshiji

Reputation: 54882

I suggest creating another model to "log" the transactions that happened for a specific Share object.

For example:

class ShareTransfer < ApplicationRecord
  belongs_to :share
  belongs_to :transaction
  # don't forget to enforce the presence of 
  # both foreign keys share_id and transaction_id

  # bonus: "freezing" the object after creation so you can never
  # update the logged object (= data integrity)
  before_save(on: :update) do
    errors.add(:base, :immutable, "Cannot update a frozen object")
  end
end

And change your current Share#transaction_ids to a simple integer column with a foreign key to the transactions table - an probably rename it as well - to end up with something like:

class Share < ApplicationRecord
  belongs_to :transaction # implies you have renamed the column transaction_ids to transaction_id (and changed the column type)
  has_many :share_transfers
end

Which enables you to do the following when a Share is transferred:

@share_numbers_on_cap.each do |share|
  # [your previous logic to change ownership]
  share.share_transfers.create!(transaction_id: share.transaction_id)
  # ^ to create history of the ownership change
end

This schema allows you to add extra fields to the ShareTransfer model, for example, the exact time when the transfer occurred (ex: occurred_at), the IP of both parties when the transfer was validated, etc. You can even lock the ShareTransfer table to be "insert only", preventing any user (or code) to update the data in there and ensuring the integrity of your logs.

Upvotes: 2

mrzasa
mrzasa

Reputation: 23317

You can use an array column type (if you use postgres). But what I'd do is to move transaction ids to a separate DB table. It will let you easily add new ids and query them fast.

Upvotes: 2

Related Questions