What does the & do in front of an argument in Ruby? - ruby-on-rails

I'm doing some Ruby Koan exercises. Since i'm quite a newbie, so some code doesn't seem to make sense for me. For example, the & in front of an argument
def method_with_explicit_block(&block)
block.call(10)
end
def test_methods_can_take_an_explicit_block_argument
assert_equal 20, method_with_explicit_block { |n| n * 2 }
add_one = lambda { |n| n + 1 }
assert_equal 11, method_with_explicit_block(&add_one)
end
Why there's a & before block and add_one? To make them global variables or refer them to the previous variable?
Thank you!

In front of a parameter in method definition, the unary prefix ampersand & sigil means: package the block passed to this method as a proper Proc object.
In front of an argument in method call, the unary prefix ampersand & operator means: convert the object passed as an argument to a Proc by sending it the message to_proc (unless it already is a Proc) and "unroll" it into a block, i.e. treat the Proc as if it had been passed directly as a block instead.

Example in case of procs
multiples_of_3 = Proc.new do |n|
n%3 == 0
end
(1..100).to_a.select(&multiples_of_3)
The "&" here is used to convert proc into block.
Another Example
It’s how you can pass a reference to the block (instead of a local variable) to a method. Ruby allows you to pass any object to a method as if it were a block. The method will try to use the passed in object if it’s already a block but if it’s not a block it will call to_proc on it in an attempt to convert it to a block.
Also note that the block part (without the ampersand) is just a name for the reference, you can use whatever name you like if it makes more sense to you.
def my_method(&block)
puts block
block.call
end
my_method { puts "Hello!" }
#<Proc:0x0000010124e5a8#tmp/example.rb:6>
Hello!
As you can see in the example above, the block variable inside my_method is a reference to the block and it can be executed with the call method. call on the block is the same as using yield, some people like to use block.call instead of yield for better readability.

Related

Test the size of array received is correct

I want to test if a function of a class is called with array of particular length.
Example in below I want to verify if function s3upload was called with array parameter where array size is 2.
ATest_spec.rb is test class. AwsUploader.s3upload is called from letsUpload
Project/lib/First/version.rb
module Documentz
class AwsUploader
def s3upload(event_id:nil, docs: nil)
puts("uploded")
end
end
end
Project/lib/First.rb
module Exporter
class AnExporter
def letsUpload
Documentz::Uploader::AwsUploader.new.s3upload( docs :[1,2])
end
end
end
ATest_spec.rb
it 'helps in mocking a class' do
exp=Exporter::AnExporter.new
exp.letsUpLoad
allow_any_instance_of(Documentz::Uploader::AwsUploader).to receive(:s3upload).with( {:docs=>[1,2]})
## how to check if the array size (:docs)==2
end
As you can notice in ATest_spec.rb I am able to test if the arguments are [1,2] but I actually want to verify that the size of array ( argument received) is actually 2.
Can you please advice how to do that?
Instead of allow_any_instance_of I would use the stub the new method and would return an instance_double on which I spy for the expected method call. To ensure that the argument has a specific structure use a custom matcher that can be as complex as needed, for example:
RSpec::Matchers.define :expected_data_structure do
match { |actual| actual.is_a?(Hash) &&
actual[:docs].is_a?(Array) &&
actual[:docs].size == 2 &&
actual[:docs].all?(Integer)
}
end
subject(:exporter) { Exporter::AnExporter.new }
let(:spy) { instance_double('Documentz::Uploader::AwsUploader') }
before do
allow(Documentz::Uploader::AwsUploader).to receive(:new).and_return(spy)
end
it 'calls `s3upload` with the expected arguments' do
exporter.letsUpLoad
expect(spy).to have_received(:s3upload).with(expected_data_structure)
end
Read about custom matches in the RSpec docs.
Btw. in Ruby by convention method names are written in underscore and not in camelcase. Following that rule, your method should be named lets_up_load (or just upload) instead of letsUpLoad
Here you have mocked with a method to receive specific parameters, additionally, you need to return an array of the docs ids(assuming that's what your s3upload method would return)
allow_any_instance_of(Documentz::Uploader::AwsUploader).to receive(:s3upload).with( {:docs=>[1,2]}).and_return([1,2])
expect(exp.letsUpLoad.length).to eq 2

loop inside Model.new in rails

I know it's a very short question. I understand "{ }" represents a loop.
and the new operator creates a new active record object.
What does this line do in rails? from where does lead come?
Proc.new{|lead| lead.lead_details.name}
It creates new Proc object. lead doest't come from anywhere in this example since this Proc doesn't get called. But you can call that, passing it as a block, for example.
leads = Lead.includes(:lead_details) # I assume it's an AR model, obviously
p = Proc.new { |lead| lead.lead_details.name }
names = leads.map(&p)
This way, lead comes from map method and represent single element of leads array-like object, it's equivalent to this:
leads.map { |lead| lead.lead_details.name }
You can also call this procedure 'by hand', passing argument explicitly, like this:
p.call(leads.first)
# => Whatever is leads.first.lead_details.name
You can even write your own method using it as block, for example:
def first_do(collection)
yield(collection.first)
end
first_do(leads, &p)
# => Whatever is leads.first.lead_details.name

method.to_proc doesn't return from enclosed function

I was trying to DRY up a Rails controller by extracting a method that includes a guard clause to return prematurely from the controller method in the event of an error. I thought this may be possible using a to_proc, like this pure Ruby snippet:
def foo(string)
processed = method(:breaker).to_proc.call(string)
puts "This step should not be executed in the event of an error"
processed
end
def breaker(string)
begin
string.upcase!
rescue
puts "Well you messed that up, didn't you?"
return
end
string
end
My thinking was that having called to_proc on the breaker method, calling the early return statement in the rescue clause should escape the execution of foo. However, it didn't work:
2.4.0 :033 > foo('bar')
This step should not be executed in the event of an error
=> "BAR"
2.4.0 :034 > foo(2)
Well you messed that up, didn't you?
This step should not be executed in the event of an error
=> nil
Can anyone please
Explain why this doesn't work
Suggest a way of achieving this effect?
Thanks in advance.
EDIT: as people are wondering why the hell I would want to do this, the context is that I'm trying to DRY up the create and update methods in a Rails controller. (I'm trying to be agressive about it as both methods are about 60 LoC. Yuck.) Both methods feature a block like this:
some_var = nil
if (some complicated condition)
# do some stuff
some_var = computed_value
elsif (some marginally less complicated condition)
#error_msg = 'This message is the same in both actions.'
render partial: "show_user_the_error" and return
# rest of controller actions ...
Hence, I wanted to extract this as a block, including the premature return from the controller action. I thought this might be achievable using a Proc, and when that didn't work I wanted to understand why (which I now do thanks to Marek Lipa).
What about
def foo(string)
processed = breaker(string)
puts "This step should not be executed in the event of an error"
processed
rescue ArgumentError
end
def breaker(string)
begin
string.upcase!
rescue
puts "Well you messed that up, didn't you?"
raise ArgumentError.new("could not call upcase! on #{string.inspect}")
end
string
end
After all this is arguably a pretty good use case for an exception.
It seems part of the confusion is that a Proc or lambda for that matter are distinctly different than a closure (block).
Even if you could convert Method#to_proc to a standard Proc e.g. Proc.new this would simply result in a LocalJumpError because the return would be invalid in this context.
You can use next to break out of a standard Proc but the result would be identical to the lambda that you have now.
The reason Method#to_proc returns a lambda is because a lambda is far more representative of a method call than a standard Proc
For Example:
def foo(string)
string
end
bar = ->(string) { string } #lambda
baz = Proc.new {|string| string }
foo
#=> ArgumentError: wrong number of arguments (given 0, expected 1)
bar.()
#=> ArgumentError: wrong number of arguments (given 0, expected 1)
baz.()
#=> nil
Since you are converting a method to a proc object I am not sure why you would also want the behavior to change as this could cause ambiguity and confusion. Please note that for this reason you can not go in the other direction either e.g. lambda(&baz) does not result in a lambda either as metioned Here.
Now that we have explained all of this and why it shouldn't really be done, it is time to remember that nothing is impossible in ruby so this would technically work:
def foo(string)
# place assignment in the guard clause
# because the empty return will result in `nil` a falsey value
return unless processed = method(:breaker).to_proc.call(string)
puts "This step should not be executed in the event of an error"
processed
end
def breaker(string)
begin
string.upcase!
rescue
puts "Well you messed that up, didn't you?"
return
end
string
end
Example

metaprograming String#scan and globals?

My goal is to replace methods in the String class with other methods that do additional work (this is for a research project). This works for many methods by writing code in the String class similar to
alias_method :center_OLD, :center
def center(args*)
r = self.send(*([:center_OLD] + args))
#do some work here
#return something
end
For some methods, I need to handle a Proc as well, which is no problem. However, for the scan method, invoking it has the side effect of setting special global variables from the regular expression match. As documented, these variables are local to the thread and the method.
Unfortunately, some Rails code makes calls to scan which makes use of the $& variable. That variable gets set inside my version of the scan method, but because it's local, it doesn't make it back to the original caller which uses the variable.
Does anyone know a way to work around this? Please let me know if the problem needs clarification.
If it helps at all, all the uses I've seen so far of the $& variable are inside a Proc passed to the scan function, so I can get the binding for that Proc. However, the user doesn't seem to be able to change $& at all, so I don't know how that will help much.
Current Code
class String
alias_method :scan_OLD, :scan
def scan(*args, &b)
begin
sargs = [:scan_OLD] + args
if b.class == Proc
r = self.send(*sargs, &b)
else
r = self.send(*sargs)
end
r
rescue => error
puts error.backtrace.join("\n")
end
end
end
Of course I'll do more things before returning r, but this even is problematic -- so for simplicity we'll stick with this. As a test case, consider:
"hello world".scan(/l./) { |x| puts x }
This works fine both with and without my version of scan. With the "vanilla" String class this produces the same thing as
"hello world".scan(/l./) { puts $&; }
Namely, it prints "ll" and "ld" and returns "hello world". With the modified string class it prints two blank lines (since $& was nil) and then returns "hello world". I'll be happy if we can get that working!
You cannot set $&, because it is derived from $~, the last MatchData.
However, $~ can be set and that actually does what you want.
The trick is to set it in the block binding.
The code is inspired by the old Ruby implementation of Pathname.
(The new code is in C and does not need to care about Ruby frame-local variables)
class String
alias_method :scan_OLD, :scan
def scan(*args, &block)
sargs = [:scan_OLD] + args
if block
self.send(*sargs) do |*bargs|
Thread.current[:string_scan_matchdata] = $~
eval("$~ = Thread.current[:string_scan_matchdata]", block.binding)
yield(*bargs)
end
else
self.send(*sargs)
end
end
end
The saving of the thread-local (well, actually fiber-local) variable seems unnecessary since it is only used to pass the value and the thread never reads any other value than the last one set. It probably is there to restore the original value (most likely nil, because the variable did not exist).
One way to avoid thread-locals at all is to create a setter of $~ as a lambda (but it does create a lambda for each call):
self.send(*sargs) do |*bargs|
eval("lambda { |m| $~ = m }", block.binding).call($~)
yield(*bargs)
end
With any of these, your example works!
I wrote simple code simulating the problem:
"hello world".scan(/l./) { |x| puts x }
"hello world".scan(/l./) { puts $&; }
class String
alias_method :origin_scan, :scan
def scan *args, &b
args.unshift :origin_scan
#mutex ||= Mutex.new
begin
self.send *args do |a|
break if !block_given?
#mutex.synchronize do
p $&
case b.arity
when 0
b.call
when 1
b.call a
end
end
end
rescue => error
p error, error.backtrace.join("\n")
end
end
end
"hello world".scan(/l./) { |x| puts x }
"hello world".scan(/l./) { puts $& }
And found the following. The change of containment of the variable $& became inside a :call function, i.e. on 3-rd step before :call $& contains a valid value, but inside the block it becomes the invalid. I guess this become due to the singularity stack and variable restoration during the change process/thread context, because, probably, :call function can't access the :scan local state.
I see two variants: the first is to avoid to use global variables in the specific function redefinitions, and second, may to dig sources of ruby more deeply.

How to get a list of the arguments a method is called with

How do I get a list of the arguments passed to a method, preferably one that I can iterate through?
For example something like
def foo(a,b,c)
puts args.inspect
end
foo(1,2,3)
=> [1,2,3]
?
Thanks!
You can always define a method that takes an arbitrary number of arguments:
def foo(*args)
puts args.inspect
end
This does exactly what you want, but only works on methods defined in such a manner.
The *args notation means "zero or more arguments" in this context. The opposite of this is the splat operator which expands them back into a list, useful for calling other methods.
As a note, the *-optional arguments must come last in the list of arguments.
If you define your method as you specified, you'll always have 3 args, or the method call is invalid. So "all the args" is already defined for you. So you would just change your method to:
def foo(a,b,c)
[a, b, c]
end
To define a method that can be called with any args (and to then access those args) you can do something like this:
def foo(*args)
args
end
What the * does is put all args after that point into an array.
As others pointed out you can use the splat operator (*) for achieving what you want. If you don't like that, you can use the fact that Ruby methods can take a hash as last argument with nicer syntax.
def foo(args)
raise ArgumentError if args.keys.any? { |arg| arg.nil? || !arg.kind_of?(Integer) }
end
puts foo(:a => 1, :b => 2, :c => "a") # raise an ArgumentError
To access the arguments inside the method you have to use args[:a] etc.

Resources