3

I have an accounts model, where I would like the balance to be readonly, but it can be updated via private methods.

Currently

class Account < ActiveRecord::Base
  def deposit(amount)
    # do some stuff and then
    update_balance(amount)
  end

  private

  def update_balance(amount)
    self.balance += amount
  end

end

However, this is still possible:

account = Account.first
account.balance += 5
account.save

I would like for the above to give an error, but still be able to do:

account.balance #=> 0
account.deposit(5)
account.balance #=> 5
Amin Shah Gilani
  • 8,675
  • 5
  • 37
  • 79

2 Answers2

3

You may define a private setter for the attribute:

class Account < ActiveRecord::Base

  private

  def balance=(the_balance)
    write_attribute(:balance, the_balance)
  end
end

Then, you are not allowed to call it outside your Account class:

Account.new(balance: 1234)
# => ActiveRecord::UnknownAttributeError: unknown attribute 'balance' for 'Account'

Account.new.balance = 1234
# => NoMethodError: private method `balance=' called for <Account ...>
Matouš Borák
  • 15,606
  • 1
  • 42
  • 53
1

ActiveRecord not only the rails gem. This is a common-used pattern that means mirror-like representation of your DB in your object. So all data-access methods in your models can be defined automatically with ruby metaprogramming mechanisms.

Maybe it's better to keep your logic clear and convenient and write class AccountManager that will works on top of Account and can provide you such isolation that you need:

class AccountManager
  def initialize(account)
    @account = account
  end

  def deposit(amount)
    # do some stuff and then
    update_balance(amount)
  end

  private

  def update_balance(amount)
    @account.balance += amount
  end
end
rootatdarkstar
  • 1,486
  • 2
  • 15
  • 26
  • While this may work, I'm using Ruby, and the convention is to let ActiveRecord handle all the ORM tasks. – Amin Shah Gilani Apr 26 '16 at 17:49
  • Business logic is not "ORM tasks". And class extraction is widely used in modern development with rails . Btw, beware fat models and hackish class patching. – rootatdarkstar Apr 26 '16 at 17:54
  • Ruby on Rails*.. sorry, I type like a spaz at times. What I needed was to be able to make the `setter` private, which BoraMa answered! – Amin Shah Gilani Apr 26 '16 at 18:03