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.
Related
The prompt:
Extend the Array class to include a method named my_each that takes a block, calls the block on every element of the array, and then returns the original array.
class Array
def my_each(&prc)
if block_given?
proc.call(self)
else
for i in (0..self.length-1)
puts self[i]
end
end
self
end
end
This is what I put together and I don't have a good understanding of how Blocks/Procs work within this context, but somehow I magically wrote the code that passed 3 of the 4 RSPEC tests.
describe "#my_each" do
it "calls the block passed to it" do
expect do |block|
["test array"].my_each(&block)
end.to yield_control.once
end
it "yields each element to the block" do
expect do |block|
["el1", "el2"].my_each(&block)
end.to yield_successive_args("el1", "el2")
end
it "does NOT call the built-in #each method" do
original_array = ["original array"]
expect(original_array).not_to receive(:each)
original_array.my_each {}
end
it "is chainable and returns the original array" do
original_array = ["original array"]
expect(original_array.my_each {}).to eq(original_array)
end
end
All of the above RSPEC tests passes with the exception of the second one, where my code returns [["el1", "el2"]] when ["el1", "el2"] is expected. Can someone please give me an explanation of how or why I am receiving a nested array here?
Can someone also give me an explanation of how the code is running as a block is passing through this method? I'm not sure if my "else" condition is actually even necessary in the context of the RSPEC tests. I'm generally confused by the concept of passing blocks through self-written methods and how they interact with the method itself.
Thanks in advance!
In the first part of your condition, you pass the whole array to the block:
if block_given?
proc.call(self)
else
# ...
E.g. for an array of ["el1", "el2"] you do proc.call(["el1", "el2"]). What you expect in the test are two consecutive calls:
proc.call("el1")
proc.call("el2")
To do that you need to use a loop also in the first part of the condition and pass there an array element, not the whole array:
if block_given?
for i in (0..self.length-1)
proc.call(self[i])
end
else
for i in (0..self.length-1)
puts self[i]
end
end
proc.call(self)
is the culprit. self is the whole array.
Extend the Array class to include a method named my_each
class Array
def my_each
end
end
that takes a block,
#every method in ruby accepts a block, it is just ignored when not used (yielded to). Do nothing.
calls the block on every element of the array,
class Array
def my_each
for element in self
yield element
end
end
end
and then returns the original array.
# "for" loops do this. Do nothing. It should pass.
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
I am trying to make a simplistic implementation of AOP in ruby. I was able to implement before and after advices, I got stuck with around advice.
This is the target class that is going to be advised:
class MyClass
def method
puts "running method"
end
end
This is the Aspect class to instantiate objects capable of making advices:
class Aspect
def advise(class_name, method, type, &block)
class_name.send(:alias_method, :proceed, :method)
class_name.send(:define_method, :method) do
case type
when :before
yield
proceed
when :after
proceed
yield
when :around
yield(proceed) # * proceed is the old version of the method
end
end
end
end
(*) Yield should execute the block around MyClass#proceed on the current object when method is invoked.
Creating the target and the aspect:
mc = MyClass.new
a = Aspect.new()
Invoking the method without advising it:
puts mc.method
Advising MyClass#method with around:
a.advise(MyClass, :method, :around) do |proceed|
puts "First"
proceed # this is not working *
puts "Last"
end
puts mc.method
(*) I am not being able to pass something to identify the call of proceed, that is the invocation of the old method without the advice.
The output should be:
First
running method
Last
In Ruby, a method call looks like this:
receiver.method(arguments)
Or, you can leave off the receiver if the receiver is self.
So, to call a method named proceed on some receiver, you would write
receiver.proceed
However, in your implementation, you don't keep track of what the receiver should be, so since you don't know the receiver, you simply cannot call the method.
Note that there are lots of other problems with your approach as well. For example, if you advise multiple methods, you will alias them all to the same method, overwriting each other.
I believe there are two things going wrong here.
This section of code
when :around
yield(proceed) # * proceed is the old version of the method
end
Calls the block given to advise providing the output of the proceed method as an argument.
So your output probably looks something like:
running method
First
Last
This block
a.advise(MyClass, :method, :around) do |proceed|
puts "First"
proceed # this is not working *
puts "Last"
end
Just evaluates the argument given as proceed. If a method is given it does not call it. So taking problem 1 into consideration in your case the original definition of method (aliased to proceed) returns nil (output of return) which will be passed as the value to the proceed argument in the block when yielded. the block ends up evaluating to something like
puts "First"
nil
puts "Last"
mc.method is called.
To address the second part, you may want to consider using send. Because the inner workings of your aspect may not be known to your code that calls it. It may change over time, so what ever calls Aspect.advise shouldn't make assumptions that the original method will still be accessible. Instead, it should take an argument (the new method name) and send it to the object. Making the block passed to advise:
a.advise(MyClass, :method, :around) do |aliased_method_name|
puts "First"
send(aliased_method_name)
puts "Last"
end
And adjusting the around item added to your class when advise is called to the following:
when :around
yield(:proceed) # * proceed is the old version of the method
end
If you do both of these things, your around section will calls the provided block, using the symbol for the new alias for the overridden method.
N.B.: This approach won't work for methods that require any arguments.
This is what I did. In the definition of Aspect#advise now I use a Proc, like this:
when :around
yield Proc.new { proceed }
end
And when calling the method to advise MyClass#method with :around parameter I use this:
a.advise(MyClass, :method, :around) do |original|
puts "First"
original.call
puts "Last"
end
I got:
First
running method
Last
Here's the fixed version that will work for arguments, and avoid clobbering.
class Aspect
##count = 0
def self.advise(class_name, method, type=nil, &block)
old_method = :"__aspect_#{method}_#{##count += 1}"
class_name.send(:alias_method, old_method, method)
class_name.send(:define_method, method) do |*args, &callblock|
case type
when :before
yield
send(old_method, *args, &callblock)
when :after
send(old_method, *args, &callblock)
yield
when :around, nil
yield lambda {
send(old_method, *args, &callblock)
}
end
end
end
end
class Foo
def foo(what)
puts "Hello, #{what}!"
end
end
Aspect.advise(Foo, :foo) do |y|
puts "before around"
y.yield
puts "after around"
end
Aspect.advise(Foo, :foo, :before) do
puts "before"
end
Aspect.advise(Foo, :foo, :after) do
puts "after"
end
Foo.new.foo("world")
# before
# before around
# Hello, world!
# after around
# after
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
I have seen here and there code like this one
Specification.new do |s|
s.name = %q{casein}
s.version = "3.1.11"
....
Can someone explain me what is the idea behind "do |s|" ?
This is an example of using blocks in ruby. Block is a chunk of code that you can pass to a method (new is just a regular method).
class Foo
def initialize
puts "doing some work"
yield('Foo') if block_given?
end
end
Foo.new do |s|
puts "output from #{s}"
end
# >> doing some work
# >> output from Foo
Here initializer in Foo calles a block (if one is given) and passes it a parameters. Block then can receive that parameter and do its work.
Educational reading here.
it's an argument that gets exposed to block by initializer of Specification
things probably look like this in that class:
class Specification
def initialize
# ...
yield self
# ...
end
end
that yield executes a block and passes a single argument to it - self (it might be any other object so better go check the source)
code block usually looks like this:
do |arguments, list|
# code here
end
but arguments are optional(unless it's lambda and you will get exception for too few or too many)