简体   繁体   中英

Rails: set a model attribute as read-only, but allow updating via method

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

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM