Is There a Ruby Method That Just Returns the Value of a Block

Is there a ruby method that just returns the value of a block?

What you're looking for is essentially the equivalent of let in Lisp or OCaml — something that allows you to temporarily bind a value to an identifier without introducing a new variable into the larger scope. There isn't anything that lets you do such a thing with that syntax in Ruby. The equivalent Ruby would be:

lambda {|value| deck.index(value).tap {|index|
STDERR.puts "Result of indexing for #{value.inspect} is #{index.inspect}"
} }.call 'A'

You could of course just write a method like:

def let(*values) 
yield *values
end

Return the value from Ruby block

Use call.

block.call

if block takes arguments, then give arguments:

block.call(whatever_arguments)

n.times do block just returns value of n

Nope, I don't see how you inferred from those answers that .times block should return anything. What it does is it runs specified block specified number of times, nothing more. Return values of the block are discarded. Will do if you want to, say, print "hello" to standard output N times or do some other work.

n.times do
puts 'hello'
end

If you expected N copies of "hello" string in an array, then there are other ways to achieve this. For example:

Array.new(n, 'hello')
n.times.map { 'hello' }
# and many others

Ruby - Array.find, but return the value the block

[1, 2, 3].detect { |i| i += 1; break i if i == 2 } 
# => 2

[1, 2, 3].detect { |i| i += 1; break i if i == 10 }
# => nil

Using return keyword in Ruby block

Herein lies one of the primary differences between a block/Proc and a lambda/Method (the other primary difference would be arity). If you don't want a call to return to exit the method, that would infer that you expect that block should be self-contained in its flow control, and be treated as an encapsulated method.

This description is essentially what a lambda is - an anonymous method. However, a standard ruby block is essentially an anonymous Proc, and takes nothing away from the flow control of the method.

As mentioned in the comments, you may be able to use next to escape the block without returning control away from the method. 'May' because next may just continue to the next item that the method iterator is passing to the block.

Related to this, see http://yehudakatz.com/2012/01/10/javascript-needs-blocks/ and Differences between Proc and Lambda

How to get the return value of the block passed to eval method?

You can return the result of eval to the block using yield. You just had to yield the value; thus I changed your yield to yield eval code. In the block you give to Sandbox.secure_eval you have to then bind this result to a block variable. The result of secure_eval will be the result of the block, like you wanted.

proc = Proc.new do
Timeout::timeout timeout do
$SAFE = safe_level
yield eval code # <= This line changed
end
end

Sandbox.secure_eval('hoge = ["hoge", "fuga"]') { |hoge| hoge[0] }
# => "hoge"

Sandbox.secure_eval('2 ** 4') { |result| result - 5 }
# => 11

In response to your comment; it turns out that with the aid of Kernel#Binding, we can get it to work more or less like you wanted. It feels rather like a hack so use it with caution.

I use the Binding to evaluate the code, which will have access to all defined variables. In addition, I define a method_missing for the Binding class so we can access the variables more easily. Without it, you would need to do eval('varname') rather than just varname. Per the comment of @hakcho who mentioned the monkey-patch solution that was in place is not ideal, I now use refinements which only locally changes the behavior of Binding (i.e., the method_missing implementation).

I have added an explicit block parameter to your method, which I use with instance_eval instead of yield. We can then access the variables directly in the block.

require 'timeout'

module Sandbox
refine Binding do
def method_missing(meth, *args, &block)
self.eval(meth.to_s)
end
end

def self.secure_eval(code, timeout: 5, safe_level: 2, &block)
raise ArgumentError, 'please set call back by block' unless block_given?

proc = Proc.new do
Timeout::timeout timeout do
$SAFE = safe_level
binding = binding()
binding.eval(code)
binding.instance_eval(&block)
end
end

proc.call
end
end

using Sandbox # Activate the refinement so we can use x, y, z directly
Sandbox.secure_eval('x = [1,2,3]; y = 0; z = { key: "Hello!" }') do
x[1] # => 2
y # => 0
z[:key] # => "Hello!"
end


Related Topics



Leave a reply



Submit