Forum: Ruby JRuby constructor issue or just me?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
324ce80d9dbe9417607192038fb880bf?d=identicon&s=25 Andrew S. Townley (Guest)
on 2009-04-26 23:50
(Received via mailing list)
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.
Ede2aa10c6462f1d825143879be59e38?d=identicon&s=25 Charles Oliver Nutter (Guest)
on 2009-04-27 03:30
(Received via mailing list)
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
397f61cc4458e13157b4facf72325e5f?d=identicon&s=25 Gennady Bystritsky (Guest)
on 2009-04-27 06:10
(Received via mailing list)
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.
526d60de6472502bb570a9df2842b33b?d=identicon&s=25 Nick Sieger (Guest)
on 2009-04-27 06:26
(Received via mailing list)
On Sun, Apr 26, 2009 at 4:49 PM, Andrew S. Townley <ast@atownley.org>
wrote:
>    super()
>    self.text = "Foo"
>    @count = count
>  end
> end
>
> foo = Foo.new

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
324ce80d9dbe9417607192038fb880bf?d=identicon&s=25 Andrew S. Townley (Guest)
on 2009-04-27 12:57
(Received via mailing list)
Going to combine a few messages in one here:

On Mon, 2009-04-27 at 13:09 +0900, Gennady Bystritsky wrote:
> 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.

On Mon, 2009-04-27 at 13:25 +0900, Nick Sieger wrote:
> 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.

On Mon, 2009-04-27 at 10:29 +0900, Charles Oliver Nutter wrote:
> 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... :)

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
Ede2aa10c6462f1d825143879be59e38?d=identicon&s=25 Charles Oliver Nutter (Guest)
on 2009-04-27 19:45
(Received via mailing list)
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.

> 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
324ce80d9dbe9417607192038fb880bf?d=identicon&s=25 Andrew S. Townley (Guest)
on 2009-04-29 13:34
(Received via mailing list)
On Tue, 2009-04-28 at 02:44 +0900, Charles Oliver Nutter wrote:
> 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. :)

> 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
Ede2aa10c6462f1d825143879be59e38?d=identicon&s=25 Charles Oliver Nutter (Guest)
on 2009-04-29 14:10
(Received via mailing list)
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 :)

- Charlie
This topic is locked and can not be replied to.