Parameter passing and assert_difference - ruby-on-rails

I am new to both Ruby and Rails. I don't understand why the following code (which uses Rails' [ActiveSupport::Testing.assert_difference]1 method) doesn't require a comma after the parameter 1. The code comes from Chapter 7 of the Rails Tutorial.
assert_difference 'User.count', 1 do
post_via_redirect users_path, ...
end
The signature for assert_difference is:
assert_difference(expression, difference = 1, message = nil, &block)
thus I would expect that a comma would be required between the difference parameter and the block parameter but that is obviously not the case.
Why is the comma not required?

Blocks aren't really parameters - what shows up in the method signature is that this method captures the block passed to it in a proc, but that is really an implementation detail that is leaked to the outside world. For example if you define a method like this
def foo(*args)
end
then blocks passed to this method don't end up in args.
However if you are passing a proc (or something that responds to to_proc), using the & argument prefix that you wish for this argument to be used as the method's block then you do need the comma.
my_proc = -> {post_via_redirect users_path}
assert_difference User.count, 1, &my_proc

Because you are passing the block using the special do |args| ... end/{ |args| ... } notation. If you pass the block as a normal argument you need the comma:
block = proc { post_via_redirect users_path, ... }
assert_difference 'User.count', 1, &block

Comma is not needed because this is how Ruby syntax works.
Block can be passed to method in 2 ways
1) Using do ... end
2) Using { ... }
def some_method(&block)
block.call
end
some_method { 2 + 2 }
#=> 4
some_method do
2 + 2
end
#=> 4
Try this examples in console and you will understand them.

Related

After Rails Proc turns a block into an object, why can a class call it?

I got a sample code that turns a block into an object using Proc.new so that it can be executed independently. There are two puts at the end to print the result, in this example, the array is brought into the block for execution. At this point, I have something that I don't understand.
In the following code, why is .iterate!(square) valid? They are the method defined in the class and the name of a separate block object. Why can they be written together?
Second, what is the working process of def iterate!(code)? Why is the value being carried into (code)? And what are self and code.call here?
square = Proc.new do |n|
n ** 2
end
class Array
def iterate!(code)
self.map {|n| code.call(n)}
end
end
puts [1, 2, 3].iterate!(square)
puts [4, 5, 6].iterate!(square)
I'm a beginner, please explain as detailed as possible, thank you.
square = Proc.new do |n|
n ** 2
end
This is a simple proc, which expect an integer argument, and return square of the argument.
eg:
square.call(5) => 25
square.(5) => 25
Now, You have opened Array class and added iterate! method which takes an argument. and the method just work on self (self here refers to same array on which you are calling the iterate method. here self = [1,2,3] in your first case and [4,5,6] in you second case.
in the iterate! method definition, you are mapping/looping the array elements and you are calling the square proc with each element as arguments.
So it is more like
square.call(1) => 1
square.call(2) => 4
square.call(3) => 9

What does the & do in front of an argument in Ruby?

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.

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.

What does the * (asterisk) symbol do near a function argument and how to use that in others scenarios?

I am using Ruby on Rails 3 and I would like to know what means the presence of a * operator near a function argument and to understand its usages in others scenarios.
Example scenario (this method was from the Ruby on Rails 3 framework):
def find(*args)
return to_a.find { |*block_args| yield(*block_args) } if block_given?
options = args.extract_options!
if options.present?
apply_finder_options(options).find(*args)
else
case args.first
when :first, :last, :all
send(args.first)
else
find_with_ids(*args)
end
end
end
This is the splat operator, which comes from ruby (and is thus not rails specific). It can be applied in two ways depending on where it is used:
to "pack" a number of arguments into an array
to split up an array into an argument list
In your function, you see the splat operator used in the function definition. The result is that the function accepts any number of arguments. The complete argument list will be put into args as an array.
def foo(*args)
args.each_with_index{ |arg, i| puts "#{i+1}. #{arg}" }
end
foo("a", "b", "c")
# 1. a <== this is the output
# 2. b
# 3. c
The second variant would be when you consider the following method:
def bar(a, b, c)
a + b + c
end
It requires exactly three arguments. You can now call this method like follows
my_array = [1, 2, 3]
bar(*my_array)
# returns 6
The splat applied in this case to the array will split it and pass each element of the array as an individual parameter to the method. You could do the same even by calling foo:
foo(*my_array)
# 1. 1 <== this is the output
# 2. 2
# 3. 3
As you can see in your example method, these rules do apply to block parameters in the same way.
This is a splat argument, which basically means that any 'extra' arguments passed to the method will all be assigned to *args.

Assert difference of number of children in relationship in Ruby on Rails

My controller is able to create a child book_loan. I am trying to test this behavior in a functional test but am having a hard time using the assert_difference method. I've tried a number of ways of passing the count of book_loans to assert_difference with no luck.
test "should create loan" do
#request.env['HTTP_REFERER'] = 'http://test.com/sessions/new'
assert_difference(books(:ruby_book).book_loans.count, 1) do
post :loan, {:id => books(:ruby_book).to_param,
:book_loan => {:person_id => 1,
:book_id =>
books(:dreaming_book).id}}
end
end
can't convert BookLoan into String
assert_difference(books(:ruby_book).book_loans,:count, 1)
NoMethodError: undefined method 'book_loans' for #
assert_difference('Book.book_loans.count', +1)
can't convert Proc into String
assert_difference( lambda{books(:ruby_book).book_loans.count}, :call, 1 )
It looks like assert_difference expects a string, which it will eval before and after the block. So the following may work for you:
assert_difference('books(:ruby_book).book_loans.count', 1) do
...
end
I was having trouble with this too and just figured out how this works. Like the original post, I too was trying something like this:
# NOTE: this is WRONG, see below for the right way.
assert_difference(account.users.count, +1) do
invite.accept(another_user)
end
This doesn't work because there is no way for assert_difference to perform an action before it runs the block and after it runs the block.
The reason the string works is that the string can be evaluated to determine if the expected difference resulted.
But a string is a string, not code. I believe a better approach is to pass something that can be called. Wrapping the expression in a lambda does just that; it allows assert_difference to call the lambda to verify the difference:
assert_difference(lambda { account.users.count }, +1) do
invite.accept(another_user)
end

Resources