Use a block to set variables in parent scope - ruby

The gem binding_of_caller has an example for how to set a variable in a parent scope:
(this just pasted from their readme)
def a
var = 10
b
puts var
end
def b
c
end
def c
binding.of_caller(2).eval('var = :hello')
end
a()
# OUTPUT
# => hello
This is useful, but it's limited by the need to do all variable initialization in a string.
I gave it a little thought and realized that I could use YAML to serialize/deserialize objects.
Take, for example, the following example:
def c
value = YAML.dump [ { a: "b" } ]
binding.of_caller(2).eval("var = YAML.load('#{value}')")
end
a()
# => {a: "b"}
This is cool, but it'd be better if I could avoid serialization altogether and just write a proper do; end; block like so:
# doesnt work
def c
binding.of_caller(2).eval do
# ideally this would set the variable named "var" in the scope of method "a"
var = [ { a: "b" } ]
end
end
How can I accomplish the functionality of this last example? I don't need to use binding_of_caller if there's another way.

This is the best I could do and, I suspect (though I'd truly love to be proven wrong), the best you'll find short of writing your own C extension a la binding_of_caller:
require 'binding_of_caller'
module BindingExtensionEvalBlock
def eval_block(&block)
eval("ObjectSpace._id2ref(%d).call(binding)" % block.object_id)
end
end
class ::Binding
include BindingExtensionEvalBlock
end
The magic is here, of course:
eval("ObjectSpace._id2ref(%d).call(binding)" % block.object_id)
We get the object ID of the Proc and then, in our Binding#eval, use ObjectSpace#_id2ref to retrieve it from wherever it is in memory and call it, passing in the local binding.
Here it is in action:
def a
var = 10
b
puts var
end
def b
c
end
def c
binding.of_caller(2).eval_block do |bnd|
bnd.local_variable_set(:var, [ { a: "b" } ])
end
end
a # => {:a=>"b"}
As you can see, instead of var = [ { a: "b" } ] in our block we must do bnd.local_variable_set(:var, [ { a: "b" } ]). There's no way to change the binding of a block in Ruby, so we have to pass in a binding (bnd) and call Binding#local_variable_set.

Related

Is there a possibility to write parametrized tests with Ruby+Minitest?

I've recently started working on a automated testing project, that uses Ruby+Minitest and I wonder if I can run 1 test as many times as many input data I provide.
So, I have smth like (code is under NDA so I can't provide real examples)
def test_new_registrations
result = process_new_entry(list_entries)
assert(validator_method(result), result)
end
The trick is that inside process_new_entry method there's a loop I'm glad to get rid of and just run this test as many times as many entries are there on the list_entries
From TestNG in Java+Selenium I recall a concept of using a dataprovider which passes input data inside the test method one by one.
Is there any chance a simmilar approach can be implemented here?
If you have:
class MyTest
TESTCASES = {
// input => expected
a: "a",
b: "b",
c: "c",
}
def test_testcases
TESTCASES.each do |entry, expected|
result = process_one(entry)
assert(expected, result)
end
end
end
And you really want to run each case in its own test instead, then it's just:
MyTest
TESTCASES = {
// input => expected
a: "a",
b: "b",
c: "c",
}
TESTCASES.each do |entry, expected|
define_method("test_testcase_#{entry}") do
result = process_one(entry)
assert(expected, result)
end
end
end
If you REALLY wanted to have the exact same syntax as in Java, it'd be possible to convert the above into a library so that this would work:
testcase_parameters(TESTCASES) # left as an exercise to the reader
def test_testcase(entry, expected)
assert(expected, process_one(entry))
end
But I don't see the benefit of being so indirect/abstract about it. I would suggest to stay explicit, keeping the code and execution state inspect-able and easy to debug:
class MyTest
TESTCASES = {
// input => expected
a: "a",
b: "b",
c: "c",
}
def test_testcases
results = TESTCASES.map do |entry, expected|
result = process_one(entry)
[entry, [expected, result]]
end
invalid_results = results.select { |e, (ex, res)| ex != res }
# This is a very easy-to-breakpoint place in code, to easily view all results:
# binding.pry
assert_empty invalid_results # failure cases will be printed out by test output
end
end

Ruby passing arbitrary function and parameters into another function

I'm trying to learn Ruby. I want to pass an arbitrary function and an arbitrary list of arguments and keyword arguments into another function.
for example, I have this arbitrary function below
def dummy_func(a, b)
return a+b
end
And I have this wrapper function
def wrapper(func, *args, **kwargs)
func(args, kwargs))
end
I want it so I can pass my arguments in any of the following ways and still return the correct answer
wrapper(dummy_func, a=1, b=2)
wrapper(dummy_func, 1, b=2)
wrapper(dummy_func, a=1, b=2)
wrapper(dummy_func, 1, 2)
Is this possible in Ruby? What would be an idiomatic way of approaching it?
The idiomatic way is to instead yield to a block.
def dummy_func(a, b, key:)
return a+b+key
end
def wrapper
puts yield
end
a = 4
b = 5
c = 6
wrapper do
dummy_func(a ,b, key: c)
end
Since the block is closure it can see all the same variables that the call to wrapper can. Now there's no need to pass wrapper's arguments through.
If you really want to make your wrapper, you can do some introspection to determine what arguments the wrapped function takes.
def dummy_func(a, b=23, key: 42)
return a+b+key
end
def no_keys(a, b=23)
return a+b
end
def wrapper(func, *array, **hash)
method = self.method(func)
takes_array = method.parameters.any? { |p| [:req, :opt, :rest].include?(p[0]) }
takes_hash = method.parameters.any? { |p| [:keyreq, :key, :keyrest].include?(p[0]) }
if takes_array && takes_hash
self.send(func, *array, **hash)
elsif takes_array
self.send(func, *array)
elsif takes_hash
self.send(func, **hash)
else
self.send(func)
end
end
a = 4
b = 5
c = 6
puts wrapper(:dummy_func, a, b, key:c)
puts wrapper(:no_keys, a, b)
But this is quite a bit more complex and less flexible than yielding to a block. This also limits you to "functions" which are really methods on the main object (there are no function references in Ruby). That's why they're called with self.send. Blocks require no assumptions about what is being wrapped.
The closest you can get is keyword arguments:
https://www.justinweiss.com/articles/fun-with-keyword-arguments/
def hello_message(greeting, time_of_day, first_name:, last_name:)
"#{greeting} #{time_of_day}, #{first_name} #{last_name}!"
end
args = ["Morning"]
keyword_args = {last_name: "Weiss"}
hello_message("Good", *args, first_name: "Justin", **keyword_args)
=> "Good Morning, Justin Weiss!"

Ruby check if block is nil

I call a method with a block;
method do
"Hello"
end
and the method is defined as;
def method
yield
end
and when defining method; i want to check if given block is empty (nil) or not, because the variable in the method may end up like this;
method do
""
end
So in definition, i want to check if the yield block is nil or not. Like;
def method
if yield ? yield : "Empty block? Seriously?"
end
I know the above does not work. Bu it is what i want to achieve.
Also keep in mind that block_given? will always be "true" since the block is given even if it is nil or empty string.
UPDATE: As most of the comments/answers state that the question is unclear; here is the problem simplified by #ndn:
I want to check if the result of executing a block is "empty"(nil or "") without
invoking it first.
It is unclear what you are asking, because a block itself can not be empty. Therefore, you might mean a few different things:
A missing block. You can check if a block is given
block_given?
Block with empty body (aka {} or do end). This is not impossible, but requires some advanced voodoo ruby metaprogramming magic. Generally, if this is what you are looking for, either you are writing something very interesting or your approach is completely wrong.
You want to check if the result of executing a block is "empty" without invoking it first. This is impossible. For example, consider the following block:
{ [nil, "", true].sample }
Obviously, there is no way to know in advance.
You are ok with calling the block. Then you can assign the result to a variable and make checks on it:
def some_method
evaluation_result = yield if block_given?
if evaluation_result.nil? or evaluation_result == ""
# do something if the block was not given or the result is nil/empty
puts "Empty block? Seriously?"
else
# do something if the block was given and the result is non nil/empty
puts evaluation_result
end
end
Now when you invoke some_method:
some_method { "something" } # => "something"
some_method { 3 + 5 } # => 8
some_method { nil } # => "Empty block? Seriously?"
some_method { "" } # => "Empty block? Seriously?"
some_method { } # => "Empty block? Seriously?"
some_method # => "Empty block? Seriously?"
EDIT:
A workaround for case #3 might be to create two procs, one with what you want to do if the block is "empty" and one - if it is not, then pass them around to the endpoint where you will finally invoke the block. This might or might not be applicable depending on your exact situation.
EDIT2:
Another workaround can be to redefine the Proc#call method for your proc instances. However, this doesn't work for yield:
def secure(&block)
insecure_call = block.method(:call)
block.define_singleton_method(:call) do
insecure_call_result = insecure_call.call
if insecure_call_result.nil? or insecure_call_result == ""
"<b>Bummer! Empty block...</b>"
else
insecure_call_result
end
end
end
x = proc { }
y = proc { "" }
z = proc { nil }
a = proc { 3 + 5 }
b = proc { "something" }
u = proc { [nil, "", true].sample }
[x, y, z, a, b, u].each { |block| secure &block }
# some method that uses the block
def user(&block)
"What I got is #{block.call}!"
end
user &x # => "What I got is <b>Bummer! Empty block...</b>!"
user &y # => "What I got is <b>Bummer! Empty block...</b>!"
user &z # => "What I got is <b>Bummer! Empty block...</b>!"
user &a # => "What I got is 8!"
user &b # => "What I got is something!"
user &u # => Different each time
EDIT3: Another alternative, which is sort of cheating, is to wrap the given proc in another proc. This way, it will work for yield too.
def wrap(&block)
proc do
internal_proc_call_result = block.call
if internal_proc_call_result.nil? or internal_proc_call_result == ""
"<b>Bummer! Empty block...</b>"
else
internal_proc_call_result
end
end
end
Now using the result of wrap and will get you behavior similar to secure.
If I understand correctly, you want to statically determine what the runtime value of a block is. This is one of the many known impossible problems resulting from the undecidability of the Halting Problem.
In other words: it can't be done.
Not "it can't be done in Ruby", not "it is hard", it simply can't be done, period. And it can be (and has been) mathematically proven that it can't be done. Ever.
UPDATED Answer
My last effort to simplify the answer based on comments..
You can check for block emptiness with block_given? and you need to explicitly check for yield output for emptiness like below
def method(&block)
# Below if condition is to prove that block can be accessed
if block_given?
p block
p block.yield
end
b = yield if block_given?
(b.nil? || b.empty?) ? "Empty block? Seriously?" : b
end
p method {"Hello"} # inline block
result = method do
"World"
end
p result
p method # No blocks provided
p method {""} # Block that returns empty string
Output of the program
"Hello"
"World"
"Empty block? Seriously?"
"Empty block? Seriously?"

Call a Ruby method explicitly using the default for a parameter

An unfortunately large number of methods are written in the following form:
def my_method(foo = {}, bar = {})
# Do stuff with foo and bar
end
While I appreciate not having to write my_method({}, {}) everywhere I reference the method, using something other than the default for the second parameter makes me use something other than the default for the first parameter too - my_method({}, foo: 'bar').
Is there a way to tell Ruby to use the default for a parameter when other, later parameters need to use something other than the default? I'm hoping for something in the form of my_method(__default__, foo: 'bar'), but would welcome any other solutions that address the core of this problem.
This would be particularly useful when APIs undergo minor (but significant) changes. It can lead to hard to find bugs occasionally:
# Original method
def foo(value = 'true', options = {})
# ...
end
# Defaults are updated slightly in a new version of the library
def foo(value = true, options = {})
# ...
end
# My code might break in an unexpected way now
foo('true', bar: 'baz')
This is the problem which keyword arguments (new to ruby 2) were made to solve (provided that you control the method definition).
def foo(a: {}, b: {})
"a: #{a}, b: #{b}"
end
foo # => "a: {}, b: {}"
foo(a: 1, b: 2) # => "a: 1, b: 2"
foo(a: 3) # => "a: 3, b: {}"
foo(b: 4) # => "a: {}, b: 4"
You could set defaults to nil then handle the actual defaulting of values within the body of the method. ie.,
def my_method(first=nil, second=nil)
first_value = first || 1
second_value = second || 2
end
This allows you to pass 'nil' when you want that value to be its default. For example,
my_method(nil, 'second')
Now 'first_value' is 1 and second_value is 'second'.
edit: though 'nil' is really non-descriptive of the action of making the method use its default value. Consider:
def my_method(first=:default, second=:default)
first_value = (first == :default ? 1 : first)
second_value = (second== :default ? 2 : second)
end
Then you can do:
my_method(:default, 'second')
(but really Sergio's answer is the best =) )
You can just refactor the code to something like this, so it gets assigned to the default value only if the named parameter isn't provided a value.
def my_method(foo, bar)
foo ||= {}; bar ||= {};
#=> Do something with foo and bar now.
end
What ||= operator does is, it assigns the value on the right to the variable on the left if the variable isn't initialized or has nil value.
You can now call it like this
my_method(nil, "I'm bar");
If by any chance, you want to pass nil as a value, then this will fail. Sergio Tulentsev's answer is the way to go. I'd have suggested the same had I known it.

Passing a method as a parameter in Ruby

I am trying to mess around a little bit with Ruby. Therefor I try to implement the algorithms (given in Python) from the book "Programming Collective Intelligence" Ruby.
In chapter 8 the author passes a method a as parameter. This seems to work in Python but not in Ruby.
I have here the method
def gaussian(dist, sigma=10.0)
foo
end
and want to call this with another method
def weightedknn(data, vec1, k = 5, weightf = gaussian)
foo
weight = weightf(dist)
foo
end
All I got is an error
ArgumentError: wrong number of arguments (0 for 1)
The comments referring to blocks and Procs are correct in that they are more usual in Ruby. But you can pass a method if you want. You call method to get the method and .call to call it:
def weightedknn( data, vec1, k = 5, weightf = method(:gaussian) )
...
weight = weightf.call( dist )
...
end
You want a proc object:
gaussian = Proc.new do |dist, *args|
sigma = args.first || 10.0
...
end
def weightedknn(data, vec1, k = 5, weightf = gaussian)
...
weight = weightf.call(dist)
...
end
Just note that you can't set a default argument in a block declaration like that. So you need to use a splat and setup the default in the proc code itself.
Or, depending on your scope of all this, it may be easier to pass in a method name instead.
def weightedknn(data, vec1, k = 5, weightf = :gaussian)
...
weight = self.send(weightf)
...
end
In this case you are just calling a method that is defined on an object rather than passing in a complete chunk of code. Depending on how you structure this you may need replace self.send with object_that_has_the_these_math_methods.send
Last but not least, you can hang a block off the method.
def weightedknn(data, vec1, k = 5)
...
weight =
if block_given?
yield(dist)
else
gaussian.call(dist)
end
end
...
end
weightedknn(foo, bar) do |dist|
# square the dist
dist * dist
end
But it sounds like you would like more reusable chunks of code here.
You can pass a method as parameter with method(:function) way. Below is a very simple example:
def double(a)
return a * 2
end
=> nil
def method_with_function_as_param( callback, number)
callback.call(number)
end
=> nil
method_with_function_as_param( method(:double) , 10 )
=> 20
The normal Ruby way to do this is to use a block.
So it would be something like:
def weightedknn(data, vec1, k = 5)
foo
weight = yield(dist)
foo
end
And used like:
weightedknn(data, vec1) { |dist| gaussian( dist ) }
This pattern is used extensively in Ruby.
You can use the & operator on the Method instance of your method to convert the method to a block.
Example:
def foo(arg)
p arg
end
def bar(&block)
p 'bar'
block.call('foo')
end
bar(&method(:foo))
More details at http://weblog.raganwald.com/2008/06/what-does-do-when-used-as-unary.html
You have to call the method "call" of the function object:
weight = weightf.call( dist )
EDIT: as explained in the comments, this approach is wrong. It would work if you're using Procs instead of normal functions.
I would recommend to use ampersand to have an access to named blocks within a function. Following the recommendations given in this article you can write something like this (this is a real scrap from my working program):
# Returns a valid hash for html form select element, combined of all entities
# for the given +model+, where only id and name attributes are taken as
# values and keys correspondingly. Provide block returning boolean if you
# need to select only specific entities.
#
# * *Args* :
# - +model+ -> ORM interface for specific entities'
# - +&cond+ -> block {|x| boolean}, filtering entities upon iterations
# * *Returns* :
# - hash of {entity.id => entity.name}
#
def make_select_list( model, &cond )
cond ||= proc { true } # cond defaults to proc { true }
# Entities filtered by cond, followed by filtration by (id, name)
model.all.map do |x|
cond.( x ) ? { x.id => x.name } : {}
end.reduce Hash.new do |memo, e| memo.merge( e ) end
end
Afterwerds, you can call this function like this:
#contests = make_select_list Contest do |contest|
logged_admin? or contest.organizer == #current_user
end
If you don't need to filter your selection, you simply omit the block:
#categories = make_select_list( Category ) # selects all categories
So much for the power of Ruby blocks.
Similarly to a Proc or a method call, you can also pass a lambda as weightf parameter :
def main
gaussian = -> (params) {
...
}
weightedknn(data, vec1, k = 5, gaussian, params)
# Use symbol :gaussian if method exists instead
end
def weightedknn(data, vec1, k = 5, weightf, params)
...
weight = weightf.call(params)
...
end
you also can use "eval", and pass the method as a string argument, and then simply eval it in the other method.

Resources