i have two methods in base class, method_1 does perform_async with some *args by calling the worker and after job is done it will call method_2, how to pass same args to method_2?
def self.method_1(action_name, *args)
perform_async(action_name,args)
method_2
end
def self.method_2
some action here
method_1(action_name, args)
handle exception here
end
getting error undefined local variable or method `action_name' in line number 3 of method 2.
If I'm understanding what you're going for, what about using an instance variable to store the splat parameter arguments?
def self.method_1(action_name, *args)
#args = args
perform_async(action_name, args)
method_2
end
def self.method_2
some action here
method_1(action_name, #args)
handle exception here
end
Related
I want a class method, step, which dynamically creates instance methods.
step(:start) do |payload|
puts payload
end
This would create the equivalent of:
def start(payload)
payload
end
The trouble I am having is getting a block passed to the class method evaluate in the context of the instance.
The problem seems to be that the block, because it is a closure, is evaluated in the context of where is was defined, the class.
module Steps
def step(name, &block)
define_method(name) do |payload|
self # => Foo instance
block.call(payload)
end
end
end
class Foo
extend Steps
step(:start) do |payload|
self # => Foo class
payload # => 1
self.data # => undefined method `data' for Foo:Class (NoMethodError)
end
def data
'DATA'
end
end
puts Foo.new.start(1)
Is it possible to have the block evaluated in the context of the instance?
The closest I have come is using instance_eval however I can't figure out how to pass the argument payload to the block:
define_method(name) do |payload|
instance_eval(&block)
end
When I do the above self in the passed block is an instance of Foo so I can call data, but how to access payload?
instance_exec
Executes the given block within the context of the receiver (obj). In
order to set the context, the variable self is set to obj while the
code is executing, giving the code access to obj’s instance variables.
Arguments are passed as block parameters.
define_method(name) do |payload|
instance_exec(payload, &block)
end
ref: Change Block Binding Without Eval?
When trying to use an instance method of a Ruby-C-Class:
RubyCClass.new.someMethod()
Ruby is raising the following error:
Error: wrong argument type RubyCClass (expected Data)
Is there any way I can instantiate the class properly such that RubyCClass is instantiated to the extent that someMethod will begin execution? In other words, is there a way I can inject Data into RubyCClass such that someMethod begins execution?
I'm not sure where that error is being generated; is it when the engine is evaluating the value returned by your Ruby code?
If so, you could do whatever you want to do, and then return a dummy Data object:
RubyCClass.new.someMethod()
# do other things, then:
Data.new
# or whatever it is you do to create a Data instance;
# as the final value in your code it will be returned
[Note: This answer was posted when the question was drastically different; it has been edited since then.]
I'm not completely sure if your question, but I think your main problem as that you are using method instead of public_send. (And, by the way, you can get a list of an object's public methods by calling object.public_methods, in case that's helpful.)
Here is some code that illustrates what might work for you:
#!/usr/bin/env ruby
class MethodAccessibility
attr_reader :accessibles, :inaccessibles
def initialize
#accessibles = []
#inaccessibles = []
populate_data
end
def method_accessible?(object, method_name, *args)
begin
object.public_send(method_name, args)
true
rescue Exception => e
e.to_s != "Error: This method cannot be used within the User Interface"
end
end
def add_to_appropriate_array(object, method_name, *args)
accessible = method_accessible?(object, method_name, args)
(accessible ? accessibles : inaccessibles) << method_name
end
def populate_data
object = # create the object on which to call the methods
add_to_appropriate_array(object, :method1, [:arg1, :arg2]) # for examples
add_to_appropriate_array(object, :method2, [])
# ...
end
end
ma = MethodAccessibility.new
ma.accessibles # do something with this array, or the `inaccessibles` array
I am using some metaprogramming (using ruby 2.3.1) so that i can call a method before calling the actual method i want to call - like a before_filter/before_action.
The pseudocode below explains what i am trying to achieve
module Api
def call_this_method_everytime
if A
go ahead and call the actual method being called
else
stop here do not call he method it was supposed to call
end
end
def when_i_call_this_method
end
def or_this_method
end
end
With the help of a SO member i was able to theoretically understand what i want to do using metaprogramming - which i arrived at the code below.
module Api
def heartbeat
...
end
def interceptor(name)
original_method = "original #{name}"
alias_method original_method, name
define_method(name) do |*args|
heartbeat
result = send original_method, *args
puts "The method #{name} called!"
result
end
end
end
Instead of calling the method i want - i call interceptor method with the name of the actual function i want to call as an argument. I would then first call heartbeat function and if the check is OK then i proceed with actually calling the actual function.
However having limited knowledge of metaprogramming i am getting this error
NoMethodError: undefined method 'alias_method'
Searching around did not help. Any help appreciated.
A simpler way of doing the same thing:
def interceptor(name, *args)
if 1 == 1
send name, args
end
end
interceptor(:puts, "oi")
In my ruby project, i want to create a task manager class which will periodically iterate through an array of registered functions. I need to pass functions as argument to the register function, create a thread and when the thread timer fires, it needs to call each function in array.
class TaskMan
##func_array = Array.new
def self.register_func &arg_func
##func_array.push arg_func
end
def self.run_server
loop do
##func_array.each do |func|
func.call
end
sleep 60
end
end
end
class Callee
def self.func_0
puts "func_0 called."
end
def self.func_1
puts "func_1 called."
end
end
TaskMan.register_func Callee.func_0
TaskMan.register_func Callee.func_1
taskman_thread = Thread.new do
TaskMan.run_server
end
taskman_thread.join
Basically, this is what i want to do, but i'm not sure how to use to_proc and call, to push in the functions and call them.
Other solutions are also appreciated, but i don't want to avoid learning passing functions as arguments, putting them in array and calling them.
Thanks.
Use the method method to get the class methods as callable objects (Method instances in this case) and then apply & to those objects:
TaskMan.register_func &Callee.method(:func_0)
TaskMan.register_func &Callee.method(:func_1)
Now you can throw all sorts of things into the func_array:
TaskMan.register_func &SomeClass.method(:m) # class method
TaskMan.register_func &some_object.method(:m) # an instance method
TaskMan.register_func &some_lambda # a lambda function
TaskMan.register_func { puts "pancakes" } # a block
# etc.
You can instead pass the receiver and the function you intend calling as below:
class TaskMan
##func_array = Array.new
def self.register_func(receiver, func)
##func_array.push([receiver, func])
end
def self.run_server
loop do
##func_array.each do |(receiver, func)|
receiver.method(func).call
end
sleep 60
end
end
end
class Callee
def self.func_0
puts "func_0 called."
end
def self.func_1
puts "func_1 called."
end
end
TaskMan.register_func Callee, :func_0
TaskMan.register_func Callee, :func_1
taskman_thread = Thread.new do
TaskMan.run_server
end
taskman_thread.join
Let's say you have methods func_0 and func_1. To call them using array iteration you can do this
methods = [:func_0, :func_1]
methods.each do |m|
send(m)
end
I want to call some_instance_method from some_class_method. In the following code, the class method is being called from the instance method.
class Foo
def self.some_class_method
puts self
end
def some_instance_method
self.class.some_class_method
end
end
Does what you want to do really make sense? Which instance should the instance method be called on? If it doesn't matter, you can do this:
class Foo
def self.some_class_method
new.some_instance_method
puts self
end
def some_instance_method
self.class.some_class_method
end
end
This will cause an infinite loop, of course.
To call an instance method you need to have that object already instantiated somewhere, you can't call an instance method without an object.
def self.some_class_method
puts self.first.some_instance_method
end
Something like this should work
You need to pass the instance you want to be the recipient of the call to the class method. So the new code is:
class Foo
def self.some_class_method(instance)
instance.some_other_instance_method
puts self
end
def some_instance_method
self.class.some_class_method(self)
end
end
Notice that the class method is calling 'some_other_instance_method'. If the class method were to call the same method that called it, it would enter an endless loop.