Why don't block parameters in a here-doc work? - ruby

I'm trying to figure out why the following code does not work in a here-doc string eval:
script = <<FILE
def i_feel(&block)
block_given? ? "I feel #{ yield } :D" : "I don't know how I feel :/"
end
i_feel
# => "I don't know how I feel :/"
i_feel { 'great' }
# => "I feel great :D"
FILE
puts script
`<main>': no block given (yield) (LocalJumpError)
I know that part of the reason is the string interpolation. But how do I make it work? Try it without the here-doc first and see if it works?

You can avoid interpolation in a heredoc by surrounding the identifier with ':
script = <<'FILE'
something with #{interpolation} like syntax.
FILE
puts script
This will just print out the literal something with #{interpolation} like syntax. Without the ' around FILE you would probably get an error (if interpolation was defined you wouldn’t).
You can also use ` (backticks) to execute the contents of the heredoc:
s = <<`SCRIPT`
echo foo
echo bar
SCRIPT
s will now contain the result of executing the heredoc – in this case the string "foo\nbar\n".

I think that I just figured it out.
script = <<FILE
def i_feel(&block)
block_given? ? "I feel yield :D" : "I don't know how I feel :/"
end
i_feel
# => "I don't know how I feel :/"
i_feel { 'great' }
# => "I feel great :D"
FILE
puts script
Just remove the string interpolation brackets and here doc prints it just fine without them. I think I forgot about setting it up to interpolate is all. Oops! I'll get back to you all on this when I can.
Maybe now I can get something new from it. At least the error went away.
That will stop distracting me.

Here's the answer and a little more about what my intended purpose is. You want to see what you just ran so this is the complete version.
script = <<FILE
def how_i_feel
block_given? ? "I feel \#{ yield }." : "I don't know how I feel :/."
end
#{ def how_i_feel
block_given? ? "I feel #{ yield }." : "I don't know how I feel :/."
end }
how_i_feel
#{ how_i_feel }
how_i_feel { 'great :D' }
#{ how_i_feel { 'great :D' } }
FILE
puts script
What Victor said was right.
Here doc includes scope also.
In this case it is the interpolation of the interpolation.
Sounds a little confusing but just look at what level those brackets go to.
Notice also that we have to escape the original #{ yield } less it try to evaluate it.
That was what caused all the trouble in the first place.
Hope this helps.
And by the way this is just copied from https://github.com/JoshCheek/ruby-kickstart/blob/kevin_challenge/cheatsheets/blocks_procs_and_lambdas.rb
From that you can see that you don't need the &block parameter.

Related

How to put a name in uppercase?

Here below is a picture of the challenge that i have to complete. I already tried two ways to solve this problem but i still can't fix it.
Here below is the snippet of how i tried to solve the problem. It might not make sense at all because i tried to solve the problem by applying what i can remember from learning Ruby a while ago. Like explained in the picture above please do not change the code that is outside the "# Please write your code below this line..." and the "# ...and above this line." comment.
my_name = "Jordan"
# Please write your code below this line...
# The first way i tried to fix this problem
def my_name(name)
upper = my_name.upcase
my_name = upper.upcase!
# Second way i tried to to fix this problem
def my_name(name)
upper = my_name.upcase
upper.upcase!
end
# ...and above this line.
puts "My name is #{upper(my_name)}"
Shouldn't:
puts "My name is #{my_name.upcase}"
work? If you want to make it into a method, you could just return the .upcase of the parameter given. I don't use ruby, so I wouldn't know as much as others, though.
https://apidock.com/ruby/String/upcase
Just call the method upcase of the class String
"jordan".upcase
Try this.
def upper(str, target)
str.gsub(/\b#{target}\b/i) { |s| s.upcase }
end
target = "jordan"
upper("My name is Jordan", target)
#=> "My name is JORDAN"
upper("My name is Bob", target)
#=> "My name is Bob"
upper("My name is Jordanny", target)
#=> "My name is Jordanny"
upper("My name is Bojordan", target)
#=> "My name is Bojordan
upper("My name is Bob", "bob")
#=> "My name is BOB"
The word breaks (\b) prevent matches in the last two examples. See String#gsub. Note:
/\b#{target}\b/i
#=> /\bjordan\b/i
The other answers provide sufficient solutions, but I wanted to share why this is happening to you. You were close but there's a little nuance to the way upcase! works that isn't intuitive
You original code snippet has this:
upper = my_name.upcase
The my_name.upcase method takes your input, Jordan, and upcases it (just like you expect) and assigns it to the upper variable.
But, you also had this line:
my_name = upper.upcase!
Which is equivalent to "JORDAN".upcase! (because the first upcase call already upcased the text for you. Per the ruby docs I linked above, this method returns nil if no changes were made. Since you already upcased the word, no changes are made, so it returns nil
You can use one of the solutions in the other others, or simply delete the 2nd line where you call upcase!
This is all you really need to do:
def my_name(name)
name.upcase
end

Can I make `"#{file_name}"` simpler?

It looks quite verbose to write something as this "#{file_name}" instead of just file_name.
def write_hello(file_name)
File.open("#{file_name}", "w") { |file| file.puts "Hello, world! I'm Ruby :)"}
end
What can I do to make it cleaner?
Updated
I just have ruby naming conventions, so I updated the fileName to file_name now.
That's needlessly verbose.
x = "test"
"#{x}"
# => "test"
It's literally the same thing in your case.
A Ruby idiomatic version of this code looks like:
def write_hello(filename)
File.open(filename, "w") do |file|
file.puts "Hello, world! I'm Ruby :)"
end
end
This uses the multi-line do ... end form which is often a lot easier to follow and understand but is otherwise equivalent to the { ... } form. New Ruby people may be a little perplexed at how { x: 'y' } and { |x| 'y' } are dramatically different things, so this avoids the ambiguity there.
It's not necessary to enquote something in isolation. That would be necessary if you wanted to append a file extension, like:
File.open("#{filename}.txt", "w") do |file|
# ...
end
Where you'd call that like:
write_hello(:example)
And that would create example.txt
Your code works, but is actually bad code. Indeed it should be written as fileName, although a Rubyist would not name a variable like that in camel case.
However, it would be better to write shorter, like
File.write(fileName, "Hello, world! I'm Ruby :)")
although you would have to explicitly add a new line character at the end if the string if you want one, as Stefan notes.

How to convert this if condition to unless?

if array.present?
puts "hello"
end
There is no else part to this.
How to write the above if condition using unless.
I'm asking this question because of this lint error:
Use a guard clause instead of wrapping the code inside a conditional expression
Regarding your comment:
I'm asking this question because of this lint error
Use a guard clause instead of wrapping the code inside a conditional expression
This means that instead of:
def foo(array)
if array.present?
puts "hello"
end
end
You are supposed to use:
def foo(array)
return unless array.present?
puts "hello"
end
See https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals
If this is a Rails question (is it?), you can also use blank?:
def foo(array)
return if array.blank?
puts "hello"
end
There's no reason to.
Remember: unless is the inverse of if (or !if if you rather), and is only intended to make your code easier to read.
Using unless with your expression would be incredibly awkward, because you're now moving the actual body of work to an else statement...
unless array.present?
return
else
puts "hello"
end
...which doesn't make your code any easier to read if you had stuck with a negated if:
if !array.present?
return
else
puts "hello"
end
Don't use unless here. You lose readability in exchange for virtually nothing.
One-liner:
puts "hello" unless !array.present?
However, I would recommend:
puts "hello" if array.present?
unless array.present?
return
else
puts "hello"
end
OP requested one-liner modification:
Pseudocode:
something unless condition
Therefore:
puts "hello" unless !array.present?

How can I make script work in Ruby?

I am new to Ruby.
I need to make this script work:
puts "Do you like cats?"
ask = gets
def ask(n)
if ask == yes
return "I do too"
end
if ask == no
return "Dogs are better"
end
end
puts "#{ask(n)}"
Error message is :
pracif.rb:15:in <main>': undefined local variable or methodn' for
main: Object (NameError)
Here's a script that would work for you :
puts "Do you like cats?"
answer = gets
def ask(n)
if n == 'yes'
return "I do too"
end
if n == 'no'
return "Dogs are better"
end
end
puts ask(answer.downcase.chomp)
Explaination
As the error said you were trying to pass in a variable n which was not defined
Secondly you have a method name ask same as variable name. I've renamed the variable to answer instead
Thirdly, enclose yes and no in quotes
And finally, since you are using gets a \n gets appended like yes\n so none of your conditions would match. So i've used chomp to remove \n. And also used downcase to make input case insensitive.
EDIT
As mentioned by #Jordan in the comments, there is no reason to use string interpolation for the puts statement. So it's enough to call the method directly.
There are a bunch of issues with your code. Try something more like:
def reply(response)
return 'I do too' if response == 'yes'
return 'Dogs are better' if response == 'no'
'Invalid response!'
end
puts 'Do you like cats?'
response = gets().chomp()
puts reply(response)
Pay attention to the variable names. If you keep them descriptive, it is easier to spot mistakes.
Your script has no n local variable defined that you are passing to your ask(n) method at the end.
Rename your ask variable that your script gets from user to answer for example and pass it to your ask method at the end like so:
Updated code to fix other problem I did not see in the first run.
puts "Do you like cats?"
answer = gets.chomp
def ask(n)
(n == 'yes') ? "I do too" : "Dogs are better"
end
puts "#{ask(answer)}"

What are those pipe symbols for in Ruby?

What are the pipe symbols for in Ruby?
I'm learning Ruby and RoR, coming from a PHP and Java background, but I keep coming across code like this:
def new
#post = Post.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #post }
end
end
What is the |format| part doing? What's the equivalent syntax of these pipe symbols in PHP/Java?
They are the variables yielded to the block.
def this_method_takes_a_block
yield(5)
end
this_method_takes_a_block do |num|
puts num
end
Which outputs "5". A more arcane example:
def this_silly_method_too(num)
yield(num + 5)
end
this_silly_method_too(3) do |wtf|
puts wtf + 1
end
The output is "9".
This was very strange to me too at first, but I hope this explanation/walkthru helps you.
The documentation touches the subject, in a quite good way - if my answer doesn't help I am sure their guide will.
First, fire up the Interactive Ruby interpreter by typing irb in your shell and hitting Enter.
Type something like:
the_numbers = ['ett','tva','tre','fyra','fem'] # congratulations! You now know how to count to five in Swedish.
just so that we have an array to play with. Then we create the loop:
the_numbers.each do |linustorvalds|
puts linustorvalds
end
It will output all the numbers, separated by newlines.
In other languages you'd have to write something like:
for (i = 0; i < the_numbers.length; i++) {
linustorvalds = the_numbers[i]
print linustorvalds;
}
The important things to note are that the |thing_inside_the_pipes| can be anything, as long as you are using it consistently. And understand that it is loops we are talking about, that was a thing I didn't get until later on.
#names.each do |name|
puts "Hello #{name}!"
end
at http://www.ruby-lang.org/en/documentation/quickstart/4/ is accompanied by this explanation:
each is a method that accepts a block of code then runs that block of code for every element in a list, and the bit between do and end is just such a block. A block is like an anonymous function or lambda. The variable between pipe characters is the parameter for this block.
What happens here is that for every entry in a list, name is bound to that list element, and then the expression puts "Hello #{name}!" is run with that name.
The code from the do to the end defines a Ruby block. The word format is a parameter to the block. The block is passed along with the method call, and the called method can yield values to the block.
See any text on Ruby for details, this is a core feature of Ruby that you will see all the time.
The equivalent in Java would be something like
// Prior definitions
interface RespondToHandler
{
public void doFormatting(FormatThingummy format);
}
void respondTo(RespondToHandler)
{
// ...
}
// Equivalent of your quoted code
respondTo(new RespondToHandler(){
public void doFormatting(FormatThingummy format)
{
format.html();
format.xml();
}
});
Parameters for a block sit between the | symbols.
To make it even more clearer, if needed:
the pipe bars essentially make a new variable to hold the value generated from the method call prior. Something akin to:
Original definition of your method:
def example_method_a(argumentPassedIn)
yield(argumentPassedIn + 200)
end
How It's used:
example_method_a(100) do |newVariable|
puts newVariable;
end
It's almost the same as writing this:
newVariable = example_method_a(100)
puts newVariable
where, newVariable = 200 + 100 = 300 :D!

Resources