2

Saw this on another page:

"with getter, one obtains the current value of @a, without modifying it."

"with setter, one modifies @a, and get its new value as return value."

However, looking at this code from the cancan wiki, I see that both the setter and the getter are actually doing something to the variable in it.

def roles=(roles)
  self.roles_mask = (roles & ROLES).map { |r| 2**ROLES.index(r) }.inject(0, :+)
end

def roles
  ROLES.reject do |r|
    ((roles_mask || 0) & 2**ROLES.index(r)).zero?
  end
end

It looks like the getter is actually returning a truth value, and if not that, at least some sort of transformation. So is this "getters get without modifying, setters set with modifications" rule actually true?

Community
  • 1
  • 1
Drew Rush
  • 710
  • 5
  • 19

1 Answers1

7

That is the wrong way to think about "getters" and "setters". Instead, think this:

A setter alters the state of an object. It might set a simple instance variable. It might set several instances variables. As in the posted code it might transform the information before saving it.

A getter retrieves some information about the state of the object. It doesn't matter what this is; it could be a value directly stored in an instance variable. Or it could be some other value based upon the current state of the object, as in the post.

It is usually advisable for the getter and setter to take/return the same type of value and affect/report the object state in a consistent way. In the above the exposed type is an "Array of Roles" and it represents the Roles associated with the object.


Using extra named methods can show the intent of the posted code more clearly as the complex bit mask building/consuming expressions can be extracted out; note the symmetry:

def toMaskFromArray (roles)
  (roles & ROLES).map { |r| 2**ROLES.index(r) }.inject(0, :+)
end

def toArrayFromMask (mask)
  ROLES.reject do |r|
    ((mask || 0) & 2**ROLES.index(r)).zero?
  end
end

def roles=(roles)
  self.roles_mask = toMaskFromArray(roles)
end

def roles
  toArrayFromMask(self.roles_mask)
end
  • So then it doesn't necessarily have to return the instance variable specifically? Am I right that the second method is returning a truth value? – Drew Rush Dec 18 '12 at 00:47
  • It doesn't have be a "simple" instance-variable expression (e.g. `@a`). The getter/setters actually encode/decode a sequence of roles roles into an internal integer (where each role is represented by a particular "bit"); this avoids having to keep around the array. The getter returns an array, as it "explodes" the integer back into the apporpriate sequence. –  Dec 18 '12 at 00:48