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 aMethod
object (or raisingNameError
).
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
How to Organize Minitest/Unit Tests
How to Do Attr_Accessor_With_Default in Ruby
Rails, Activerecord: Self[:Attribute] VS Self.Attribute
After Installing Ruby Gems, Running the New Gem Returns "Could Not Find" Errors
Ruby: How to Group a Ruby Array
What Does the Operator ||= Stand for in Ruby
What Is the Best Diff Library in Ruby
Creating a Hash with Values as Arrays and Default Value as Empty Array
Using the Value of a Variable as Another Variables Name in Ruby
How Would I Go About Programmatically Interacting with VSt(I) Plugins to Synthesize Audio
Natural Language Date Parser for Ruby/Rails
Get Server File Path with Paperclip
In Ruby, Should I Use ||= or If Defined? for Memoization
How to Use Ruby Metaprogramming to Add Callbacks to a Rails Model