Passing Anonymous Classes With Ruby

I’m using JRuby to write a game in libgdx. In libgdx’s native Java, you
can add an event listener to an object by passing an anonymous class
that is created on the fly. I’ve found that I can also do this in Ruby,
but I wanted to know if anyone here saw a cleaner way.

@actions_crafting_button.add_listener(

Class.new(ClickListener) do

  def initialize(switch_actions_method)
    super()
    @switch_actions_method = switch_actions_method
  end

  def clicked(event, x, y)
    @switch_actions_method.call(:crafting)
  end

end.new(method(:switch_actions_method)))

This isn’t bad, but the only issue is that in Java it seems that the
scope of the anonymous class is embedded in the scope of the class its
being created in. Therefore, I didn’t need to override the constructor
and pass in any data to the new class. I could directly call the
switch_actions_method. I don’t get the same behavior in Ruby. Ruby seems
to define the anonymous class as if it really was a standalone class
with its own scope, which is more appropriate, but adds complexity here.

Here is the Java version for reference:

actionsCraftingButton.addListener(

  new ClickListener() {

      @Override
      public void clicked(InputEvent event, float x, float y) {
          switchActions("crafting");
      }
  }

);

Any thoughts are appreciated, including “you’re doing this all wrong,
here’s the right way” thoughts.

PREMISE I’ve never used that library nor JRuby and I’m working on what
you tell me.

That’s was an interesting problem. After some tries, I’ve found a just
slightly more convenient way:

parent = self
@actions_crafting_button.add_listener(
Class.new(ClickListener) do
define_method(:clicked) do|event, x, y|
parent.switch_actions(:crafting)
end
end.new
)

Notice the use of define_method instead of def and that you need a
variable containing a reference to the object you want send message to;
I’ve called it parent supposing you use this code in some class
method.
In that scope self is referring to the anonymous class itself, hence
the variable.

We can go a little further with the magic and do some simple method:

def on_click(object, &block)
object.add_listener(
Class.new(ClickListener) do
define_method(:clicked, &block)
enda.new
)
end

And then use it like this:
parent = self
on_click(@actions_crafting_button) do
parent.switch_actions(:crafting)
end

The latter is convenient only if you have a good amount of events to
listen or if you abstract more this method to use with different
listener without make it awkward to use (the primary problem is define
the right method for different listener types).

You can go further and define this kind of method directly on the object
that fire the event (more logical for my point of view).

Have fun! :slight_smile:

Thanks! But your first idea doesn’t seem to work. I’ve tried something
similar in the past, and I think the problem is that there seems to be
something strange with how JRuby talks to Java. These listeners are java
classes, and for some reason if I override their methods using
define_method, then the listeners ignore the new method and no
longer…listen ; )

I’ve even tried adding a call to the super method within the
redefinition to see if that helps. It seems like java doesn’t consider
the new overridden method to be a legitimate method for the listener. To
make things even trickier, I’ve been able to actually call the method
after creating it to make sure it really is there with the right
signature.

For some reason, the only way java will accept the new method is if I
create the entire class and override its constructor. I’ve even tried
defining the anonymous class, stuffing it in a variable, and then
directly redefining its methods like this,

new_class = Class.new(ClickListener) do … end.new

def new_class.enter(event, x, y, pointer, from_actor)

super

. . .

end

def new_class.exit(event, x, y, pointer, to_actor)

super

. . .

end

Sorry, I should have mentioned these are native java listeners. That
seems to be what is making this a rather sticky wicket.

Thanks for all of your help.

Uhm… Then, for the info I’ve, we can only hide the complexity and do
something like this:

def on_click(object, &fn)
object.add_listener(
Class.new(ClickListener) do
def initialize(fn)
@fn = fn
end
def clicked(event, x, y)
fn.(event, x, y)
end
end.new(fn)
)
end

usage remain the same

parent = self
on_click(@actions_crafting_button) do
parent.switch_actions(:crafting)
end