Jeff Storey
Jeff Storey

Reputation: 57192

ruby understanding enum method

I was reading another SO question Enums in Ruby and it had the following code snippet:

class Enum

  private

  def self.enum_attr(name, num)
    name = name.to_s

    define_method(name + '?') do
      @attrs & num != 0
    end

    define_method(name + '=') do |set|
      if set
        @attrs |= num
      else
        @attrs &= ~num
      end
    end
  end

  public

  def initialize(attrs = 0)
    @attrs = attrs
  end

  def to_i
    @attrs
  end
end

As I understand this, this is defining a class method named enum_attr, is that correct? What I'm unsure of is what it means to have the define_method statements inside of the enum_attr method.

Then later on that post it shows the class being extended as follows

class FileAttributes < Enum
  enum_attr :readonly,       0x0001
  enum_attr :hidden,         0x0002
end

I don't quite understand what this second part does - can someone explain?

Upvotes: 1

Views: 504

Answers (2)

Alex D
Alex D

Reputation: 30445

You are looking at a class method which generates true/false attributes which are stored using a bitfield. Do you have a background in C (or perhaps Java)? If so, you may be familiar with bitfields and general "bit twiddling". Most Internet resources on these topics will be related to C; but you can still use them in Ruby and other languages.

In this case, you don't gain anything from storing your boolean attributes in individual bits, and I would advise you not to actually use this code. You would be better off using a different instance variable for each attribute, with true or false values.

Upvotes: 1

Andrew Marshall
Andrew Marshall

Reputation: 96924

In Enum, a method, enum_attr, is defined on the class's singleton, and is available to all subclasses. This method is in scope in the class definition body, and in FileAttributes it is being called with the arguments :readonly, 0x0001 and then :hidden, 0x0002.

When enum_attr is called (let's look at just the first call, enum_attr :readonly, 0x0001), it defines two methods: readonly? & readonly=(set). The result of this call to enum_attr is functionally equivalent to writing out the following in FileAttributes:

def readonly?
  @attrs & 0x0001 != 0
end

def readonly=(set)
  if set
    @attrs |= 0x0001
  else
    @attrs &= ~0x0001
  end
end

Since the block passed to define_method is a closure, the variable num from the scope in which the block is passed is still in scope when you call the method that is defined. In other words, the num variable passed in to enum_attr is still available within the generated methods readonly? & readonly= when they are called later from a different scope.

define_method must be used because the name of the method is being dynamically generated (i.e., we do not know the name of the method ahead of time).

Upvotes: 2

Related Questions