Reputation: 57192
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
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
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