This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Meaning of #{ } in Ruby?
I know it is used for meta-programming, and I'm having a hard time trying to wrap my mind about what this operator is doing in the following example:
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
attr_reader attr_name+"_history"
class_eval %Q"
def #{attr_name}=(value)
if !defined? ##{attr_name}_history
##{attr_name}_history = [##{attr_name}]
end
##{attr_name} = value
##{attr_name}_history << value
end
"
end
end
class Foo
attr_accessor_with_history :bar
end
In general terms, #{...} evaluates whatever's inside of it and returns that, converted to a string with to_s. This makes it a lot easier to combine several things in a single string.
A typical example:
"There are #{n} car#{n == 1 ? '' : 's'} in the #{s}"
This is equivalent to:
"There are " + n.to_s + " car" + (n == 1 ? '' : 's').to_s + " in the " + s.to+_s
It's important to remember that the contents of the #{...} interpolation is actually a block of Ruby code and the result of it will be converted to a string before being combined.
That example of meta programming is awfully lazy as instance_variable_get and instance_variable_set could've been used and eval could've been avoided. Most of the time you'll see string interpolation used to create strings, not methods or classes.
There's a more robust formatter with the String#% method:
"There are %d car%s in the %s" % [ n, (n == 1 ? '' : 's'), s ]
This can be used to add a precise number of decimal places, pad strings with spaces, and other useful things.
#{var} does variable substitution in Ruby. For example:
var = "Hello, my name is #{name}"
The code you've posted is generating a string with the code for an accessor method for the attr_name you've passed in.
It's not doing much really :D. All the red text is basically just a string. The "bla #{var} bla" part is just a nicer way of writing "bla " + var + " bla". Try it yourself in irb:
a = 10
puts "The Joker stole #{a} pies."
what it does is called variable interpolation.
name = "Bond"
p "The name is #{name}. James #{name}."
will output,
> "The name is Bond. James Bond."
Related
I know writing like
a=23
p "the value of a is #{a}"
it will print: the value of a is 23.
but now I am actually receiving this string as a parameter like
def evaluate string
a=23
puts string
end
calling method pass that string as a parameter
evaluate "the value of a is #{a}"
Is there any way to evaluate this string inside the method? puts string has to interpolate the value a=23.
Edit:
I have to read and execute the program from Excel.
At the first line,
Excel entry is,
"id=something" setvalue a
So now corresponding program will read the value from locator id=something and set it into the instance variable #a.
and user's next excel entry would be
"the value of a is 23" compare "the value of a is #{a}"
Now the program will read "the value of a is 23" and this "the value of a is #{a}" for comparison, but before it compares, it has to replace the value a. That's all I want. I hope now my question is very clear.
For ruby you can change how you "format" your strings in Excel, than you can use "classic" formatting
a = 23
s = 'the value of a is %s'
def evaluate(text, value)
puts text % value
end
You can use different formatting keys, for example %d for integers, %f for float numbers
You can use named arguments
dynamic_text = 'the value of the %<product_name>s is %<product_price>0.2f'
def evaluate(text, args)
puts text % args
end
name = "Product"
price = 78.99
evaluate dynamic_text, product_name: name, product_price: price
Without names, use order of the given values
dynamic_text = 'the value of the %s is %0.2f'
def evaluate(text, args)
puts text % args
end
name = "Product"
price = 78.99
evaluate dynamic_text, [name, price]
You can make a block and then evaluate the string:
def evaluate &block
a=23
block.call(a)
end
evaluate { |a| "the value of a is #{a}" } #=> "the value of a is 23"
It's a very odd thing you're attempting to do. When you have some sort of a pattern with placeholders, you do it like:
def evaluate(string)
a=23
format string, a: a
end
evaluate "the value of a is %{a}"
String interpolation with #{..} is not meant for the case you're describing as the value is evaluated at the time of constructing the string, not later. You could do some regexp matching and replace the #{..} with %{..} as a workaround.
There's a few ways:
"Code" Dynamic
lazy evaluation with lambdas:
def evaluate(str_template)
a = 23
str_template.call(a)
end
user_input = gets
my_lambda = lambda do |str|
user_input.size > 10 ? "dynamic 1 #{str}" : "dynamic 2 #{str}"
end
evaluate(my_lambda)
# => "dynamic 1/2 23"
This is "code dynamic", but not "input dynamic", i.e. you can't receive the string template from the user.
"Input" Dynamic 1
ERB templating:
require 'erb'
user_input_erb = gets
puts user_input_erb # "Hello <%= name %>"
name = gets # also user input, e.g. "World"
ERB.new(user_input_erb).result
# => "Hello World"
Note that in general, getting string templates from the user and evaluating them is a potential security vulnerability. If there's any possibility user input can be adversarial, you'll want to see if you can find a "guaranteed to be safe against all user input" string templating library.
"Input" Dynamic 2
user_input_template = gets
puts user_input_template # "Hello %s"
name = gets # also user input, e.g. "World"
user_input_template % name
# => "Hello World"
"Input" Dynamic 3
Really dangerous, but:
user_input_ruby_code = gets
puts user_input_ruby_code # '"Hello #{name}"'
name = gets # also user input, e.g. "World"
eval user_input_ruby_code # DANGER
# => "Hello World"
I have a few syntax errors on my program that are really bothering me. I can't seem to figure out how to fix them as I am new to ruby. The first error is on the title, I have a few more I'm sure. The purpose of the program is to create cars with make, model, and year and have user input how many cars they want then display all of them at the end.
Can someone point me to the right direction?
Here is my code:
class Car
def initialize(make, model, year)
#make = make
#model = model
#year = year
end
print "How many cars do you want to create? "
array_of_cars = Array.new
num_cars = gets.to_i
c = car.new
for i in 1..num_cars
end
puts
print "Enter make for car #{i}: "
make = gets.chomp
print "Enter model for car #{i}: "
model = gets.chomp
print "Enter year for car #{i}: "
year = gets.to_i
c.set_make(make)
c.set_model(model)
c.set_year(year)
array_of_cars << c
end
puts
puts "You have the following cars: "
for car in array_of_cars
puts "#{car.get_year} #{car.get_make} #{car.get_model}"
end
In ruby, class names are constants, so should start with a capital letter, as in class Car. When creating a new object of that class, you call new on the class itself. So you would change car.new into Car.new.
You will also need to define your set_* and get_* methods inside the class, but since this is a common pattern, ruby has attr_accessor available. See this answer for a full explanation of attr_accessor.
Consider that your Car does not do anything, it contains only data and has no methods. When this happens, consider making it a Struct instead of a class. A Struct generates a reader and writer method automatically without even specifying attr_reader.
Car = Struct.new(:make, :model, :year)
array_of_cars = Array.new
while true
puts
print "Enter make for car ('x' to exit): "
make = gets.chomp
break if make == 'x'
print "Enter model for car: "
model = gets.chomp
print "Enter year for car: "
year = gets.to_i
array_of_cars << Car.new(make, model, year)
end
puts
puts 'You have the following cars:' # sorted by year for fun
array_of_cars.sort_by{ | car | car.year }.each do | car |
puts "#{car.year} #{car.make} #{car.model}"
end
A few pieces of advice.
Run Ruby with the -w option :
$ ruby -w cars.rb
cars.rb:17: warning: mismatched indentations at 'end' with 'for' at 16
cars.rb:34: warning: mismatched indentations at 'end' with 'class' at 1
cars.rb:41: warning: mismatched indentations at 'end' with 'for' at 39
and eliminate the cause of warnings.
$ ruby -w cars.rb
How many cars do you want to create? 2
cars.rb:2:in `initialize': wrong number of arguments (given 0, expected 3) (ArgumentError)
from cars.rb:13:in `new'
from cars.rb:13:in `<main>'
new calls initialize, so new must have the same number of arguments
as parameters in initialize. Hence a car can be created only after you have asked all the information.
Don't work in the class. As written, your code is executed when Ruby reads
the class definition. For this exercise, you can leave it in the main level outside the class definition, or put it into a method.
for i in 1..num_cars
end
This loop is empty and does nothing. And prefer powerful iterators instead of this C, Perl, Java style (for, while, etc).
I define strings with apostrophes and keep double quotes when interpolation is needed (even if it's a question of nano seconds and personal choice). See here and there.
If you want to be comfortable with Ruby programming, I recommend The Pickaxe.
There are many ways of doing things in Ruby. The following is one solution.
class Car
attr_reader :make, :model, :year
def initialize(make, model, year)
#make = make
#model = model
#year = year
end
def self.make_car # class method (more precisely : singleton method)
print 'How many cars do you want to create? '
array_of_cars = Array.new
num_cars = gets.to_i
num_cars.times do | i |
real_index = i + 1
puts
print "Enter make for car #{real_index}: "
make = gets.chomp
print "Enter model for car #{real_index}: "
model = gets.chomp
print "Enter year for car #{real_index}: "
year = gets.to_i
=begin
c = Car.new(make, model, year)
array_of_cars << c
=end
# some will tell you to avoid unnecessary variables ...
array_of_cars << Car.new(make, model, year)
end
puts
puts 'You have the following cars:' # sorted by year for fun
array_of_cars.sort_by{ | car | car.year }.each do | car |
puts "#{car.year} #{car.make} #{car.model}"
end
end
end # class Car
Car.make_car
class Bike
attr_accessor :color, :gear_numbers, :style
def spin
puts " spins! Woosh!"
end
end
gw = Bike.new
gw.color = "white"
gw.gear_numbers = 11
gw.style = "compact"
puts "This bike is #{gw.color} and it has #{gw.gear_numbers} gears. Oh, and it has a #{gw.style} design. Did I mention that my bike #{gw.spin}?"
Using IRB, this is what I get:
**spins! Woosh!
This bike is white and it has 11 gears. Oh, and it
has a compact design. Did I mention that my bike ?**
Why is "spins! Woosh!" coming BEFORE the string and why isn't it IN the string?
Because you're not returning the string from your method, you're printing it directly.
To do what you want to do, simply remove the puts from your spin method and you're good to go.
class Bike
attr_accessor :color, :gear_numbers, :style
def spin
"spins! Woosh!"
end
end
Because to interpolate the string Ruby needs to call spin. Then Ruby includes the return value of the spin method (which is nil, because puts returns nil) into the string and prints the generated string.
The issue here is that string interpolation needs to be fully complete before the string is passed through to the main puts you have there. As part of figuring out what's in there it must execute each of the methods referenced in the order which they appear.
Your spin method causes an immediate puts and it doesn't return anything, as that's how puts works. If you want to supply a string to go in there, simply leave it:
def spin
" spins! Woosh!"
end
Think of this string interpolation:
"a #{b} c #{d} e"
This is roughly equivalent to:
"a " + b.to_s + " c " + d.to_s + " e"
Where those .to_s calls are to force it into a string. You'd expect b and d to be exercised before the whole string is returned.
When anticipating what code will do, trace execution to the bottom first, then work back up. Simple programs work in very predictable ways.
The inner_method is only ever called within outer_method, and its argument will always be identical to outer_method's.
This works:
def outer_method(word)
inner_method(word)
puts word + " are like candy."
end
def inner_method(same_word)
puts "I really love " + same_word + "!"
end
outer_method("onions")
but this doesn't:
def outer_method(word)
inner_method
puts word + "tastes like candy."
end
def inner_method
puts "I really love " + word + "!"
end
outer_method("onions")
It seems that inner_method's reference to word is not being registered by outer_method. Is there a better way to do this?
(I realize there's no reason to have a separate inner_method in the above example; this is simplified for clarity's sake)
I honestly think your first technique is the best way to go and perfectly idiomatic. Still, here's another option:
def outer_method(word)
inner_lambda = lambda do
puts "I really love " + word + "!"
end
inner_lambda.call
puts word + " tastes like candy."
end
outer_method("onions")
lambda creates a lexical closure, which means it captures the surrounding environment, including the reference to word.
There are two concerns with your question. The shallow one is your learning Ruby syntax. The deeper one is learning proper coding patterns. In your case, word object begs to exist:
class MyWord < String
def singular?; #singular end
def initialize **setup
singular, plural = setup[:singular], setup[:plural]
if singular then #singular = true
super singular
elsif plural then #singular = false
super plural
else fail ArgumentError, "Bad MyWord constructor arguments!" end
end
def interjection_1
"I really love #{self}!"
end
def interjection_2
"#{capitalize} #{singular? ? 'is' : 'are'} like cand#{singular? ? 'y' : 'ies'}!"
end
def hysteria
puts interjection_1
puts interjection_2
end
end
And then:
MyWord.new( plural: "onions" ).hysteria
This question already has answers here:
Ruby: String Comparison Issues
(5 answers)
Closed 3 years ago.
I was wondering why when I'm trying to gets to different inputs that it ignores the second input that I had.
#!/usr/bin/env ruby
#-----Class Definitions----
class Animal
attr_accessor :type, :weight
end
class Dog < Animal
attr_accessor :name
def speak
puts "Woof!"
end
end
#-------------------------------
puts
puts "Hello World!"
puts
new_dog = Dog.new
print "What is the dog's new name? "
name = gets
puts
print "Would you like #{name} to speak? (y or n) "
speak_or_no = gets
while speak_or_no == 'y'
puts
puts new_dog.speak
puts
puts "Would you like #{name} to speak again? (y or n) "
speak_or_no = gets
end
puts
puts "OK..."
gets
As you can see it completely ignored my while statement.
This is a sample output.
Hello World!
What is the dog's new name? bob
Would you like bob
to speak? (y or n) y
OK...
The problem is you are getting a newline character on your input from the user. while they are entering "y" you are actually getting "y\n". You need to chomp the newline off using the "chomp" method on string to get it to work as you intend. something like:
speak_or_no = gets
speak_or_no.chomp!
while speak_or_no == "y"
#.....
end
once you use gets()...
print that string.. using p(str)
usually string will have \n at the end.. chomp! method should be used to remove it...