Ruby method with optional options and &block parameter - ruby

Hey there
Is it possible to have optional attributes and a block as parameters
for a method call?
Example: I have to call
method(foo, foo: bar, -> { do_something }
and tried it with
def method(foo, *bar, &block)
end
As for my understanding the block always has to be at last position?
After a bit of research I found out the unary(?) * seems to be for
arrays. Since I try to pass a Hash I changed the code to
def method(foo, bar={}, &block)
end
But this doesn't do the trick either. I guess its because he cant
figure out where the bar ends and the block starts.
Any ideas or suggestions? Thank you in advance
Append: Just for the curious why I need this. We have a big json
schema running and have a small DSL that builds the json from the
model definitation. Without going to much into detail we wanted to
implement exportable_scopes.
class FooBar
exportable_scope :some_scope, title: 'Some Scope', -> { rewhere archived: true }
end
On some initializer this is supposed to happens:
def exportable_scope scope, attributes, &block
scope scope block
if attributes.any?
attributes.each do |attribute|
exportable_schema.scopes[scope] = attribute
end
else
exportable_schema.scopes[scope] = {title: scope}
end
end
So this is working fine, I just need a hint for the method
parameters.

Yes, it is possible.
When mixing different kinds of parameters, they have to be included in the method definition in a specific order:
Positional parameters (required and optional) and a single splat parameter, in any order;
Keyword parameters (required and optional), in any order;
Double splat parameter;
Block parameter (prefixed with &);
The order above is somewhat flexible. We could define a method and begin the parameter list with a single splat argument, then a couple of optional positional arguments, and so on. Even though Ruby allows that, it's usually a very bad practice as the code would be hard to read and even harder to debug. It's usually best to use the following order:
Required positional parameters;
Optional positional parameters (with default values);
Single splat parameter;
Keyword parameters (required and optional, their order is irrelevant);
Double splat parameter;
Explicit block parameter (prefixed with &).
Example:
def meditate cushion, meditation="kinhin", *room_items, time: , posture: "kekkafuza", **periods, &b
puts "We are practicing #{meditation}, for #{time} minutes, in the #{posture} posture (ouch, my knees!)."
puts "Room items: #{room_items}"
puts "Periods: #{periods}"
b.call # Run the proc received through the &b parameter
end
meditate("zafu", "zazen", "zabuton", "incense", time: 40, period1: "morning", period2: "afternoon" ) { puts "Hello from inside the block" }
# Output:
We are practicing zazen, for 40 minutes, in the kekkafuza posture (ouch, my knees!).
Room items: ["zabuton", "incense"]
Periods: {:period1=>"morning", :period2=>"afternoon"}
Hello from inside the block
Notice that when calling the method, we have:
Provided the cushion mandatory positional argument;
Overwritten the default value of the meditation optional positional argument;
Passed a couple of extra positional arguments (zabuton and incense) through the *room_items parameter;
Provided the time mandatory keyword argument;
Omitted the posture optional keyword argument;
Passed a couple of extra keyword arguments (period1: "morning", period2: "afternoon") through the **periods parameter;
Passed the block { puts "Hello from inside the block" } through the &b parameter;
Please note the example above servers only to illustrate the possibility of mixing different types of parameters. Building a method like this in real code would be a bad practice. If a method needs that many arguments, it's probably best to split it into smaller methods. If it's absolutely necessary to pass that much data to a single method, we should probably create a class to store the data in a more organized way, then pass an instance of that class to the method as a single argument.

Related

why pass block arguments to a function in ruby?

I'm unclear on why there is a need to pass block arguments when calling a function.
why not just pass in as function arguments and what happens to the block arguments, how are they passed and used?
m.call(somevalue) {|_k, v| v['abc'] = 'xyz'}
module m
def call ( arg1, *arg2, &arg3)
end
end
Ruby, like almost all mainstream programming languages, is a strict language, meaning that arguments are fully evaluated before being passed into the method.
Now, imagine you want to implement (a simplified version of) Integer#times. The implementation would look a little bit like this:
class Integer
def my_times(action_to_be_executed)
raise ArgumentError, "`self` must be non-negative but is `#{inspect}`" if negative?
return if zero?
action_to_be_executed
pred.my_times(action_to_be_executed)
end
end
3.my_times(puts "Hello")
# Hello
0.my_times(puts "Hello")
# Hello
-1.my_times(puts "Hello")
# Hello
# ArgumentError (`self` must be non-negative but is `-1`)
As you can see, 3.my_times(puts "Hello") printed Hello exactly once, instead of thrice, as it should do. Also, 0.my_times(puts "Hello") printed Hello exactly once, instead of not at all, as it should do, despite the fact that it returns in the second line of the method, and thus action_to_be_executed is never even evaluated. Even -1.my_times(puts "Hello") printed Hello exactly once, despite that fact that it raises an ArgumentError exception as the very first thing in the method and thus the entire rest of the method body is never evaluated.
Why is that? Because Ruby is strict! Again, strict means that arguments are fully evaluated before being passed. So, what this means is that before my_times even gets called, the puts "Hello" is evaluated (which prints Hello to the standard output stream), and the result of that evaluation (which is just nil because Kernel#puts always returns nil) is passed into the method.
So, what we need to do, is somehow delay the evaluation of the argument. One way we know how to delay evaluation, is by using a method: methods are only evaluated when they are called.
So, we take a page out of Java's playbook, and define a Single Abstract Method Protocol: the argument that is being passed to my_each must be an object which implements a method with a specific name. Let's call it call, because, well, we are going to call it.
This would look a little bit like this:
class Integer
def my_times(action_to_be_executed)
raise ArgumentError, "`self` must be non-negative but is `#{inspect}`" if negative?
return if zero?
action_to_be_executed.call
pred.my_times(action_to_be_executed)
end
end
def (hello = Object.new).call
puts "Hello"
end
3.my_times(hello)
# Hello
# Hello
# Hello
0.my_times(hello)
-1.my_times(hello)
# ArgumentError (`self` must be non-negative but is `-1`)
Nice! It works! The argument that is passed is of course still strictly evaluated before being passed (we can't change the fundamental nature of Ruby from within Ruby itself), but this evaluation only results in the object that is bound by the local variable hello. The code that we want to run is another layer of indirection away and will only be executed at the point where we actually call it.
It also has another advantage: Integer#times actually makes the index of the current iteration available to the action as an argument. This was impossible to implement with our first solution, but here we can do it, because we are using a method and methods can take arguments:
class Integer
def my_times(action_to_be_executed)
raise ArgumentError, "`self` must be non-negative but is `#{inspect}`" if negative?
__my_times_helper(action_to_be_executed)
end
protected
def __my_times_helper(action_to_be_executed, index = 0)
return if zero?
action_to_be_executed.call(index)
pred.__my_times_helper(action_to_be_executed, index + 1)
end
end
def (hello = Object.new).call(i)
puts "Hello from iteration #{i}"
end
3.my_times(hello)
# Hello from iteration 0
# Hello from iteration 1
# Hello from iteration 2
0.my_times(hello)
-1.my_times(hello)
# ArgumentError (`self` must be non-negative but is `-1`)
However, this is not actually very readable. If you didn't want to give a name to this action that we are trying to pass but instead simply literally write it down inside the argument list, it would look something like this:
3.my_times(Object.new.tap do |obj|
def obj.call(i)
puts "Hello from iteration #{i}"
end
end)
# Hello from iteration 0
# Hello from iteration 1
# Hello from iteration 2
or on one line:
3.my_times(Object.new.tap do |obj| def obj.call; puts "Hello from iteration #{i}" end end)
# Hello from iteration 0
# Hello from iteration 1
# Hello from iteration 2
# or:
3.my_times(Object.new.tap {|obj| def obj.call; puts "Hello from iteration #{i}" end })
# Hello from iteration 0
# Hello from iteration 1
# Hello from iteration 2
Now, I don't know about you, but I find that pretty ugly.
In Ruby 1.9, Ruby added Proc literals aka stabby lambda literals to the language. Lambda literals are a concise literal syntax for writing objects with a call method, specifically Proc objects with Proc#call.
Using lambda literals, and without any changes to our existing code, it looks something like this:
3.my_times(-> i { puts "Hello from iteration #{i}" })
# Hello from iteration 0
# Hello from iteration 1
# Hello from iteration 2
This does not look bad!
When Yukihiro "matz" Matsumoto designed Ruby almost thirty years ago in early 1993, he did a survey of the core libraries and standard libraries of languages like Smalltalk, Scheme, and Common Lisp to figure out how such methods that take a piece of code as an argument are actually used, and he found that the overwhelming majority of such methods take exactly one code argument and all they do with that argument is call it.
So, he decided to add special language support for a single argument that contains code and can only be called. This argument is both syntactically and semantically lightweight, in particular, it looks syntactically exactly like any other control structure, and it is semantically not an object.
This special language feature, you probably guessed it, are blocks.
Every method in Ruby has an optional block parameter. I can always pass a block to a method. It's up to the method to do anything with the block. Here, for example, the block is useless because Kernel#puts doesn't do anything with a block:
puts("Hello") { puts "from the block" }
# Hello
Because blocks are not objects, you cannot call methods on them. Also, because there can be only one block argument, there is no need to give it a name: if you refer to a block, it's always clear which block because there can be only one. But, if the block doesn't have methods and doesn't have a name, how can we call it?
That's what the yield keyword is for. It temporarily "yields" control flow to the block, or, in other words, it calls the block.
With blocks, our solution would look like this:
class Integer
def my_times(&action_to_be_executed)
raise ArgumentError, "`self` must be non-negative but is `#{inspect}`" if negative?
return enum_for(__callee__) unless block_given?
__my_times_helper(&action_to_be_executed)
end
protected
def __my_times_helper(&action_to_be_executed, index = 0)
return if zero?
yield index
pred.__my_times_helper(&action_to_be_executed, index + 1)
end
end
3.my_times do
puts "Hello from iteration #{i}"
end
# Hello from iteration 0
# Hello from iteration 1
# Hello from iteration 2
0.my_times do
puts "Hello from iteration #{i}"
end
-1.my_times do
puts "Hello from iteration #{i}"
end
# ArgumentError (`self` must be non-negative but is `-1`)
Okay, you might notice that I simplified a bit when I wrote above that the only thing you can do with a block is call it. There are two other things you can do with it:
You can check whether a block argument was passed using Kernel#block_given?. Since blocks are always optional, and blocks have no names, there must be a way to check whether a block was passed or not.
You can "roll up" a block (which is not an object and doesn't have a name) into a Proc object (which is an object) and bind it to a parameter (which gives it a name) using the & ampersand unary prefix sigil in the parameter list of the method. Now that we have an object, and a way to refer to it, we can store it in a variable, return it from a method, or (as we are doing here) pass it along as an argument to a different method, which otherwise wouldn't be possible.
There is also the opposite operation: with the & ampersand unary prefix operator, you can "unroll" a Proc object into a block in an argument list; this makes it so that the method behaves as if you had passed the code that is stored inside the Proc as a literal block argument to the method.
And there you have it! That's what blocks are for: a semantically and syntactically lightweight form of passing code to a method.
There are other possible approaches, of course. The approach that is closest to Ruby is probably Smalltalk. Smalltalk also has a concept called blocks (in fact, that is where Ruby got both the idea and the name from). Similarly to Ruby, Smalltalk blocks have a syntactically light-weight literal form, but they are objects, and you can pass more than one to a method. Thanks to Smalltalk's generally light-weight and simple syntax, especially the keyword method syntax which intersperses parts of the method name with the arguments, even passing multiple blocks to a method call is very concise and readable.
For example, Smalltalk actually does not have an if / then / else conditional expression, in fact, Smalltalk has no control structures at all. Everything is done with methods. So, the way that a conditional works, is that the two boolean classes TrueClass and FalseClass each have a method named ifTrue:ifFalse: which takes two block arguments, and the two implementations will simply either evaluate the first or the second block. For example, the implementation in TrueClass might look a little bit like this (note that Smalltalk has no syntax for classes or methods, instead classes and methods are created in the IDE by creating class objects and method objects via the GUI):
True>>ifTrue: trueBlock ifFalse: falseBlock
"Answer with the value of `trueBlock`."
↑trueBlock value
The corresponding implementation in FalseClass would then look like this:
FalseClass>>ifTrue: trueBlock ifFalse: falseBlock
"Answer with the value of `falseBlock`."
↑falseBlock value
And you would call it like this:
2 < 3 ifTrue: [ Transcript show: 'yes' ] ifFalse: [ Transcript show: 'no' ].
"yes"
4 < 3 ifTrue: [ Transcript show: 'yes' ] ifFalse: [ Transcript show: 'no' ].
"no"
In ECMAScript, you can simply use function definitions as expressions, and there is also lightweight syntax for functions.
In the various Lisps, code is just data, and data is code, so you can just pass the code as an argument as data, then inside the function, treat that data as code again.
Scala has call-by-name parameters which are only evaluated when you use their name, and they are evaluated every time you use their name. It would look something like this:
implicit class IntegerTimes(val i: Int) extends AnyVal {
#scala.annotation.tailrec
def times(actionToBeExecuted: => Unit): Unit = {
if (i < 0) throw new Error()
if (i == 0) () else { actionToBeExecuted; (i - 1).times(actionToBeExecuted) }
}
}
3.times { println("Hello") }
// Hello
// Hello
// Hello

How to pass a block

For the sake of simplicity, I've tried to abstract the problem down to its core elements. I've included a small piece of functionality wherein I use Socket to show that I want to pass the block further down into a method which is a black box for all intents and purposes. I'm also passing a constant True for the sake of showing I want to pass arguments as well as a yield block.
With all that being said, if I small have a hierarchy of calls as such:
def foo(use_local_source)
if use_local_source
Socket.unix("/var/run/my.sock") &yield
else
Socket.tcp("my.remote.com",1234) &yield
end
end
foo(True) { |socket|
name = socket.read
puts "Hi #{name}, I'm from foo."
}
How can I pass the implicitly declared block right down through foo and into Socket as if I were calling Socket.tcp(...) { ... } directly.
I know I could set it as an argument, but it doesn't feel idiomatic to Ruby. Is this also untrue and I should pass it as an argument? I've tried combinations of & and *, and I get a range of exception.
def foo(use_local_source)
if use_local_source
yield Socket.unix("/var/run/my.sock")
else
yield Socket.tcp("my.remote.com",1234)
end
end
From the docs for yield:
Yields control back to the context that resumed the fiber, passing along any arguments that were passed to it.

Function call in Ruby

I am new to ruby and I want to achieve this:
Foo.runtask param1: :asd, param2: :qwerty
I know how to create a function taking two parameters, but I want to know how to call it like I mentioned specifically "param1:" and "param2:"
param1: :asd, param2: :qwerty It is a shorthand for { param1: :asd, param2: :qwerty }, which is a Hash (In some cases, you can omit the curlies of a Hash).
If you want to pass arguments like that, you method should accept a Hash as the parameter
eg
def my_method(options)
puts options[:param1]
puts options[:param2]
end
Then you can call my_method param1: :asd, param2: :qwerty
From ruby 2.0 onwards ruby supports named arguments, so while you can still declare your method as just taking a single options argument you can also do
def foo(param1: 1, param2: :bar)
...
end
Deep down in the inside there is still a hash of arguments being passed around, but this allows you to specify default values easily (like normal default values these can be any ruby expression) and will raise an exception if you pass a names parameter other than a listed one.
In ruby 2.1 you can also have mandatory named arguments
def foo(param1: 1, param2:)
...
end
param2 is now mandatory.
In both cases you invoke the method like before:
foo(param1: :asd, param2: :qwerty)
In fact just by looking at this invocation you can't tell whether these will be consumed as named arguments or as a hash
You can of course emulate this with hashes but you end up having to write a bunch of boilerplate argument validation code repeatedly.
To tell whether a parameter is taking its default value was passed explicitly you can use this well known trick
def foo(param1: (param_1_missing=true; "foo")
...
end
Here param1 will be set to "foo" by default and param_1_missing will be true or nil

rspec check any argument is hash including particular pair

I have a shared rspec example for a number of functions (rest requests). Each function receives a hash which I want to check, but they can be at different positions, e.g.:
get(url, payload, headers)
delete(url, headers)
I want to write the following test:
shared_examples_for "any request" do
describe "sets user agent" do
it "defaults to some value" do
rest_client.should_receive(action).with(????)
run_request
end
it "to value passed to constructor"
end
end
end
describe "#create" do
let(:action) {:post}
let (:run_action) {rest_client.post(url, payload, hash_i_care_about)}
it_behaves_like "any request"
end
The question is, how can I write a matcher which matches any of the arguments, e.g.:
client.should_receive(action).with(arguments_including(hash_including(:user_agent => "my_agent")))
In order to match any argument, you can pass a block to should_receive which can then check the arguments in any fashion you want:
client.should_receive(action) do |*args|
# code to check that one of the args is the kind of hash you want
end
You could search the list of args for an argument of hash type, you could pass a parameter into the shared example indicating what position in the argument list the hash should exist in etc. Let me know if this is not clear and I can provide more detail.
This is covered briefly in https://www.relishapp.com/rspec/rspec-mocks/docs/argument-matchers
My example is a little bit in conjunction with what Peter Alfvin suggested:
shared_examples "any_request" do |**args|
action = args[:action]
url = args[:url]
payload = args[:payload]
headers = args[:headers]
# ...etc
case action
when :get
# code to carry on
when :post
# code to continue
end
end
This way you can define and work with the arguments as your code expands, in any order and in any amount. You would call the function like so:
it_behaves_like "any_request", { action: :post,
url: '/somewhere' }
Undeclared arguments, like :payload in this example would automatically carry the value of nil. Test its existence like: if payload.nil? or unless payload.nil? etc.
Note: This works for Ruby 2.0, Rails 4.0, rspec 2.13.1. Actual code definitions may vary on earlier versions.
Note_note: ... do |**args| the two asterisks is not a typo ;)

Can someone explain Ruby's use of pipe characters in a block?

Can someone explain to me Ruby's use of pipe characters in a block? I understand that it contains a variable name that will be assigned the data as it iterates. But what is this called? Can there be more than one variable inside the pipes? Anything else I should know about it? Any good links to more information on it?
For example:
25.times { | i | puts i }
Braces define an anonymous function, called a block. Tokens between the pipe are the arguments of this block. The number of arguments required depends on how the block is used. Each time the block is evaluated, the method requiring the block will pass a value based on the object calling it.
It's the same as defining a method, only it's not stored beyond the method that accepts a block.
For example:
def my_print(i)
puts i
end
will do the same as this when executed:
{|i| puts i}
the only difference is the block is defined on the fly and not stored.
Example 2:
The following statements are equivalent
25.times &method(:my_print)
25.times {|i| puts i}
We use anonymous blocks because the majority of functions passed as a block are usually specific to your situation and not worth defining for reuse.
So what happens when a method accepts a block? That depends on the method. Methods that accept a block will call it by passing values from their calling object in a well defined manner. What's returned depends on the method requiring the block.
For example: In 25.times {|i| puts i} .times calls the block once for each value between 0 and the value of its caller, passing the value into the block as the temporary variable i. Times returns the value of the calling object. In this case 25.
Let's look at method that accepts a block with two arguments.
{:key1 => "value1", :key2 => "value2"}.each {|key,value|
puts "This key is: #{key}. Its value is #{value}"
}
In this case each calls the block ones for each key/value pair passing the key as the first argument and the value as the second argument.
The pipes specify arguments that are populated with values by the function that calls your block. There can be zero or more of them, and how many you should use depends on the method you call.
For example, each_with_index uses two variables and puts the element in one of them and the index in the other.
here is a good description of how blocks and iterators work
Block arguments follow all the same conventions as method parameters (at least as of 1.9): you can define optional arguments, variable length arg lists, defaults, etc. Here's a pretty decent summary.
Some things to be aware of: because blocks see variables in the scope they were defined it, if you pass in an argument with the same name as an existing variable, it will "shadow" it - your block will see the passed in value and the original variable will be unchanged.
i = 10
25.times { | i | puts i }
puts i #=> prints '10'
Will print '10' at the end. Because sometimes this is desirable behavior even if you are not passing in a value (ie you want to make sure you don't accidentally clobber a variable from surrounding scope) you can specify block-local variable names after a semicolon after the argument list:
x = 'foo'
25.times { | i ; x | puts i; x = 'bar' }
puts x #=> prints 'foo'
Here, 'x' is local to the block, even though no value is passed in.

Resources