1

I'm having a lot of difficulty putting together what the following code is supposed to do (I understand what it's supposed to do, I just can't put together the pieces):

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

ROLES is an array specified in the link. I'm assuming roles is also an array.

  1. Why have a setter method and a regular method with the same name?
  2. What array serves as the object of the map method? If roles is ['author', 'editor'] and ROLES is ['author', 'editor', 'manager'], how does the & operator create an array for map?
  3. What is 2**ROLES? I figured out that this is really just 2 raised to the power of ROLES.index(r).
  4. "Bitmask attributes on a Rails application" says that (roles & ROLES) is sanitizing the parameters roles against the array ROLES, but what does sanitizing it mean?
  5. If r is the current value of the array (roles & ROLES), how does the index method of 2**ROLES return r? I figured out that this is returning the position of roles in the array ROLES, but I still don't know how (roles & ROLES).map interacts/works with this.
  6. How does the inject method work on the condition in the brackets for the map method?

I'd like to figure out how this bitmasking works, but I have no idea how what's on the right side of the equation for def roles=(roles) returns an integer.

Drew Rush
  • 710
  • 5
  • 19
  • 1. `roles=` is a different method name than `role`. 2. Array#& : Returns a new array containing elements common to the two arrays, with no duplicates. (explanation found in The Pickaxe http://pragprog.com/book/ruby3/programming-ruby-1-9) – BernardK Dec 16 '12 at 07:26
  • "roles= is a different method name than role" But the second one is named "roles." So are you saying roles=() and roles are two different method names? If so, i get that. And thanks! – Drew Rush Dec 16 '12 at 15:28
  • Sorry for the typo. We say that `roles` is a _getter_, returns a value, and `roles=()` is a _setter_, modifies the value. Writing `roles = xyz` is a syntactic sugar of Ruby. Actually the equal sign is part of the name of the method, if roles is a method. But in `var = xyz`, if var is a variable, the equal sign is just the assign operator. A search with **attr_accessor** will give you plenty of answers, like http://stackoverflow.com/questions/4370960/what-is-attr-accessor-in-ruby – BernardK Dec 16 '12 at 17:27
  • Ruby's IRB is great for trying these things out for yourself. Create dummy values for `roles` and `ROLES` and see what `(roles & ROLES)` returns. Follow that with the `map` statement and see what happens. Stack Overflow greatly prefers a single question. Multiple questions dilute and confuse the possible answers, so try to keep them restricted. One or two questions is manageable. Six is overboard. – the Tin Man Dec 16 '12 at 20:18

1 Answers1

3

Basically the index call converts the given roles to integers by determining their position in the array of ROLES. 2 is then raised to the power of this index and inject simply sums the the results.

Put simply

Step 1 is to get roles that are found in ROLES by using & to se which values in the two arrays match.

Step 2, for each matching role compute 2**index. Map returns an array of these values.

Step 3, sum the array to get the final integer.

The idea is to compute a unique integer for each combination of roles.

The getter simply unwinds the process returning the combination of roles that match.

NullRef
  • 3,713
  • 1
  • 20
  • 18