Glad You're Ready. Let's Get Started!

Let us know how we can contact you.

Thank you!

We'll respond shortly.

ActiveRecord learns to respect your privates

This is somewhat old news, but I don’t think it has received the attention it deserves. As of Rails 2.2, ActiveRecord associations and attributes will now behave properly with regard to access control. You can view the Rails tickets, with patches, here and here.

Take, for example, this schema:

mysql> desc accounts;
| Field          | Type         | Null | Key | Default | Extra          |
| id             | int(11)      | NO   | PRI | NULL    | auto_increment |
| balance        | int(11)      | NO   |     | 0       |                |

used by this model:

class Account < ActiveRecord::Base
  def deposit(amount)
    balance += amount

  def withdraw(amount)
    if sufficient_funds?(amount)
      balance -= amount

private :balance=


  def do_state_and_federally_mandated_things

You most likely don’t want someone coming along and modifying the balance attribute directly, either intentionally or inadvertently. However, prior to Rails 2.2, ActiveRecord ignores the privacy declaration for #balance=, so you must execute horrid machinations in order to protect it:

class Account < ActiveRecord::Base
  def balance=(amount)
    raise "I'm private!"

  def deposit(amount)
    write_attribute(:balance, balance + amount)


As of Rails 2.2, ActiveRecord will respect private accessors for database column attributes.

Along the same vein, if we add the following:

class User < ActiveRecord::Base
  has_one :account

Now we can call methods on the proxy returned by calling User#account, just as if we were calling methods on an account instance; any methods:

johnny_taxpayer = User.first

Who knows what scary things #do_state_and_federally_mandated_things does? This will, frighteningly, run just fine prior to Rails 2.2. But, no more.

Now, a number of people have considered this change and asked “why bother?” Ruby allows access to private methods via #send, so they’re not really private, right?

This argument leads down the dark path. Like it or not, cheat around it as you may, access control is an important aspect of object oriented programming. If nothing else, the private keyword is my way of saying “hic sunt dracones,” or “hands off!” It’s also a way of saying “this method may or may not exist in the future.” As a class designer I have every right to refactor that private method entirely away, thus breaking any code that calls it; including, significantly, test code.

More fundamentally, object interactions should be via interfaces. If code makes calls to an object’s private methods, that code has now tied itself to the object’s implementation. Coupling ensues, duck-typing breaks down, anarchy reigns.

So, this begs the question, why does #send ignore access control?. Why should two forms of sending a message to an object differ in their access control semantics? Sometimes I’m forced to use #send:

method_name = extract_method_name_from_the_aether

In order to make this code correct with regard to access control I have to add cruft:

method_name = extract_method_name_from_the_aether
some_object.send(method_name) if some_object.respond_to?(method_name)

Now, this is not to say that I don’t think Ruby should provide the ability to call private methods, or generally dig around in an object’s internals. This comes in handy in some instances (although I find many of these instances are short-term solutions that should get refactored away). But, in an ideal world I think the sender should explicitly specify when a message should ignore access control:

potentially_private = extract_method_name_from_the_aether

This makes the syntax somewhat more ugly, but ugly syntax suits an ugly operation.

  1. Mark Wilden says:

    At one point, in Ruby 1.9, #send was changed to not call private methods any more. However, the change apparently broke too much code, so it was reverted.

  2. Adam Keys says:

    I agree that private methods are great for signaling the _intent_ of the class designer. As to their enforcement, I’m a believer in [Gentlemen’s Agreements](

  3. Adam Milligan says:

    @Another Adam,

    Keep in mind I said I have no problem with Ruby providing mechanisms for circumventing access control. What I don’t like is that in cases where I have to use #send rather than a direct method invocation Ruby gives me no choice to respect access control.

    In other terms, #send violates cohesion; it serves dual purposes. On one hand, it allows the sending of arbitrary messages. On the other, it allows calling private methods. When I want one behavior I get both, whether I like it or not.

  4. Adam Keys says:

    I believe we are in violent agreement.

  5. Alex Chaffee says:

    Wouldn’t “send!” fit the semantic bill for “send_without_restriction”? Bang means “dangerous!”

  6. Adam Milligan says:

    @Alex, I quite like it. Make it so.

Post a Comment

Your Information (Name required. Email address will not be displayed with comment.)

* Copy This Password *

* Type Or Paste Password Here *