Composite pattern and DSL design

Hello,

I am currently writing a little DSL for gui framework (Ruiby, for gtk).
As my DSL become more and more complex, I have some design issue.

For declaring a drawing area, with event handler, I use

canvas(w,h,{
  mouse_down: proc { |w,e| … },
  mouse_up: proc { |w,e| … },
  mouse_move: proc { |w,e| … },
  expose: proc { |w,gc| … }
})

But the list of handler becoming bigger, I wonder if this form should be
better :
canvas w,h do
  on_mouse_up do |e|
    …
  end
  on_mouse_down do |e|
     …
  end
  …
end

The issue is that in this form, user can put other commands
than on_xxx in the block.
So if it put, by example, a button, I can’t control what will append:
canvas w,h do
  on_mouse_up do |e|
    …
  end
  button “hello” do alert(“coucou”) end
end

so the framework will become hard to use : very common mistake will
produce exotic bug.

So here my question:
Implementing object composition by cascade of closures, how to control
the ‘syntax’ of the composition?

···

--
Posted via http://www.ruby-forum.com/.

Regis d'Aubarede wrote:

So here my question:
Implementing object composition by cascade of closures, how to control
the ‘syntax’ of the composition?

The same way you yield events to your callbacks, you should yield some
"context" object to the other blocks. The most common object would
probably be `self` so you can append to it:

    class GUI::Canvas
      def initialize(width, height)
        @width, @height = width, height
        yield self
      end
    end

    class GUI::Button
      def initialize(width, height)
        @width, @height = width, height
        yield self
      end
    end

    canvas w, h do |c|
      c.on_mouse_up do |e|
      end
      c.button "hello" do |b|
        b.on_mouse_up do |e|
          c.alert("coucou")
        end
      end
    end

Only the methods that are available on the context object will work, and
it's explicit about which context is being appended so there are no
exotic bugs.

Look at XML::Builder (either the original [1] or nokogiri [2]) for
examples of this pattern.

[1]: http://builder.rubyforge.org/
[2]: http://nokogiri.org/Nokogiri/XML/Builder.html

You can also use `instance_eval` if you don't want to yield the context
to the block. (Or `instance_exec` if you still need to pass parameters
to the block.) But this will change the scope of the block so you can't
access any of the outer closures unless you yield it in:

    class GUI::Canvas
      def initialize(width, height, &block)
        @width, @height = width, height
        instance_exec(self, &block)
      end
    end

    class GUI::Button
      def initialize(width, height)
        @width, @height = width, height
        instance_exec(self, &block)
      end
    end

    canvas w, h do |c|
      # with instance_eval, self is the canvas
      on_mouse_up do |e|
      end
      c.button "hello" do |b|
        # with instance_eval, self is the button
        on_mouse_up |e| do
          # error: with instance_eval, `c` is not available here:
          c.alert("coucou")
        end
      end
    end

Andrew Vit

Andrew Vit wrote in post #1131541:

Regis d'Aubarede wrote:

So here my question:
Implementing object composition by cascade of closures, how to control
the ‘syntax’ of the composition?

The same way you yield events to your callbacks, you should yield some
"context" object to the other blocks. The most common object would

Thank you, that work.
but to try to avoid instance_eval and yielding parent object
to each children (it's a little tyring for user).
for that i use a @stack variable.

···

==============================
ex. :
vbox {
  button 'e' { }
  hbox {
     canvas 22,33
     button 'a' { action }
  }
}

implementation:

====================================
def vbox
  w= Gtk::VBox.new
  @stack << w
  yield
  @stack.pop!
  @stack.last.pack w
  w
end

def button(r,&blk)
  @stack.last.authorize? :button # the solution?
  w=Gtk:Button.new(t)
  w.signal('click') { &blk.call } if &blk
  @stack.last.pack w
  w
end

so the solution should be to stack a dummy object before
canvas yield :

====================================
def canvas(w,h,&blk)
   @stack.last.authorize? :canvas
   w= Drawing.new(w,h)
   @stack << NoPackClass.new
   yield
   @stack.pop
end

def button(r,&blk)
  @stack.last.authorize? :button
  ....
end

====================================

So @stack.last has role of parent in the composition.

--
Posted via http://www.ruby-forum.com/\.

Hello,

I am currently writing a little DSL for gui framework (Ruiby, for gtk).
As my DSL become more and more complex, I have some design issue.

For declaring a drawing area, with event handler, I use

canvas(w,h,{
  mouse_down: proc { |w,e| … },
  mouse_up: proc { |w,e| … },
  mouse_move: proc { |w,e| … },
  expose: proc { |w,gc| … }
})

But the list of handler becoming bigger, I wonder if this form should be
better :
canvas w,h do
  on_mouse_up do |e|
    …
  end
  on_mouse_down do |e|
     …
  end
  …
end

The issue is that in this form, user can put other commands
than on_xxx in the block.
So if it put, by example, a button, I can’t control what will append:
canvas w,h do
  on_mouse_up do |e|
    …
  end
  button “hello” do alert(“coucou”) end
end

You can easily avoid that by providing a proxy instance to the block
which will only accept #on_xyz methods and forward them.

class EventProxy < BasicObject
  def initialize(owner)
    @owner = owner
  end

  def method_missing(sym, *args, &block)
    if /\Aon_/ =~ sym
      @owner.send(sym, *args, &block)
    else
      super
    end
  end
end

def canvas(&b)
  Canvas.new.tap do |canvas|
    # ...
    EventProxy.new(canvas).instance_eval(&block)
    # ...
  end
end

Note: a bit more error checking and logic may be in order, e.g. to
convert between the proxy and the proxied object for method arguments
and return values.

so the framework will become hard to use : very common mistake will
produce exotic bug.

I am not entirely sure that this is true. Why is it necessary to
first define event listeners before further composing the UI? That
may be quite a restriction. In fact I would create the framework in a
way that I could add and remove listeners at will at any time in the
widget's life cycle.

So here my question:
Implementing object composition by cascade of closures, how to control
the ‘syntax’ of the composition?

See above.

Kind regards

robert

···

On Tue, Dec 24, 2013 at 3:38 PM, Regis d'Aubarede <lists@ruby-forum.com> wrote:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Nice to hear from you, Robert. :slight_smile:

···

On Thu, Dec 26, 2013 at 4:31 PM, Robert Klemme <shortcutter@googlemail.com>wrote:

On Tue, Dec 24, 2013 at 3:38 PM, Regis d'Aubarede <lists@ruby-forum.com> > wrote:
> Hello,
>
> I am currently writing a little DSL for gui framework (Ruiby, for gtk).
> As my DSL become more and more complex, I have some design issue.
>
>
> For declaring a drawing area, with event handler, I use
>
> canvas(w,h,{
> mouse_down: proc { |w,e| … },
> mouse_up: proc { |w,e| … },
> mouse_move: proc { |w,e| … },
> expose: proc { |w,gc| … }
> })
>
> But the list of handler becoming bigger, I wonder if this form should be
> better :
> canvas w,h do
> on_mouse_up do |e|
> …
> end
> on_mouse_down do |e|
> …
> end
> …
> end
>
> The issue is that in this form, user can put other commands
> than on_xxx in the block.
> So if it put, by example, a button, I can’t control what will append:
> canvas w,h do
> on_mouse_up do |e|
> …
> end
> button “hello” do alert(“coucou”) end
> end

You can easily avoid that by providing a proxy instance to the block
which will only accept #on_xyz methods and forward them.

class EventProxy < BasicObject
  def initialize(owner)
    @owner = owner
  end

  def method_missing(sym, *args, &block)
    if /\Aon_/ =~ sym
      @owner.send(sym, *args, &block)
    else
      super
    end
  end
end

def canvas(&b)
  Canvas.new.tap do |canvas|
    # ...
    EventProxy.new(canvas).instance_eval(&block)
    # ...
  end
end

Note: a bit more error checking and logic may be in order, e.g. to
convert between the proxy and the proxied object for method arguments
and return values.

> so the framework will become hard to use : very common mistake will
> produce exotic bug.

I am not entirely sure that this is true. Why is it necessary to
first define event listeners before further composing the UI? That
may be quite a restriction. In fact I would create the framework in a
way that I could add and remove listeners at will at any time in the
widget's life cycle.

> So here my question:
> Implementing object composition by cascade of closures, how to control
> the ‘syntax’ of the composition?

See above.

Kind regards

robert

--

:slight_smile:

I should have mentioned that there is another way to reach the goal:
the widget class could store an internal state indicating that it is
under construction. During that time other methods than defining event
listeners could throw. Still, I am not sure it is such a good idea to
be so restrictive.

Kind regards

robert

···

On Fri, Dec 27, 2013 at 2:29 AM, tamouse pontiki <tamouse.lists@gmail.com> wrote:

Nice to hear from you, Robert. :slight_smile:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Robert Klemme wrote in post #1131685:

the widget class could store an internal state indicating that it is
under construction. During that time other methods than defining event
listeners could throw. Still, I am not sure it is such a good idea to
be so restrictive

yes,this is my solution : this avoid the instance_éval on an object
which is unknown by the user.

···

--
Posted via http://www.ruby-forum.com/\.