JRuby constructor issue or just me?

Hi,

I can't figure out why this doesn't work. I need to do this because I'm
initializing the button elements separately within the constructor, but
I want to ensure that the button is actually created first. I do this
all the time with GTK+ & Ruby/GNOME2 on MRI, but it doesn't seem to want
to go with JRuby.

Is this problem something to do with the overloaded JButton
constructors? How can I force a particular one to be called? I've read
several Google results, but since I'm trying to call the JButton()
constructor with no arguments, I can't figure out what to do.

Any help would be appreciated.

Cheers,

ast

$ cat $HOME/jbutton.rb
require 'java'

include_class 'javax.swing.JButton'

class Foo < JButton
  def initialize(count)
    super()
    self.text = "Foo"
    @count = count
  end
end

foo = Foo.new

$ cat $HOME/output.txt
nene$ c:/devel/src/jruby/bin/jruby -v
jruby 1.3.0 (ruby 1.8.6p287) (2009-04-26 r6586) (Java HotSpot(TM) Client VM 1.6.0_13) [x86-java]
nene$ c:/devel/src/jruby/bin/jruby jbutton.rb
jbutton.rb:13: wrong # of arguments(0 for 1) (ArgumentError)

The above example is contrived to produce a suitable testcase, but based
on the actual code that I'm trying to run.

···

--
Andrew S. Townley <ast@atownley.org>
http://atownley.org

Andrew S. Townley wrote:

$ cat $HOME/output.txt
nene$ c:/devel/src/jruby/bin/jruby -v
jruby 1.3.0 (ruby 1.8.6p287) (2009-04-26 r6586) (Java HotSpot(TM) Client VM 1.6.0_13) [x86-java]
nene$ c:/devel/src/jruby/bin/jruby jbutton.rb
jbutton.rb:13: wrong # of arguments(0 for 1) (ArgumentError)

Constructors can be a little fiddly from within a Ruby class that extends a Java class, due to some complicated gymnastics. If at all possible you'd be better off aggregating the button, but if you must extend, extend with the same constructor style and just call "super"

We want to improve how this works, but the extension code needs a rewrite before that can happen. Contributing broken specs for cases like this that ought to "just work" would help us too.

- Charlie

In Ruby, "super()" is different from just "super". The former will invoke a superclass' constructor with no parameters, while the latter will call it with the same number of parameters as was in "new" call.

Gennady.

···

-----Original Message-----
From: Andrew S. Townley [mailto:ast@atownley.org]
Sent: Sunday, April 26, 2009 2:50 PM
To: ruby-talk ML
Subject: JRuby constructor issue or just me?

Hi,

I can't figure out why this doesn't work. I need to do this because
I'm
initializing the button elements separately within the constructor, but
I want to ensure that the button is actually created first. I do this
all the time with GTK+ & Ruby/GNOME2 on MRI, but it doesn't seem to
want
to go with JRuby.

Is this problem something to do with the overloaded JButton
constructors? How can I force a particular one to be called? I've
read
several Google results, but since I'm trying to call the JButton()
constructor with no arguments, I can't figure out what to do.

Any help would be appreciated.

Cheers,

ast

$ cat $HOME/jbutton.rb
require 'java'

include_class 'javax.swing.JButton'

class Foo < JButton
  def initialize(count)
    super()
    self.text = "Foo"
    @count = count
  end
end

foo = Foo.new

$ cat $HOME/output.txt
nene$ c:/devel/src/jruby/bin/jruby -v
jruby 1.3.0 (ruby 1.8.6p287) (2009-04-26 r6586) (Java HotSpot(TM)
Client VM 1.6.0_13) [x86-java]
nene$ c:/devel/src/jruby/bin/jruby jbutton.rb
jbutton.rb:13: wrong # of arguments(0 for 1) (ArgumentError)

The above example is contrived to produce a suitable testcase, but
based
on the actual code that I'm trying to run.
--
Andrew S. Townley <ast@atownley.org>
http://atownley.org

Your initialize method requires at least a "count" parameter, but
you're not passing any. Do you mean to default it to something?

foo = Foo.new(42)

should work.

/Nick

···

On Sun, Apr 26, 2009 at 4:49 PM, Andrew S. Townley <ast@atownley.org> wrote:

Hi,

$ cat $HOME/jbutton.rb
require 'java'

include_class 'javax.swing.JButton'

class Foo < JButton
def initialize(count)
super()
self.text = "Foo"
@count = count
end
end

foo = Foo.new

Going to combine a few messages in one here:

In Ruby, "super()" is different from just "super". The former will
invoke a superclass' constructor with no parameters, while the latter
will call it with the same number of parameters as was in "new" call.

Thanks. That's exactly the behavior I wanted. I wanted to call the empty constructor explicitly.

Your initialize method requires at least a "count" parameter, but
you're not passing any. Do you mean to default it to something?

foo = Foo.new(42)

should work.

Good catch, Nick. Test case error. I was incrementally trying to build
up to the exact scenario that got the same error as my actual code.
Unfortunately, I stopped when I got the error, rather than actually
believing the compiler.

The original code didn't have this problem.

Andrew S. Townley wrote:
> $ cat $HOME/output.txt
> nene$ c:/devel/src/jruby/bin/jruby -v
> jruby 1.3.0 (ruby 1.8.6p287) (2009-04-26 r6586) (Java HotSpot(TM) Client VM 1.6.0_13) [x86-java]
> nene$ c:/devel/src/jruby/bin/jruby jbutton.rb
> jbutton.rb:13: wrong # of arguments(0 for 1) (ArgumentError)

Constructors can be a little fiddly from within a Ruby class that
extends a Java class, due to some complicated gymnastics. If at all
possible you'd be better off aggregating the button, but if you must
extend, extend with the same constructor style and just call "super"

We want to improve how this works, but the extension code needs a
rewrite before that can happen. Contributing broken specs for cases like
this that ought to "just work" would help us too.

Thanks for the info here, Charlie. Unfortunately, I was barking up the
wrong tree due to the message I got from jruby. I haven't been able to
duplicate it in a simple case, but the code that caused the problem was
actually this:

28 class Button < JButton
29 def initialize(*args, &block)
30 super()
31 if args.length > 0 && args[0].is_a?(Action)
32 # action = args[0]
33 self.action_command = action.action_id
34 if action.status_text.nil?
35 self.tool_tip_text = action.label
36 else
37 self.tool_tip_text = action.status_text
38 end
39 self.text = action.label

Originally, I didn't have the reference in line 32 because I'd copied
this code from another method where I was using aggregation instead of
inheritance, which produced the following error:

excalibur$ jruby actiontest.rb
..[path elided]/button_peer.rb:33:in `initialize': wrong # of
arguments(0 for 2) (ArgumentError)
  from actiontest.rb:46:in `initialize'
  from actiontest.rb:58

The call in actiontest.rb looks like this:

45 back = BackAction.new
46 b1 = Button.new(back)

Since I hadn't done the subclassing before, and the error was
complaining about the constructor call, that's where I started looking.
I couldn't figure out why the line number in the original error refers
to the first time the JButton instance was used, however.

Since Nick pointed out that the empty call to JButton() actually works
via 'super()', I went back to ensure that I wasn't making the same
mistake in the original code. This morning, I noticed that I had
inadvertently lost the reference to the named action variable in the
refactoring. After adding line #32, everything works swimmingly.

This is actually the kind of thing I was talking about when I said I was
having to translate JRuby error messages.

Another case was when I was trying to give a derived AbstractAction
sub-class, e.g.:

27 class ToggleAction < AbstractAction
28 def initialize(action)
29 super("Toggle Action State")
30 @action = action
31 end
32
33 def actionPerformed(event)
34 puts "perf"
35 @action.enabled = !@action.enabled
36 end
37 end

Originally, since JRuby does method translation for action_performed to
actionPerformed, I'd implemented the class like this:

27 class ToggleAction < AbstractAction
28 def initialize(action)
29 super("Toggle Action State")
30 @action = action
31 end
32
33 def action_performed(event)
34 puts "perf"
35 @action.enabled = !@action.enabled
36 end
37 end

This gives the usefully obtuse (but totally accurate stack trace):

excalibur$ jruby actiontest.rb
Exception in thread "AWT-EventQueue-0" java.lang.AbstractMethodError: org.jruby.proxy.javax.swing.AbstractAction$Proxy2.actionPerformed(Ljava/awt/event/ActionEvent;)V
  at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995)
  at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318)
  at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:387)
  at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:242)
  at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:236)
  at java.awt.Component.processMouseEvent(Component.java:6134)
  at javax.swing.JComponent.processMouseEvent(JComponent.java:3265)
  at java.awt.Component.processEvent(Component.java:5899)
  at java.awt.Container.processEvent(Container.java:2023)
  at java.awt.Component.dispatchEventImpl(Component.java:4501)
  at java.awt.Container.dispatchEventImpl(Container.java:2081)
  at java.awt.Component.dispatchEvent(Component.java:4331)
  at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4301)
  at java.awt.LightweightDispatcher.processMouseEvent(Container.java:3965)
  at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3895)
  at java.awt.Container.dispatchEventImpl(Container.java:2067)
  at java.awt.Window.dispatchEventImpl(Window.java:2458)
  at java.awt.Component.dispatchEvent(Component.java:4331)
  at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
  at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
  at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
  at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
  at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
  at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
  at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

Admittedly, this one didn't take me as long to figure out the root
cause, but it still wasn't very helpful in identifying the actual cause.
Given that the behavior of "name mapping" isn't 100% consistent (that it
does it during calls, but not during definition), it might actually
cause problems for more people than just me.

I know that JRuby isn't the only implementation that does this (so does
IronRuby), but it might be something to put on the "todo" list to at
least provide some kind of warning or more automatic fixup.

While convenient, and certainly "more ruby-like", I'm not really sure
that doing all of the automagic method translation stuff is actually all
that necessary, and, since it's difficult to do for all cases without
adding even more overhead, maybe might just be simpler to avoid.

I'm sure most people would disagree with me, but really, how much more
of a PITA is it to do 'rubyobj.addActionListener xyzzy' than
'rubyobj.add_action_listener xyzzy'? At least you've some visual clue
that you're calling 'native' code here, and you don't have to worry
about knowing when the interpreter's going to "do what you mean" vs. "do
what you actually said".

Overloaded methods are another kettle of fish... :slight_smile:

Anyway, the problem's solved now thanks to your collective help.
Aggregation isn't an option in this particular case (even though that's
my default approach), because what I need is a UI peer class that does
some translation between an abstract toolkit's concepts of buttons,
actions, etc. and their "native" counterparts. All the peers need to be
'native' so that they can be passed around to the appropriate classes
where necessary in a consistent way vs. being ignored 99% of the time by
the rest of the application code.

Glad it did turn out to be just me, but a more accurate error message
would be nice.

Actually, as I was going to write the rest, I just figured out why I hit
the problem. Action is an accessor of JButton. Since I used the same
name, silent collision, the compiler's happy, but it's obviously still
somewhat confused. Not really sure what message I'd expect, but I still
think the current message is misleading.

At least now I understand what the issue was.

Cheers,

ast

···

On Mon, 2009-04-27 at 13:09 +0900, Gennady Bystritsky wrote:
On Mon, 2009-04-27 at 13:25 +0900, Nick Sieger wrote:
On Mon, 2009-04-27 at 10:29 +0900, Charles Oliver Nutter wrote:
--
Andrew S. Townley <ast@atownley.org>
http://atownley.org

Andrew S. Townley wrote:

Admittedly, this one didn't take me as long to figure out the root
cause, but it still wasn't very helpful in identifying the actual cause.
Given that the behavior of "name mapping" isn't 100% consistent (that it
does it during calls, but not during definition), it might actually
cause problems for more people than just me.

I know that JRuby isn't the only implementation that does this (so does
IronRuby), but it might be something to put on the "todo" list to at
least provide some kind of warning or more automatic fixup.

While convenient, and certainly "more ruby-like", I'm not really sure
that doing all of the automagic method translation stuff is actually all
that necessary, and, since it's difficult to do for all cases without
adding even more overhead, maybe might just be simpler to avoid.

Yes, we did make a modification in JRuby 1.1.5 or so to allow implementing interface methods with underscore names, but that never got into the class-extending code (which is way more complicated). We're really tight on resources to work on this stuff, but we're hoping to do another pass over the summer to complete the already-started rewrite of the whole Java integration layer. This sort of gap will be remedied then as well.

Glad it did turn out to be just me, but a more accurate error message
would be nice.

Actually, as I was going to write the rest, I just figured out why I hit
the problem. Action is an accessor of JButton. Since I used the same
name, silent collision, the compiler's happy, but it's obviously still
somewhat confused. Not really sure what message I'd expect, but I still
think the current message is misleading.

There's actually a bug in our tracker to improve error messages throughout Java integration code, since I know it can be very frustrating. The current code is unfortunately the product of almost 8 years of people poking it this way and that without ever completing a full refactoring. But we're untangling it bit by bit and making improvements with every release. We're also more than willing to help folks like you sort out remaining issues.

And probably the biggest thing you could do to help us would be to contribute simple specs/tests for behaviors you discover and behaviors that aren't working like you expect, so we can continue to build out a set of expectations we need to meet.

- Charlie

Andrew S. Townley wrote:
> While convenient, and certainly "more ruby-like", I'm not really sure
> that doing all of the automagic method translation stuff is actually all
> that necessary, and, since it's difficult to do for all cases without
> adding even more overhead, maybe might just be simpler to avoid.

Yes, we did make a modification in JRuby 1.1.5 or so to allow
implementing interface methods with underscore names, but that never got
into the class-extending code (which is way more complicated). We're
really tight on resources to work on this stuff, but we're hoping to do
another pass over the summer to complete the already-started rewrite of
the whole Java integration layer. This sort of gap will be remedied then
as well.

Sounds good. Hopefully the changing of the guard won't interfere with
this. :slight_smile:

> Glad it did turn out to be just me, but a more accurate error message
> would be nice.
>
> Actually, as I was going to write the rest, I just figured out why I hit
> the problem. Action is an accessor of JButton. Since I used the same
> name, silent collision, the compiler's happy, but it's obviously still
> somewhat confused. Not really sure what message I'd expect, but I still
> think the current message is misleading.

There's actually a bug in our tracker to improve error messages
throughout Java integration code, since I know it can be very
frustrating. The current code is unfortunately the product of almost 8
years of people poking it this way and that without ever completing a
full refactoring. But we're untangling it bit by bit and making
improvements with every release. We're also more than willing to help
folks like you sort out remaining issues.

That's both very obvious and much appreciated. Thanks.

And probably the biggest thing you could do to help us would be to
contribute simple specs/tests for behaviors you discover and behaviors
that aren't working like you expect, so we can continue to build out a
set of expectations we need to meet.

Can you point me to the best documentation on how to do this? I'm happy
to help with the cases I've seen so far.

Cheers,

ast

···

On Tue, 2009-04-28 at 02:44 +0900, Charles Oliver Nutter wrote:
--
Andrew S. Townley <ast@atownley.org>
http://atownley.org

Andrew S. Townley wrote:

And probably the biggest thing you could do to help us would be to contribute simple specs/tests for behaviors you discover and behaviors that aren't working like you expect, so we can continue to build out a set of expectations we need to meet.

Can you point me to the best documentation on how to do this? I'm happy
to help with the cases I've seen so far.

No specific docs, but if you check out JRuby (on github, jruby user's jruby repository), there's a spec/java_integration directory with other examples. It's pretty self-explanatory from there :slight_smile:

- Charlie