How to Turn a Ruby Method into a Block

How to turn a Ruby method into a block?

You can use Object#method(method_name):

filenames.map(&File.method(:size))

Making a method available within a block without changing its context?

instance_eval does not consider the scope of where the block is called, so every method call is only relative to what is defined inside Foo.

So you have 2 options. Either

def initialize
baz = self
Foo.new do
bar # -> Works
baz.quux # -> Works
end
end

or

def initialize
puts "This represents a beginning action"
yield self
puts "This symbolizes an ending action"
end

....

def initialize
Foo.new do |b|
b.bar # -> Works too
quux # -> Works too
end
end

I am not sure which one would be better performance wise, but the option you pick is based on your own preference.

How to pass a function instead of a block

According to "Passing Methods like Blocks in Ruby", you can pass a method as a block like so:

p [1,2,3].map(&method(:inc))

Don't know if that's much better than rolling your own block, honestly.

If your method is defined on the class of the objects you're using, you could do this:

# Adding inc to the Integer class in order to relate to the original post.
class Integer
def inc
self + 1
end
end

p [1,2,3].map(&:inc)

In that case, Ruby will interpret the symbol as an instance method name and attempt to call the method on that object.


The reason you can pass a function name as a first-class object in Python, but not in Ruby, is because Ruby allows you to call a method with zero arguments without parentheses. Python's grammar, since it requires the parentheses, prevents any possible ambiguity between passing in a function name and calling a function with no arguments.

How to pass a block (which becomes a Proc object) to a ruby method

The assignment syntax has nothing to do with blocks. Instead, what is happening here is called "parallel assignment".

With that, you can assign multiple variables on the left from multiple values on the right with one atomic operation. From your example, this statement

@name, @block = name, block

is equivalent to

@name = name
@block = block

In this example, this usage is more of a gimmick (which I'd flag in a code-review). However, there are some patterns where parallel assignment is very useful.

The most import one is the ability to switch the values of two variables without requiring an intermediate variable:

a = 1
b = 2
a, b = b, a

Another option is the ability to deconstruct an array into multiple variables. In the following example, the method returns an array whose elements get assigned to the two variables hello and world.

def get_array
[:hello, :world]
end

hello, world = get_array

Can a Ruby method accept either a block OR an argument?

Sure it's possible. All you have to do is check if an argument is given and also check if a block is given.

def call_me(arg=nil)
puts "arg given" unless arg.nil?
puts "block given" if block_given?
end

call_me(1)
# => arg given
call_me { "foo" }
# => block given
call_me(1) { "foo" }
# => arg given
# block given

Or:

def call_me(arg=nil, &block)
puts "arg given" unless arg.nil?
puts "block given" unless block.nil?
end

The latter is useful because it converts the block to a Proc (named block) that you can then reuse, as below.

You could implement your own count method like this:

module Enumerable
def my_count(*args, &block)
return size if args.empty? && block.nil?
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 1)" if args.size > 1

counter = block.nil? ? ->(i) { i == args[0] } : block
sum {|i| counter.call(i) ? 1 : 0 }
end
end

arr = [1,2,3,4,5]
p arr.my_count # => 5
p arr.my_count(2) # => 1
p arr.my_count(&:even?) # => 2
p arr.my_count(2, 3) # => ArgumentError: wrong number of arguments (given 2, expected 1)

See it on repl.it: https://repl.it/@jrunning/YellowishPricklyPenguin-1

How to convert method to proc for &method usage in Ruby

You're using the method method incorrectly. From the fine manual:

method(sym) → method
Looks up the named method as a receiver in obj, returning a Method object (or raising NameError).

You'd usually say

m = some_obj.method(:some_method_name)

and then work with m. Saying:

method(:Foo::Bar.logger.debug)

should be giving you a TypeError because because :Foo is a symbol, not a class or module and trying to apply :: to a symbol makes no sense. I suspect that you're actually saying:

method(':Foo::Bar.logger.debug')

as that will produce the error you're seeing. Assuming that that's the case, then you're actually trying to get a reference to the method named ':Foo::Bar.logger.debug' in the object self.

If you want a reference to the debug method in Foo::Bar.logger then you'd say:

Foo::Bar.logger.method(:debug)

Combining that your to_proc call (via &):

[a, b, c].each(&Foo::Bar.logger.method(:debug))

Passing a class method as a block to map in ruby

The long way is the most conventional, but the short way is your last example:

%w{4S 5D 9H 1C 3S}.map(&Card.method(:safe_parse))

This only works if safe_parse is a module method, not a mixin method, or in other words you've defined it as self.safe_parse or done an extend self at the end of the module.

As far as I know it's not possible to use instance_method directly on mixin methods since those are missing their instance context.

Accessing methods in a Ruby block

No, this is not possible. You could (in theory) parse the Ruby code associated to the block definition, but I'm not sure that would make sense.

Your question is very generic, you don't provide any detail if you have control over the block or not, and a real world example would probably be more helpful.

From the details I have, my suggestion is that you should split the block at the origin. Instead of passing the whole block containing all those methods, pass an array of the methods as argument so that you can reuse them as you want.

You can wrap them in a lambda, to delay the execution.

def func(*chain)
end

func(
->(arg) { method1 },
->(arg) { method2(arg) },
->(arg) { method3(arg) }
)

You can also use Object.method to fetch the method and pass it as parameter.

def func(*chain)
end

func(
method(:method1),
method(:method2),
method(:method3)
)

Ruby: How to create a block and pass it as an argument?

It depends partially on how you want to use it. An easy way is this, if it fits your usage needs:

@result = ssh.exec!("cd /some/dir; ls") do |something|
# Whatever you need to do
# The "something" variable only makes sense if exec! yields something
end

Or

@result = ssh.exec!("cd /some/dir; ls") { |something| puts something }

The {} notation is generally used when the block is short.

You can also create a Proc or lambda; ultimately the "right" answer depends on what you're trying to do.

Note there's an example if you're talking about Net::SSH.

Pass block passed to method to another method in Ruby

You can reference the block explicitly

def discard(&block)
self - self.keep(&block)
end

or implicitly

def discard
self - self.keep(&Proc.new {})
end

In your case, I would suggest the first approach.



Related Topics



Leave a reply



Submit