Howarto
Howarto

Reputation: 134

Meaning of # symbol

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

Answers (3)

Doodad
Doodad

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

Arup Rakshit
Arup Rakshit

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

sasha.sochka
sasha.sochka

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

Related Questions