Reputation: 3079
Currently I am doing something like the following to have the Runnable
classes' run
method access methods of the 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's block executing!"
end
end
end
runnable = Runnable.new
context = ExecutionContext.new
runnable.do_run(context)
The idea is that the Runnable
class is written by "end users" who want to access functionality (such as the message
and answer?
methods) of an ExecutionContext
instance which is provided from elsewhere. In fact, the Runnable
class is all the "end user" is concerned about and it should be as small and simple as possible.
The code above works as expected in that all methods called by Runnable.run
are provided by the ExecutionContext
.
However I am wondering if there is a more elegant way to achieve the same thing, where I can avoid using method_missing
and defining the temporary @context
instance variable.
Ideally, I would like to modify the code to something similar to this (ExecutionContext
stays the same):
class Controller
def do_run(context, runnable)
runnable.extend_from_instance(context)
runnable.run
end
end
class Runnable
def run
if answer?
message "Runnable's block executing!"
end
end
end
runnable = Runnable.new
context = ExecutionContext.new
Controller.new.do_run(context, runnable)
Is there such thing as extend_from_instance
?
Thanks everyone for looking into this.
Since it has come up in the two answers I got so far, I realised I should mention the following restrictions: Unfortunately neither Runnable
nor ExecutionContext
can be turned into module
s. They need to be class
es as their instances will behave differently for different cases (both will have their own instance variables etc).
I really need the run
method to execute as if it was part of the ExecutionContext
instance (context
in the code).
Upvotes: 3
Views: 284
Reputation: 45943
Yes, you don't want to use method_missing
in this case.
I don't really understand what you are trying to accomplish. Perhaps define_block
is not the correct name...?
However, a module seems to have the functionality that you want.
module Messaging
def message(text)
puts "Provider.message: #{text}"
end
end
class Parent
include Messaging
...
EDIT: I think you want to use binding. It allows you to pass the current scope (context) to another scope.
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 foo's local
variable. But it doesn't have access to it because local
exists only in foo's scope (or context).
So, you can pass in foo's scope as binding
.
See binding.
Upvotes: 1
Reputation: 7220
I think what B Seven said was correct. It makes the most sense to create the shared functionality in a Module
and include
that module in the class. However if you really want something like your extend_from_instance
you can use Ruby's Object#extend method to dynamically extend the module in an instance of the class (though I think this is a bad idea). Working form your desired code:
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's block executing!"
end
end
end
runnable = Runnable.new
Controller.new.do_run(ExecutionContext, runnable)
Again though, this is a bad idea and will probably come back to bite you in the long run.
Upvotes: 0