ruby pass method as a parameter - ruby-on-rails

Im trying to pass a method as a parameter to method_2, execute this one and return the result:
def method_2( method_p, param )
res = method(method_p).call(param)
return res
end
def method_1
klass = MyKlass.instance
return method_2( klass.foo, "test" )
end
this's MyKlass file:
class MyKlass
def foo(param)
param+param
end
end
All I got is an error
wrong number of arguments (0 for 1)

You can use symbols to refer to methods:
def method_2(method_symbol, *args)
send method_symbol, *args
end
However, since you're calling the method on a specific object, you would either have to pass that in as an additional argument, or use a proc or a lambda, which is like a block wrapped in an object:
def method_2(proc, *args)
proc.call(*args)
end
method_2(->(param){ klass.foo(param) }, "test")
It's more common to just use blocks to do this:
def method_2(receiver, *args, &block)
yield receiver, *args
end
method_2(klass, "test") do |receiver, param|
receiver.foo(param)
end
All of these are fairly contrived examples; is there a specific problem you're trying to solve?

When you:
return method_2( klass.foo, "test" )
klass.foo requires one arg, that might be what's causing your error.

Related

Collect KeyErrors from Ruby hash into array

I need to extract multiple fields from hash. But I respect my client and I want to gather all missed fields instead of returning it one by one. My idea was to use #fetch, intercept error with KeyError, put error.key into instance variable array and return proper error explanation with full list of missed keys.
Something like that
class Extractor
def initialize hash
#hash = hash
#missed_keys = []
end
def call
extract_values
return "Missed keys: #{#missed_keys.join(', ')}" if #missed_keys.present?
rescue KeyError => e
puts 'Field was missed'
#missed_keys << e.key
return 'Error'
end
private
def extract_values
{
value_1: #hash.fetch(:required_field_1),
value_2: #hash.fetch(:required_field_2),
value_3: #hash.fetch(:required_field_3)
}
end
end
When I try to process hash without required fields I got 'Error' after the first missed field:
pry(main)> Extractor.new(hash: {}).call
Field was missed
=> "Error"
Any clues?
DrySchema and other hash validators are not an option.
An issue with the provided solution is that the extracted values are never returned in the happy path (which presumably is important?). The call method is also stateful / non-idempotent. Subsequent calls to call will duplicate the missing-keys.
Finally - not sure how it's being used, but I don't love a method that returns either a hash or a string.
An alternative that attempts to follow a more functional pattern might look like:
class Extractor
attr_reader :hash, :missed_keys, :required_keys
def initialize hash
#hash = hash
#missed_keys = []
#required_keys = [:required_field_1, :required_field_2, :required_field_3]
end
def call
validate_keys_exist!
extract_values
end
private
def validate_keys_exist!
missed_keys = find_missing_keys
raise MissingKeysError, "Missed keys: #{missed_keys.join(', ')}" if missed_keys.any?
end
def find_missing_keys
required_keys - hash.keys
end
def extract_values
hash.slice(*required_keys)
# not sure if you need to map the keys to new values.
# if so you can iterate over a hash of `from: :to` pairs instead of the
# required_keys array.
end
end
Ok, I got it. The reason is in intercept level and method closures.
In aforementioned implementation Ruby tried to execute call method, got an error and exits.
If we rework it like that:
class Extractor
def initialize hash
#hash = hash
#missed_keys = []
end
def call
extract_values
return "Missed keys: #{#missed_keys.join(', ')}" if #missed_keys.present?
end
private
def extract_values
{
value_1: #hash.fetch(:required_field_1),
value_2: #hash.fetch(:required_field_2),
value_3: #hash.fetch(:required_field_3)
}
rescue KeyError => e
puts 'Field was missed'
#missed_keys << e.key
nil
end
end
it looks better, but still not what we wanted:
pry(main)> Extractor.new(hash: {}).call
Field was missed
=> "Missed keys: required_field_1"
This is because ruby tried to execute extract_values method, encounters first missed value and exits
So the solution as follow:
class Extractor
def initialize hash
#hash = hash
#missed_keys = []
end
def call
extract_values
return "Missed keys: #{#missed_keys.join(', ')}" if #missed_keys.present?
end
private
def extract_values
{
value_1: fetch_value(:required_field_1),
value_2: fetch_value(:required_field_2),
value_3: fetch_value(:required_field_3)
}
end
def fetch_value(key)
#hash.fetch(key)
rescue KeyError => e
puts 'Field was missed'
#missed_keys << e.key
nil
end
end
Extractor.new(hash: {}).call
Field was missed
Field was missed
Field was missed
=> "Missed keys: required_field_1, required_field_2, required_field_3"
Error interception is accomplished on the fetch_value level and Ruby skips required values one by one

yield to an anonymous block two functions up

there is probably a simple way to do this.
I'm trying to refactor something like the following
def foo(baz)
baz.update_first
if baz.has_condition?
yield baz.val if block_given?
baz.a
else
baz.b
end
end
called like
foo(baz) {|b| b.modify}
to something like
def foo(baz)
baz.update_first
bar(baz) {|i| yield i if block_given? }
end
def bar(baz)
if baz.has_condition?
yield baz.val if block_given?
baz.a
else
baz.b
end
end
Will that work? How?
I think it will, but I'd appreciate a clear explanation of how yielding inside a block works... reading through proc.c and vm.c and a relevant git commit in the ruby source code , I think when bar is called in foo it executes until it yields, and then you walk up the frame stack to the local environment pointer for block defined in foo, which is called, where the yield walks up to the block foo is called with, executes it, and then you are back in bar. Is that correct? Is there a better way to do this?
This feels a little weird to me, like inverting control, and it requires foo to know about baz more then I'd like, but I unfortunately can't simply pass a proc or lambda in this code.
I think maybe the concept of yield will be more clear if you look at an alternative syntax, which is converting the bloc to a proc argument.
For example, the following examples are the same
def my_each(arr)
arr.each { |x| yield x }
end
def my_each(arr, &blk)
arr.each { |x| blk.call(x) }
end
# Both are called the same way
my_each([1,2,3]) { |x| print x }
# => 123
When using yield, the variable is available in the method without declaring it in the parameters list. Prepending an & sign to a parameter converts it to a proc, so in the method it can be run with .call.
Here's an example of providing a block to one method then executing it two scopes in:
def method_a(number, &blk)
method_b do
method_c do
blk.call(number)
end
end
end
def method_b(&blk)
blk.call
end
def method_c(&blk)
blk.call
end
method_a(1) { |num| puts num + 1 }
# => 2
Note that blk is not a magic word - you can name the variable whatever you want.
Here's the same thing with yield:
def method_a(number)
method_b do
method_c do
yield number
end
end
end
def method_b
yield
end
def method_c
yield
end
method_a(1) { |num| puts num + 1 }
# => 2
I think using the &blk syntax is clearer because it assigns a variable to the proc. Just because a proc is used in the method doesn't mean you have to ever run Proc.new. The block is automatically converted to a proc.

Is there a way to access method arguments in Ruby?

New to Ruby and ROR and loving it each day, so here is my question since I have not idea how to google it (and I have tried :) )
we have method
def foo(first_name, last_name, age, sex, is_plumber)
# some code
# error happens here
logger.error "Method has failed, here are all method arguments #{SOMETHING}"
end
So what I am looking for way to get all arguments passed to method, without listing each one. Since this is Ruby I assume there is a way :) if it was java I would just list them :)
Output would be:
Method has failed, here are all method arguments {"Mario", "Super", 40, true, true}
In Ruby 1.9.2 and later you can use the parameters method on a method to get the list of parameters for that method. This will return a list of pairs indicating the name of the parameter and whether it is required.
e.g.
If you do
def foo(x, y)
end
then
method(:foo).parameters # => [[:req, :x], [:req, :y]]
You can use the special variable __method__ to get the name of the current method. So within a method the names of its parameters can be obtained via
args = method(__method__).parameters.map { |arg| arg[1].to_s }
You could then display the name and value of each parameter with
logger.error "Method failed with " + args.map { |arg| "#{arg} = #{eval arg}" }.join(', ')
Note: since this answer was originally written, in current versions of Ruby eval can no longer be called with a symbol. To address this, an explicit to_s has been added when building the list of parameter names i.e. parameters.map { |arg| arg[1].to_s }
Since Ruby 2.1 you can use binding.local_variable_get to read value of any local variable, including method parameters (arguments). Thanks to that you can improve the accepted answer to avoid evil eval.
def foo(x, y)
method(__method__).parameters.map do |_, name|
binding.local_variable_get(name)
end
end
foo(1, 2) # => 1, 2
One way to handle this is:
def foo(*args)
first_name, last_name, age, sex, is_plumber = *args
# some code
# error happens here
logger.error "Method has failed, here are all method arguments #{args.inspect}"
end
This is an interesting question. Maybe using local_variables? But there must be a way other than using eval. I'm looking in Kernel doc
class Test
def method(first, last)
local_variables.each do |var|
puts eval var.to_s
end
end
end
Test.new().method("aaa", 1) # outputs "aaa", 1
If you need arguments as a Hash, and you don't want to pollute method's body with tricky extraction of parameters, use this:
def mymethod(firstarg, kw_arg1:, kw_arg2: :default)
args = MethodArguments.(binding) # All arguments are in `args` hash now
...
end
Just add this class to your project:
class MethodArguments
def self.call(ext_binding)
raise ArgumentError, "Binding expected, #{ext_binding.class.name} given" unless ext_binding.is_a?(Binding)
method_name = ext_binding.eval("__method__")
ext_binding.receiver.method(method_name).parameters.map do |_, name|
[name, ext_binding.local_variable_get(name)]
end.to_h
end
end
This may be helpful...
def foo(x, y)
args(binding)
end
def args(callers_binding)
callers_name = caller[0][/`.*'/][1..-2]
parameters = method(callers_name).parameters
parameters.map { |_, arg_name|
callers_binding.local_variable_get(arg_name)
}
end
You can define a constant such as:
ARGS_TO_HASH = "method(__method__).parameters.map { |arg| arg[1].to_s }.map { |arg| { arg.to_sym => eval(arg) } }.reduce Hash.new, :merge"
And use it in your code like:
args = eval(ARGS_TO_HASH)
another_method_that_takes_the_same_arguments(**args)
If the function is inside some class then you can do something like this:
class Car
def drive(speed)
end
end
car = Car.new
method = car.method(:drive)
p method.parameters #=> [[:req, :speed]]
If you would change the method signature, you can do something like this:
def foo(*args)
# some code
# error happens here
logger.error "Method has failed, here are all method arguments #{args}"
end
Or:
def foo(opts={})
# some code
# error happens here
logger.error "Method has failed, here are all method arguments #{opts.values}"
end
In this case, interpolated args or opts.values will be an array, but you can join if on comma. Cheers
It seems like what this question is trying to accomplish could be done with a gem I just released, https://github.com/ericbeland/exception_details. It will list local variables and vlaues (and instance variables) from rescued exceptions. Might be worth a look...
Before I go further, you're passing too many arguments into foo. It looks like all of those arguments are attributes on a Model, correct? You should really be passing the object itself. End of speech.
You could use a "splat" argument. It shoves everything into an array. It would look like:
def foo(*bar)
...
log.error "Error with arguments #{bar.joins(', ')}"
end

How to define a method some how like the 'yield' ( I mean, automatically catch the block)?

If I need to define a method called 'yields' which will call yiled 3 times:
def yields
3.times do
yield
end
end
And then I will use it in an other method:
def call_me_3_times
yields
end
In the console or irb:
>> call_me_3_times { puts 'me'} # => Cause error
=> LocalJumpError: no block given (yield)
from (irb):32:in `yields'
from (irb):35:in `call_me_3_times'
I hope you can read what I want;
And how to make the 'yields' autolly capture the block given?
I mean that when we use the 'yields',we don't need to pass it a '&block', just like the usage of 'yield'(we don't have to even mustn't pass the '&block' to 'yield', need we?).
Something like:
def call_me_3_times &block
yields &block
end
I tried to look at yield implementation to see if we could reproduce its behaviour, but I think it is a keyword, so there's no way to look at the implementation.
I tried with block_given, and looking at the implementation from the ruby core rdocs, I found that block_given? is implemented this way :
rb_f_block_given_p()
{
if (ruby_frame->prev && ruby_frame->prev->iter == ITER_CUR && ruby_block)
return Qtrue;
return Qfalse;
}
As you see, it's C, so it's too low-level implementation. We can't do the same.
If block_given? methods needs to rely on C implementation to just check that a block is given, I can't see how we could get that block and call it within ruby code.
So I think there's no way to do what you want.
You need given a block to you yields method or avoid yield if no block
no yield if no block :
def yields
3.times do
yield if block_given?
end
end
Pass a block to your yields methods
def call_me_3_times
yields { puts 'hello' }
end
A solution to this can be created using the techniques described in this blog post http://weblog.raganwald.com/2008/06/what-does-do-when-used-as-unary.html
def call_me_three_times
yields &(Proc.new) if block_given?
end
When you define a method as def some_method(&block) ruby will expect you to pass a block to the method. It will convert that block to a Proc and store it in the block variable.
If you prefix a Proc object with an & it will convert it to a block.
If you call Proc.new within a method and do not provide it with a block then it will create a Proc from the block passed to it.
Some test results are below
def yields
puts "Tripling"
3.times do
yield
end
end
def call_me_three_times
yields &(Proc.new) if block_given?
end
x="Foo"
call_me_three_times { puts x }
x="Bar"
call_me_three_times { puts x }
call_me_three_times
Output
Tripling
Foo
Foo
Foo
Tripling
Bar
Bar
Bar

Writing an around_each filter in ruby for every method within a block.

I need a method that takes a block, and performs something similar to an around_each filter for every method within the block.
For instance:
def method_that_takes_block
(#threads ||= Array.new) << Thread.new {yield if block.given?}
end
method_that_takes_a_block do
method_one
method_two
method_three
end
In this instance I would like my method that takes a block to Thread each method within the block and pushes that thread to the #threads array. Essentially I'm just looking for a DRY way to wrap a thread around every method called within a block.
You can't directly wrap a thread around each statement in the block body, if they can be arbitrary statements; there's no way to get that sort of control over the execution of a block body in Ruby. If you can restrict what goes in the block body, you have some more flexibility.
If each of the statements you are executing is simply a method call, as you imply in your example, you can use instance_exec to execute that block on a proxy object, which uses method_missing to spawn a new thread and then forward the method call on to the real object (or do whatever wrapper you're interested in; for the sake of example, I'll just wrap with some print statements):
class Proxy
def initialize obj
#obj = obj
end
def method_missing method, *args
puts "<wrapper>"
#obj.send method, *args
puts "</wrapper>"
end
end
class MethodWrapper
def tell_me_a_joke
puts "Knock, knock?"
end
def whos_there
puts "Orange"
end
def orange_who
puts "Orange you glad I didnt say banana?"
end
def wrap_around &blk
Proxy.new(self).instance_exec &blk
end
end
And here's how you can use it:
>> MethodWrapper.new.wrap_around { tell_me_a_joke; whos_there; orange_who }
<wrapper>
Knock, knock?
</wrapper>
<wrapper>
Orange
</wrapper>
<wrapper>
Orange you glad I didnt say banana?
</wrapper>
=> nil
The previous follows the pattern that you gave in your question, but it's less than ideal as only methods that are forwarded to the underlying object get wrapped:
>> MethodWrapper.new.wrap_around { tell_me_a_joke; puts "something" }
<wrapper>
Knock, knock?
</wrapper>
something
=> nil
You could instead just use instance_exec directly, and call the a wrapper method that takes a block, to get almost the same effect, though slightly less DRY as you need to call your wrapper method each time:
class SimpleWrapper
def tell_me_a_joke
puts "Knock, knock?"
end
def whos_there
puts "Interrupting cow"
end
def interrupting_co
puts "Moooooo!"
end
def wrap
puts "<wrap>"
yield
puts "</wrap>"
end
end
And in use:
>> SimpleWrapper.new.instance_exec do
wrap { tell_me_a_joke }
wrap { whos_there }
wrap { interrupting_co }
wrap { puts "Something" }
end
<wrap>
Knock, knock?
</wrap>
<wrap>
Interrupting cow
</wrap>
<wrap>
Moooooo!
</wrap>
<wrap>
Something
</wrap>
=> nil
You can also do it like this:
%w(method_one method_two method_three).each do |method|
(#threads ||= Array.new) << Thread.new { self.send(method) }
end

Resources