Reputation: 134
My code has a #
symbol in the class_eval
section. This is foreign to me, what does it mean?
class Class
def attr_accessor_with_history(attr_name)
attr_name = attr_name.to_s # make sure it's a string
attr_reader attr_name # create the attribute's getter
attr_reader attr_name+"_history" # create bar_history getter
class_eval %Q{
def #{attr_name}=(attr_name)
@#{attr_name} = attr_name
@#{attr_name}_history = [nil] if @#{attr_name}_history.nil?
@#{attr_name}_history << attr_name
end
}
end
end
Upvotes: 0
Views: 245
Reputation: 1518
def #{attr_name}=(attr_name)
@#{attr_name} = attr_name
@#{attr_name}_history = [nil] if @#{attr_name}_history.nil?
@#{attr_name}_history << attr_name
end
If attr_name
variable where equal to, let's say, "params"
. This would actually transform into this:
def params=(attr_name)
@params = attr_name
@params_history = [nil] if @params_history.nil?
@params_history << attr_name
end
Why that happens? Because of something called String interpolation. If you write #{something}
inside a String, something
will be evaluated and replaced inside that String.
And why whould the above code work even though it's not in a String?
The answer is, because it is!
Ruby gives you different ways to do things, and there is an alternative syntax for some literals, that goes like that: %w{one two three}
where {}
could be any delimiter, as long as you use the same or the corresponding closing one. So it could be %w\one two three\
or %w[one two three]
, they would all work.
That one, %w
is for Arrays, %Q
is for double-quoted String. If you wanna see all of them, I suggest you take a look at this: http://www.ruby-doc.org/docs/ProgrammingRuby/html/language.html
Now, in that code
class Class
def attr_accessor_with_history(attr_name)
attr_name = attr_name.to_s # make sure it's a string
attr_reader attr_name # create the attribute's getter
attr_reader attr_name+"_history" # create bar_history getter
class_eval %Q{ <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< # STRING BEGINS
def #{attr_name}=(attr_name)
@#{attr_name} = attr_name
@#{attr_name}_history = [nil] if @#{attr_name}_history.nil?
@#{attr_name}_history << attr_name
end
} <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< # STRING ENDS
end
end
We can see the whole part with the String interpolation is inside a %Q{ }
. That means the entire block is a big double-quoted String. And that's why the String interpolation will successfully do it's job before sending the String to eval.
Upvotes: 2
Reputation: 118271
why the next code has got # in class_eval?
this is string interpolation.
one example:
x = 12
puts %Q{ the numer is #{x} }
# >> the numer is 12
%Q Here
This is an alternative for double-quoted strings, when you have more quote characters in a string.Instead of putting backslashes in front of them.
Upvotes: 2
Reputation: 14715
This feature is called string interpolation. What it effectively does is it substitutes #{attr_name}
with attr_name
actual value. The code you posted shows one of the use cases - when you want to use variables with generic names in runtime.
Then use case used even more often is the following:
You can use string this way: "Hello, #{name}!"
and #{name}
will be automatically replaced here - this is very handy feature. A syntactic sugar.
But note %Q
in the code - this transforms the following code to a string and is later passed to class_eval
and is executed there. See more about it here. Without it it wouldn't work, of course.
Upvotes: 4