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.
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 <transfire@gmail.com> wrote
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.
Any ideas?
--
Sincerely,
Isaac Sanders
Section C-4B Vice Chief, Order of the Arrow
Vice Chief of Administration, Tecumseh #65
Eagle Scout
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 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 ~$
Kind regards
robert
···
On Sun, Nov 6, 2011 at 11:35 PM, Intransition <transfire@gmail.com> wrote:
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.
Any ideas?
On Sun, Nov 6, 2011 at 10:35 PM, Intransition <transfire@gmail.com> wrote:
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.
Any ideas?
Sorry 'bout the double posting, but it hit me that it's only impossible
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
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
1. CEmployee
class CEmployee
def initialize()
puts "inside CEmployee"
end
end
2. 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 Thomas has a great screencast on it. This question would be
answered if the ROM was analyzed closely.
···
On Mon, Nov 7, 2011 at 6:56 AM, Robert Klemme <shortcutter@googlemail.com>wrote:
On Sun, Nov 6, 2011 at 11:35 PM, Intransition <transfire@gmail.com> wrote:
> 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.
> 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 ~$
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 Klemme
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.
Can you elaborate on what you said?
Thanks,
-Luke
···
On Sun, Nov 6, 2011 at 10:35 PM, Intransition <transfire@gmail.com>
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.
What do you think?
···
On Monday, November 7, 2011 9:14:44 AM UTC-5, Sean O'Halpin wrote:
You could capture the calling self by getting it from the binding of
the block and hide the evaluating call behind your own #eval method:
class Evaluator < BasicObject
def initialize(&block) @__config__ = {}
if block @context = block.binding.eval("self")
instance_eval(&block)
end
end
def __config__ @__config__
end
def method_missing(sym, *args, &block) @__config__[sym] = block
end
def eval(sym, *args) @context.instance_exec(*args, &@__config__[sym])
end
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
1. CEmployee
class CEmployee
def initialize()
puts "inside CEmployee"
end
end
2. 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?
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
<main>). 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:
def block.call(*args)
::Kernel.eval('self',::TOPLEVEL_BINDING).instance_exec(*args,
&self)
end
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:
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')".
Kind regards
robert
···
On Mon, Nov 7, 2011 at 4:32 AM, Gennady Bystritsky <gennady-ruby@bystr.com> wrote:
On Nov 6, 2011, at 7:16 PM, Ankush Ganatra wrote:
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
1. CEmployee
class CEmployee
def initialize()
puts "inside CEmployee"
end
end
2. 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?
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.
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.)
···
On Sun, Nov 6, 2011 at 9:32 PM, Gennady Bystritsky <gennady-ruby@bystr.com> wrote:
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.