I’d want to write a DSL such that a surface method_missing catches
undefined methods and records the blocks that they define e.g.
foo do
puts “foo”
end
So I would end up with:
{ :foo=>#<Proc …> }
Presently I have something like this:
class Evaluator < BasicObject
def initialize(&block)
@config = {}
instance_eval(&block) if block
end
def __config__
@__config__
end
def method_missing(sym, *args, &block)
@__config__[sym] = block
end
end
However when I call on a block I want it to evaluate as if in the
defining
context (in this case toplevel), not inside the “DSL” class that
evaluated
via method_missing.
e = Evaluator.new do
foo do
puts “foo”
end
end
e.config[:foo].call
Instead of what I want, I get a method missing error for #puts.
def method_missing(sym, *args, &block)
if block_given?
@outside.class.class_eval do
define_method sym do |*args|
block.call *args
end
end
@config[sym] =
@outside.class.instance_method(sym).bind(self)
else
super
end
end
end
e = Evaluator.new(self) do
foo do
puts “foo”
end
end
e.config[:foo].call
I don’t know if that’s what you were after, but after playing around
for a bit that’s what I came up with.
You are missing a super on the method_missing. first you should ask if
is
a method on the class, and if it is, call super with no args.
try:
class Evaluator < BasicObject
def initialize(&block)
@config = {}
instance_eval(&block) if block
end
def __config__
@__config__
end
def method_missing(sym, *args, &block)
respond_to? sym ? super : @__config__[sym] = block
end
end
On Sun, Nov 6, 2011 at 5:35 PM, Intransition [email protected] wrote
@__config__
Any ideas?
–
Sincerely,
Isaac S.
Section C-4B Vice Chief, Order of the Arrow
Vice Chief of Administration, Tecumseh #65
Eagle Scout
Sorry 'bout the double posting, but it hit me that it’s only possible
to bind a method to an instance that’s of the same class. That and the
fact that the whole binding thing was useless in my previous post made
me go back to my computer and give it another shot.
It’s still not a great solution, because I couldn’t figure out how to
inherit from BasicObject and still have it work.
class Evaluator < Object
attr_reader :config, :outside
def method_missing(sym, *args, &block)
if block_given?
outside = @outside
outside.class.class_eval do
define_method sym do |*args|
outside.instance_exec *args, &block
end
end
@config[sym] = @outside.method(sym)
else
super
end
end
Now if i create an Object of CManager class (unlike other languages like C++ )
the constructor of it gets called but not the base class “Until i use the super
keyword”.
objManager = CManager.new
Can someone please help me here? And even if the this is supposed to be working
like this, isn’t it something different from the Object Oriented Concept which
says base class constructor always gets called first?
This is how it is supposed to work indeed, in Ruby there’s no notion of
default constructor like in C++ (a non-default constructor must be
invoked explicitly even there). With a call to super, a subclass can
choose how a superclass constructor is invoked in regards to passed
parameters and the order of statements. Or not to invoke it at all.
And this is not something unheard of in Object Oriented Concept world.
Just look at Objective-C for quick examples, where a subclass is
responsible also for its superclass allocation, not only initialization.
I think your idea about the “outside” is on the right track. I ended
doing
something akin to that, but in my case the “outside” is just toplevel
(aka
). Your solution is defining methods on the class of the outside
object. It gets the desired results, but at the expense of polluting the
class's namespace. In my case, using toplevel, that actually might not
be a
problem, so yea, this might be a workable solution. But I found using
methods had some other limitations (e.g. problem evaluate it in another
scope). So I ended up doing something a bit different, but at it's core
it's the same approach as yours, just without the method definitions.
The
"trick" I came up with was:
This defines a singleton method on the block overriding the original #call
method, forcing evaluation of the block at the toplevel. Once I had that
working, I refactored so I could get the same result but without have to
monkey patch the Proc object. So far so good.
If you are interested in the actual project code, it is here:
Now if i create an Object of CManager class (unlike other languages like C++ )
the constructor of it gets called but not the base class “Until i use the super
keyword”.
objManager = CManager.new
Can someone please help me here? And even if the this is supposed to be working
like this, isn’t it something different from the Object Oriented Concept which
says base class constructor always gets called first?
This is how it is supposed to work indeed, in Ruby there’s no notion of default
constructor like in C++ (a non-default constructor must be invoked explicitly even
there). With a call to super, a subclass can choose how a superclass constructor
is invoked in regards to passed parameters and the order of statements. Or not to
invoke it at all.
Ankush, an additional note: super is different than ordinary method
calls with regard to brackets. If you just write “super” the complete
argument list of the current method is copied (including block if
passed); regular methods are invoked with empty argument list in this
case. If you want to explicitly invoke the super class method without
arguments you need to write “super()”. Providing specific arguments
works as well of course: “super(1, ‘foo’)”.
I am a little confused and need some help. I am a newbie for Ruby but i
think i understand Object Oriented programming a little bit so i got
confused here.
I have got 2 classes here
CEmployee
class CEmployee
def initialize()
puts “inside CEmployee”
end
end
CManager
class CManager < CEmployee
def initialize()
puts “inside CManager”
end
end
Now if i create an Object of CManager class (unlike other languages like
C++ ) the constructor of it gets called but not the base class “Until i
use the super keyword”.
objManager = CManager.new
Can someone please help me here? And even if the this is supposed to be
working like this, isn’t it something different from the Object Oriented
Concept which says base class constructor always gets called first?
I really think that we should all take another look at the Ruby Object
Model. Dave T. has a great screencast on it. This question would be
answered if the ROM was analyzed closely.
On Sun, Nov 6, 2011 at 11:35 PM, Intransition [email protected]
wrote:
class Evaluator < BasicObject
end
Instead of what I want, I get a method missing error for #puts.
Any ideas?
You can’t have both (evaluate with self = Evaluator to trigger
Evaluator’s method missing and invoking the block in the defining
context) at the same time.
Question is, what happens if a missing method is invoked? In your
implementation the block is returned. I would at least change that
to return nil to avoid nasty side effects. But generally execution
cannot reasonably continue. What about catching the exception and
recording it?
12:54:59 ~$ ./x.rb
{:foo=>#Proc:0x101b3554@./x.rb:24}
12:55:02 ~$ cat -n x.rb
1 #!/bin/env ruby19
2
3 class Evaluator < BasicObject
4 def initialize(&block)
5 @config = {}
6
7 if block
8 begin
9 block.call
10 rescue ::NoMethodError => e
11 @config[e.name] = block
12 nil
13 end
14 end
15 end
16
17 def config
18 @config
19 end
20
21 end
22
23
24 e = Evaluator.new do
25 foo do
26 puts “foo”
27 end
28 end
29
30 p e.config
31
12:55:03 ~$
Nicely coded. And spot on. If I didn’t know that self would always be
toplevel in my particular case this is the code I would be using.
Actually… this code seems like it might make for a good general use
DSL
pattern. I’m going to try to generalize it further and see if makes
sense
to make it a reusable library.
Sean O’halpin wrote in post #1030618:
On Sun, Nov 6, 2011 at 10:35 PM, Intransition [email protected]
class Evaluator < BasicObject
def initialize(&block)
@config = {}
if block @context = block.binding.eval(“self”)
instance_eval(&block)
That’s a nice solution, I like that. I came up with something similar
after I posted last night, but I like this more.
Aside: What’s with threads sneaking their way into other ones? I
can’t be the only one seeing “Newbie:: Understand Constructor Flow”
alongside
the “Tricky DSL” one. Sorry if this has been discussed elsewhere.
Posted by Robert K.
You can’t have both (evaluate with self = Evaluator to trigger
Evaluator’s method missing and invoking the block in the defining
context) at the same time.
I’m not sure I understand what you’re saying. With some solutions being
posted above, at least ‘self’ is changed in the invoked block. That
seems
to be what Thomas was looking for, at least from my understanding.
And this is not something unheard of in Object Oriented Concept world. Just look
at Objective-C for quick examples, where a subclass is responsible also for its
superclass allocation, not only initialization.
An init method in Objective-C (at least in Cocoa) only calls its
superclass’s init method, not alloc. The class method alloc
automatically allocates enough space for the whole object.
(The strange thing about calling -[super init] in Cocoa is that you
have to assign your self variable to the result, just in case -[super
init] actually returns a whole different object. In that case, the
super initializer will deallocate the original one. That’s an area I
don’t understand very well.)
This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.