Reputation: 311506
Inside the body of a class, I'd like to pass a block to a method called with
. For the lifetime of the block, I would like a with_value
method to be available.
Otherwise, everything inside the block should behave as if it were outside the block.
Here's an example:
class C
extend M
with "some value" do
do_something_complicated
do_something_complicated
do_something_complicated
end
end
We can almost get this with:
module M
def with(str, &block)
Object.new.tap do |wrapper|
wrapper.define_singleton_method :with_value do # Here's our with_value
str # method.
end
end.instance_eval &block
end
def do_something_complicated # Push a value onto an
(@foo ||= []).push with_value # array.
end
end
but there's a problem: since we're evaluating the block passed to with
inside the context of a different object, do_something_complicated
isn't available.
What's the right way to pull this off?
Upvotes: 1
Views: 91
Reputation: 168101
Here is a way that is closer to your attempt:
module M
def with(str, &block)
dup.tap do |wrapper|
wrapper.define_singleton_method :with_value do
...
end
end.instance_eval &block
end
...
end
dup
will duplicate the class from where with
is called as a class method.
Upvotes: 0
Reputation: 168101
This will make with_value
available only within the block. However, _with_value
will be defined within or outside of the block.
module M
def _with_value
...
end
def with(str, &block)
alias with_value _with_value
block.call
undef with_value
end
...
end
I cannot tell from the question whether this is a problem. If it is a problem, you need to further describe what you are trying to do.
Upvotes: 3
Reputation: 48368
Basically, the idea is to use method_missing
to forward method calls from the dummy class to the calling class. If you also need to access instance variables, you can copy them from the calling class to your dummy class, and then back again after the block returns.
The Ruby gem docile
is a very simple implementation of such a system. I suggest you read the source code in that repository (don't worry, it's a very small codebase) for a good example of how DSL methods like the one in your example work.
Upvotes: 1