Using any trick to omit () in constant lookup?

Hi.

I once had the idea of an abstracted UI language.

My first attempt was to do something like:

Button.new

And convert the Button to the specific toolkit in
question - i.e. GTK Button, QT Button and so on.

This was not really possible, at least not for me.

When you have:

class Button

Then ruby does not allow you to change that object
lateron. In other words, it will always be of class
Button, and never of class Gtk::Button, which I would
need in order to make an abstracted toolkit.

Lately I had another idea. I could simply try to
trick Ruby.

def Button

Do something here, decide which toolkit to

use, then return that.

It would then allow me to do something like:

use :gtk

Button.new

The interface logic would be described only once
and then be valid no matter which toolkit would
be used, within reason. (I am more interested in
finding a common base within all toolkits, and
using specialized solutions within that toolkit
only lateron.)

I could then switch to qt like this:

use :qt

Ok, I tried a first proof of concept but failed.


require ‘gtk2’

$use_gtk = true

def Button
if $use_gtk
return Gtk::Button
else # else use something else.
end
end

x = Button.new
puts x.class


^^^^^ The above code does not work. It stops with an
error like this here:

uninitialized constant Button (NameError)

It works however when I change the line to:

x = Button().new

But this is ugly. I don’t think I want to use that,
it does not please my eyes.

Is there a way to tell ruby to change its behaviour
and treat Button not as a constant, but instead as
a method invocation call where the () parens are
omitted?

I think if I would have that option, I could write a
cross UI DSL “language”.

For any more ideas I am quite happy too. My ultimate
goal is to actually use just ONE way to describe ALL
User Interface elements, including the WWW. Be it
in Ruby, or with an abstract interface language that
has to be parsed, does not matter that much to me.

Though using Ruby directly would simplify my life
of course.

Make a simple button wrapper class and put the logic from your Button
method in the constructor.

class Button
def initialize()
if gtk
@button = GTK::Button

end
end

def method_missing(…)
@button.send(…)
end
end

2011/12/8 Marc H. [email protected]:

What you are asking for is possible, simply by overriding the new
method:

class GTKButton; end
class OldButton; end

class Button
def self.new(*args,&blk)
if $use_gtk
GTKButton.new(*args,&blk)
else
OldButton.new(*args,&blk)
end
end
end

$use_gtk = true
puts Button.new.class

This is unusual, and it may surprise users of your library to call
Button.new and actually receive an object of a different class. But it’s
not unreasonable - I did this in a ruby-ip library once, so that
IP.new(…) would return either an IP::V4 object or an IP::V6 object
depending on the argument format.

Gunther’s solution is the more general and flexible one. It allows your
Button wrapper to be composed of one or more other objects, which can be
changed dynamically as the object runs. A method call x(a,b,c) on your
wrapper object can then be mapped to whatever calls are required on the
underlying objects, e.g. @obj1.y(a) and @obj2.z(c,b)

I suspect you will want to do both: return different classes for each
type of Button, and within those classes, map the method calls to an
underlying object or objects. This will allow your Button facade objects
to implement the same API calls and translate them to what is actually
required.

This is of course only part of the story. If you want your UI toolkit to
be interchangeable then it will have to expose the same programming
model and data structures, and unifying that across the various
underlying libraries may be more difficult than you expect.

or an idea, forgot your Button class, and override const_missing

def Object.const_missing(name)
if name == :Button
if $use_gtk
return Gtk::Button
else # else use something else.
end
else
super
end
end

so:

p Button #=> Gtk::Button

but it think Gunther has the better idea

On Dec 8, 2011, at 0:06, Marc H. [email protected] wrote:

My first attempt was to do something like:

Button.new

And convert the Button to the specific toolkit in
question - i.e. GTK Button, QT Button and so on.

Don’t convert the button instance. Change the class.

Button = QT::Button

Button.new

On Thu, Dec 8, 2011 at 10:35 AM, Hans M. [email protected] wrote:

or an idea, forgot your Button class, and override const_missing

There is a much simpler solution

require ‘gtk2’

$use_gtk = true

def Button
if $use_gtk
Gtk::Button
else # else use something else.
raise “unknown”
end.new
end

x = Button
puts x.class

But the problem will come later, i.e. when using all those toolkit
instances. They most likely have different interfaces. So you better
wrap all the classes with your own classes which exhibit a standard
interface (which you must define) and work with specific toolkit
classes behind the scenes. The ugly thing then will be to find the
way back from a toolkit class to its corresponding wrapper. Another
option could be to add methods to toolkit classes to create the
uniform interface.

I am not sure whether all the effort pays off though…

Cheers

robert

On Thu, Dec 8, 2011 at 11:51 AM, Ryan D. [email protected]
wrote:

Don’t convert the button instance. Change the class.
Road to perdition.

Button = QT::Button

Button.new

Cheers

robert