Extending an instance using methods from another instance of another class
I am currently doing something like the following to have the Runnable
classes method accessors run
for ExecutionContext
:
class ExecutionContext
def message(text)
puts "ExecutionContext.message: #{text}"
end
def answer?
puts "ExecutionContext.answer called"
true
end
end
class Controller
def do_run(context)
@context = context
run
@context = nil
end
def method_missing(mth, *args)
@context.send(mth, *args)
end
end
class Runnable < Controller
def run
if answer?
message "Runnable block executing!"
end
end
end
runnable = Runnable.new
context = ExecutionContext.new
runnable.do_run(context)
The idea is that the class is Runnable
written by "end users" who want to access the functions (eg, methods message
and answer?
) of an instance ExecutionContext
that is provided from other sources. In fact, a class Runnable
is the entire "end user" and should be as small and simple as possible.
The above code works as expected, that all methods called Runnable.run
are provided ExecutionContext
.
However, I'm wondering if there is a more elegant way to achieve the same, where I can avoid using method_missing
and defining a temporary variable @context
.
Ideally I would like to change the code to something similar to this ( ExecutionContext
remains unchanged):
class Controller
def do_run(context, runnable)
runnable.extend_from_instance(context)
runnable.run
end
end
class Runnable
def run
if answer?
message "Runnable block executing!"
end
end
end
runnable = Runnable.new
context = ExecutionContext.new
Controller.new.do_run(context, runnable)
Is there such a thing as extend_from_instance
?
Update
Thank you everyone for looking at this.
Since it appeared in the two answers I've received so far, I realized that I should mention the following restrictions: Unfortunately, neither Runnable
, nor ExecutionContext
can be turned into module
s. They should be class
es as their instances will behave differently for different cases (both will have their own instance variables, etc.).
I really need a method run
to execute as if it were part of an instance ExecutionContext
( context
in code).
source to share
Yes, you don't want to use method_missing
in this case.
I really don't understand what you are trying to achieve. Perhaps define_block
not the correct name ...?
However, the module appears to have the required functionality.
module Messaging
def message(text)
puts "Provider.message: #{text}"
end
end
class Parent
include Messaging
...
EDIT: I think you want to use anchor. It allows you to transfer the current area (context) to another area.
def print_local binding
puts 'local is ' + binding.eval('local')
end
def foo
local = 'value'
print_local binding
end
foo
=> local is value
In this case, print_local wants to print the variable foo local
. But it doesn't have access to it because it local
only exists in the scope of foo (or context).
So, you can go to scope foo like binding
.
See binding .
source to share
I think B Seven said correctly. The most reasonable to establish a common functionality Module
, and include
this module in class. However, if you really want something like your own extend_from_instance
, you can use Ruby Object # extend to dynamically extend a module on a class instance (although I think this is a bad idea). Create the code you want:
module ExecutionContext
def message(text)
puts "ExecutionContext.message: #{text}"
end
def answer?
puts "ExecutionContext.answer called"
true
end
end
class Controller
def do_run(context_module, runnable)
runnable.extend(context_module)
runnable.run
end
end
class Runnable
def run
if answer?
message "Runnable block executing!"
end
end
end
runnable = Runnable.new
Controller.new.do_run(ExecutionContext, runnable)
Again, this is a bad idea and will probably come back to bite you eventually.
source to share