I am working on the following problem:
describe "some silly block functions" do
describe "reverser" do
it "reverses the string returned by the default block" do
result = reverser do
"hello"
end
expect(result).to eq("olleh")
end
From my understanding this should reverse a string. My code is as follows:
def reverser
yield "hello"
end
reverser do |i|
puts i.reverse
end
This simply returns "hello". I may be missing some fundamental concepts here about how yield, blocks, and functions all interact. How do I going about doing what I am trying to accomplish?
The answers are good and correct but perhaps it still do not help.
You should start with your spec:
it "reverses the string returned by the default block"
So, it's very clear what your method should do:
def reverser
# should reverse the string returned by the default block
end
Let's now see how to achieve it. Ok, it should reverse something. But what? Let's see:
string returned by the default block
This suggests that we need to execute the default block and get its returned value. Let's see what the docs say:
yield - Called from inside a method body, yields control to the code block (if any) supplied as part of the method call. ... The value of a call to yield is the value of the executed code block.
So, it seems that your method needs to perform a yield. It will execute a block and return the value the block returns. So, just put a yield there.
def reverser
yield
end
If you run your spec, it will complain - you will see that the string is still not reversed. So, that's whats left for your method to do:
def reverser
yield.reverse
end
and that's it.
You need to include the logic of reversing the string in reverser.
def reverser
yield.reverse
end
But why bothering using block anyway? It's much clearer to use a normal parameter.
def reverser(str)
str.reverse
end
reverser('hello') #=> olleh
If you want to put the string to reverse in the block, then you need to get the result of calling the block and reverse it.
def reverser(&block)
block.call.reverse
end
irb(main):009:0> result = reverser do
irb(main):010:1* "hello"
irb(main):011:1> end
=> "olleh"
I know it's been a year but this hasn't been answered right.
def reverser
out = []
yield.split.each{|word| out << word.reverse}
out.join(" ")
end
I'm pretty sure it has to do with scope
I agree with the above responses - they make the most sense. but want to add why your code isn't working and how to fix it:
expect(result).to eq("olleh")
So according to that you want result to return a string. Is it doing that?
puts returns nil. when you have puts at the end of a method - be aware that the method will return nil. It's insidious because sometimes the results are not what is expected.
but you are expecting it to return 'olleh'
get rid of the puts and it should work like you expect (untested)
def reverser
yield "hello"
end
reverser do |i|
i.reverse # NOTE THAT THE PUTS is missing here
end
I think that's what you are looking for.
edit: Please test and let me know because some folks think I have the answer completely wrong! of course you'd not want to rely on the particular block that you are using as a design point, but this should give you an idea of why it wasn't working
Related
Can't figure out what code to put in "05_silly_blocks" to call the string from the block reverser that is stored in the variable result.
require "05_silly_blocks"
describe "some silly block functions" do
describe "reverser" do
it "reverses the string returned by the default block" do
result = reverser do
"hello"
end
result.should == "olleh"
end
Also does it make a difference is the block is not stored in a variable like result?
I'm not sure I exactly understand what you are trying to accomplish with the blocks, but if you want to reverse a String in Ruby you can just say "hello".reverse.
The reverser method would look something like this:
def reverser
yield.reverse
end
This is a dangerous implementation however, as it assumes that whatever is passed to the block will return a String at the very end.
Within a method, I want to dynamically evaluate the following code chunk with a regex:
if (/^[A-F][A-Z]*[^\.\*]$/).match(some_value)
The method I attempted is this:
def check(val)
if (/^[val][A-Z]*[^\.\*]$/).match(some_value)
puts "foo"
else
puts "waa"
end
end
check("A-F")
The value I am passing in is not making it there correctly. It appears that passing a value in this fashion needs something more. Is this not something you can do with a method?
You expected string interpolation. To do that, you need to use the interpolation syntax #{}:
def check(val)
if (/^[#{val}][A-Z]*[^\.\*]$/).match(some_value)
puts "foo"
else
puts "waa"
end
end
I'm working on some homework problems for a ruby course and I've been having some trouble with my answer. Basically I need to build a program that can satisfy these conditions:
describe "reverser" do
it "reverses the string returned by the default block" do
result = reverser do
"hello"
end
result.should == "olleh"
end
it "reverses each word in the string returned by the default block" do
result = reverser do
"hello dolly"
end
result.should == "olleh yllod"
end
end
I puzzled together some code that I feel should satisfy these conditions:
reverser = Proc.new do |string|
words = string.split(/\b/)
answer = ''
i = 0
while i < words.count
answer = answer + words[i].reverse
i += 1
end
answer
end
def reverser
yield
end
Yet when I run the rake, my error tells me I have failed the first condition.
expected: "olleh"
got: "hello"
Is there something I'm missing? Do I just not have a proper understanding of procs?
This question has been asked in some form already by a member named pete and answered quite well by another user named mind.blank. This is the source:
Beginner RSpec: Need help writing Ruby code to pass RSpec tests (Silly Blocks exercise).
mind.blank's code was straightforward and worked properly, but I don't just want to copy it without understanding why mine doesn't work. Thanks in advance for any help.
So - what you've got there is a local variable named "reverser" and a method named "reverser" (which is going to "shadow" the reverser local)
Your code is never executing.
So - what you want to do is ... take the result of the yield and do the reverse operation on that. (Leaving aside how bizarre that requirement is for a moment.)
def reverser
string = yield
# ... all of your reverser code ...
end
Now circling back around - that's a bizarre way to use a block. A block is for passing additional execution to a method, not for passing argument to it. So if you wanted to say, have a callback executed for each character in reverser (in reverse?) that would be the proper use of yield.
Besides, Ruby already has a String#reverse method - so the easiest possible thing to do to get your tests to pass is something like.
def reverser
(yield).split(/\b/).map(&:reverse).join
end
Your reverser proc does work, if you say reverser.call('abc') (or reverser['abc'] or reverser.('abc')) then you will get the expected 'cba' back.
The problem is that your test isn't using your reverser proc, it is using your reverser method. This is a call to the reverser method with a block:
result = reverser do
"hello"
end
but the reverser method doesn't do anything interesting, it just yields to the block:
def reverser
yield
end
so result ends up being the block's value and that's 'hello'.
Either test the proc:
it "reverses the string returned by the default block" do
result = reverser['hello']
result.should == "olleh"
end
or move the proc's guts into the reverser method and test that:
def reverser
string = yield
words = string.split(/\b/)
answer = ''
i = 0
while i < words.count
answer = answer + words[i].reverse
i += 1
end
answer
end
There are better ways to write this code (such as words.map(&:reverse).join instead of the while look) but your reversing logic works, you just have to make sure you call the right version.
This code will reverse strings given to the method as a block
def reverser
# yield is the string given in the block
words = yield.split(' ')
final = []
words.each do |word|
final.push(word.reverse)
end
final.join(' ')
end
Is it possible to create a method in Ruby that takes a block and runs each statement in the block until one of them returns false?
done = prepare_it do |x|
method_1
method_2
method_3
end
puts "Something did not work" unless done
I wanted the function prepare_it to run each statement but if one of them fails, the function quits. If the function returns true, it means that all steps were successful.
This seems to be calling for an Exception, but I'm not sure how to trigger and process it. I wish I did not have to modify the method_x functions to throw an Exception upon failure, but rather make prepare_it throw the exception if one of the method_x fails (returns false)
Edit:
Thanks for the suggestions, I think I need to add more details to my question. This is my first stab at metaprogramming and creating a DSL. :)
I have a class to send commands to a router. There are many steps that always need to be executed in sequence (connect, login, pass, etc). I thought it would be nice to give the user the ability to change the order of the commands if they wish to do so. For example, some routers don't ask for a user and go directly to the password prompt. Others skip the login altogether. In some cases you need to elevate the privilege or set terminal options.
However, there should be a mechanism to fail the entire block if any of the commands failed. So, if for some reason the password failed (the method returns false/nil), it makes no sense to continue with the rest of the commands.
And I should flag it that something failed.
The && method works, but I don't think it would be a nice interface.
Maybe instead of getting a big block, maybe I should force users to give me smaller blocks, one at a time, which are put in a stack and a run command yields then one by one?
my_router.setup do {send_pass}
my_router.setup do {set_terminal}
my_router.setup do {enable_mode}
my_router.run_setup
I think it would be super cleaner if I could do
my_router.setup do |cmd|
cmd.send_pass
cmd.set_terminal
end
puts "Done" if my_router.ready?
So any magic trickery that happens behind the scene is welcome! :)
Solution
done = prepare_it do |x|
method_1 && method_2 && method_3
end
puts "Something did not work" unless done
Explanation
The && operator "short-circuits" so if method_1 returns false, 2 and 3 won't be called and done will be false.
Example
http://gist.github.com/1115117
There isn't a good way to hack things to work like that. I would do something like one of the following:
done = prepare_it do |x|
method_1 && method_2 && method_3
end
Now, in some specific cases, you can do magic trickery to make this work. For instance, if the methods are supposed to be called on the block argument x: you could do something like this:
class SuccessProxy
def initialize(obj)
#object = obj
end
def method_missing(meth, *args, &block)
#object.send(meth, *args, &block) || raise SuccessProxy::Exception
end
class Exception < ::Exception
end
end
def prepare_it
# however you usually generate x
yield SuccessProxy.new(x)
rescue SuccessProxy::Exception
false
end
prepare_it do |x|
x.method_1
x.method_2
x.method_3
end
Or alternatively, if the methods are to be called just like method_1 on the default object in context, you could use instance_eval instead of yield to put the proxy in scope.
Finally, if you really wanted to be fancy, you could actually parse the separate statements out using a Ruby parser.
But honestly, I'm not convinced you really want to be going to metaprogramming as a tool for this. Maybe if you provide better detail, I can be more helpful as to what the best solution is, but it's probably similar to the first code sample above.
Naturally compactness will be your highest priority, so I believe this is the best answer:
done = (1..3).all? { |n| send "method_#{n}" }
puts "Something did not work" unless done
If you use exceptions, it's not as pretty, but you don't have to rely on returning values:
done = prepare_it do |x|
begin
method_1
method_2
method_3
true
rescue
false
end
end
puts "Something did not work" unless done
def method_1
# No issues, doesn't matter what we return
return true
end
def method_2
# Uh-oh, problem
raise
end
At first I thought, "wouldn't it be nice if this was Lisp..." but that made me realize the exact problem. Doing what you want (executing each step in a block until one is false) would be nice, but requires the access to the AST.
Your idea to do
my_router.setup do {send_pass}
my_router.setup do {set_terminal}
my_router.setup do {enable_mode}
my_router.run_setup
is trying to do exactly what you would do in Lisp--build a list and hand the list off to something else to execute each thing until you get a false return value.
If you're just calling methods in your class without any arguments, then how about as a workaround defining something that takes symbols?:
class Router
def run_setup *cmds
# needs error handling
while c = cmds.shift
return false unless send(c)
end
end
end
my_router.run_setup :send_pass, :set_terminal, :enable_mode
class MyClass
def test
...
end
end
tmp = MyClass.new
tmp.test do |t|
"here"
end
Why am I getting the error
multiple values for a block parameter (0 for 1)
Here is a slightly longer example, based on your code:
class MyClass
def test
yield self
end
def my_own_puts s
puts s
end
end
tmp = MyClass.new
tmp.test do |t|
t.my_own_puts "here"
end
Running this code will output "here".
What is happening is there is a method test that can take a block of code, so you can call it with the do .. end syntax. Because it is passing an arg to yield, that arg is available to the block, so you give this to the block using the do |some_arg_name| ... end syntax.
The yield is where the block gets executed in the test method, and in this case I to yield I pass in self. Since the block now has access to self (an instance of MyClass), the block can call the my_own_puts method on it, and print out "here".
if test is defined with a yield statement, when that statement is reached and if there is a parameter on the yield statement, that parameter will be put into the block variable t. Thus if you have:
def test
.....
yield x
......
end
then x will be the value of t when yield is executed.
With your help, I was able to get the code working like this
class MyClass
def test
a = yield self
puts a
end
end
tmp = MyClass.new
tmp.test do |t|
"here"
end
Thanks, I had to tweak your code a bit but it works the way I wanted to now.
Passing a block to a function (as Bob shows in his answer) is overkill in this case. If you are reading in a string and printing it out, all you should need is something like:
class MyClass
def test(a)
puts a
end
end
tmp = MyClass.new
tmp.test("here")
Using a block might function correctly, but you are calling a lot of unnecessary code and making the true nature of your code unclear.
Proper block usage aside, let me address the particular error message you are seeing. When you say tmp.test do |t|, Ruby is expecting tmp.test to yield a single value which it will temporarily call t and pass to the block (think like the block is a function and you are passing it the argument your yield statement as a parameter). In your case, the method test method must not be yield-ing anything, thus the message "(0 for 1)" implies that it is seeing zero objects yielded when it is expecting to see one. I don't know what your code for test does, but check and make sure that test yields exactly one value.