Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

java_import does not define top-level global when called from class method #975

Closed
jrochkind opened this issue Aug 27, 2013 · 10 comments
Closed

Comments

@jrochkind
Copy link

jruby 1.7.4 (1.9.3p392) 2013-05-16 2390d3b on Java HotSpot(TM) 64-Bit Server VM 1.6.0_51-b11-457-11M4509 [darwin-x86_64]

From the wiki,

java_import java.lang.System
...
After this point, the "System" constant will be available in the global name space (i.e. available to any script).

However, this does not work when calling java_import from inside a class method.

class Util
  def self.import_stuff
     java_import java.lang.System
  end
end

Util.import_stuff

System   #=> NameError: uninitialized constant System
::System #=> NameError: uninitialized constant System 
Util::System # There it is! => Java::JavaLang::System

# It does _normally_ create top-level globals....
java_import java.lang.System
::System # => Java::JavaLang::System

Is this a bug? Or is it somehow as designed? It would be convenient to be able to import java classes to top-level methods in utility methods like that. Oddly, it does seem to work if you do the java_import in an actual ordinary instance method, just not a class method.

I suppose I could do my own stuff to somewhat confusingly dynamically set
the top level ::System constant to Util::System as a workaround, but it gets to be kind of confusing.

@enebo
Copy link
Member

enebo commented Aug 27, 2013

java_import imports the constant into the current namespace. In this case it will add System to Util not to the calling context. To do what you want you could do something like:

class Util
  def self.import_stuff(context)
    context.instance_eval do
      java_import java.lang.System
    end
  end
end

Util.import_stuff(self)

p System

So it is working as designed even if the side-effect of where the constant is defined may not seem so intuitive.

@enebo enebo closed this as completed Aug 27, 2013
@jrochkind
Copy link
Author

Is this documented anywhere?

The only documentation I find for java_import is the one I quoted before in the wiki, which specifically says "global name space", not "current namespace". Should it be changed?

I'm not sure I understand what 'current namespace' even means with regard to this, though.

module MyPackage ; end

class MyPackage::Util
    def import_stuff
         java_import java.lang.System
    end
end

MyPackage::Util.new.import_stuff

::System # => Java::JavaLang::System

Why is the 'current namespace' in that instance the top-level namespace (rather than ::MyPackage or MyPackage::Util), but the 'current namespace' is different when it's a class method instead of an instance method? Why does the 'current namespace' change when inside a class method, but not when inside an instance method? How do I understand what the 'current namespace' is for any given context?

@enebo
Copy link
Member

enebo commented Aug 27, 2013

Actually, I had to look at the source to actually believe this. If we are in a module we class_eval it so that it behave the way I described where in my parlance namespace == module/class it lives in. I actually thought if self was not a module we would define it with self's class. Unfortunately, it is not this straightforward.

When self is not a module/class we will call a straight eval with a call to binding. This ends up getting you the behavior you showed in your last comment. I am pretty perplexed on why we do this. I have no doubt it was done for a reason but since I was not even aware of it we need to figure out why.

As for documentation, I don't even see a reference to this in our book so I think I will reopen this issue at least until we can properly document the behavior. I might even prefer to change the behavior itself, but I would still like to understand the reason for it.

@enebo enebo reopened this Aug 27, 2013
@jrochkind
Copy link
Author

Thanks for reopening! It does seem like a bug or undesirable behavior to me, but I understand your point that there may be a reason for it we are not currently aware of.

@jrochkind
Copy link
Author

Although if you're saying you want to change things to define in top-level namespace less frequently -- it might be backwards-breaking.

Either way, what I think is really needed is more documentation on exactly how java_import is intended to work, including where it defines it's constants. Which would suggest the rules for where it defines it's constants should ideally be straightforward enough to document comprehensibly with a simple description! (I still don't entirely understand current behavior myself).

When you say 'our book' -- is there jruby documentation somewhere I have not been aware of, what is this book?

@enebo
Copy link
Member

enebo commented Aug 27, 2013

I agree we should document it which is why I re-opened this issue. The book I am referring to is "Using JRuby" published by Pragmatic Programmers. Since it does not appear to be documented online I was hoping we had covered this in our book. Unfortunately, not.

@ghost ghost assigned enebo Aug 30, 2013
@headius
Copy link
Member

headius commented Sep 1, 2013

The difference in behavior was so that both java_import within instance methods and java_import within class bodies would define the constant on the class.

For example...

java_import org.foo.Blah # toplevel, not a class, normal eval

class Quux
  java_import org.foo.Blah # class body, class_eval

  def Quux.some_class_method
    java_import org.foo.Blah # self still === Class, class_eval
  end

  def some_instance_method
    java_import org.foo.Blah # self !=== Class, normal eval
  end
end

I'm open to suggestions on a better-and-not-too-complicate heuristic. I think this is simply a documentation issue, though.

@jrochkind
Copy link
Author

Ah, wanting to define the constant within a certain class, when you do a java_import within a certain class, does make a certain kind of sense.

both java_import within instance methods and java_import within class bodies would define the constant on the class.

Right now, if you do a java_import within an instance method, you get a global constant, not a constant namespaced within any class. I am not quite sure if that's intended or not, esp from above quote.

But anyway either way, backwards incompat changes prob don't make any sense at this point, but docs would be very welcome.

The docs on java_import and (deprecated?) java_include or include on java classes, and java-related uses of require, and all that stuff -- have been a bit hard to track down for me. The user-editable wiki page at have been the only 'official' docs I've been able to find, and they're a bit spotty and example-oriented. Googling, you find historical explanations from different points at jruby history that may or may not match current behavior or recommendations.

@rtyler
Copy link

rtyler commented Aug 7, 2015

FWIW this confusing behavior still applies to JRuby 9k

[1] pry(main)> module MyPackage ; end
=> nil
[2] pry(main)> 
[3] pry(main)> class MyPackage::Util
[3] pry(main)*   def import_stuff    
[3] pry(main)*     java_import java.lang.System         
[3] pry(main)*   end      
[3] pry(main)* end  
=> :import_stuff
[4] pry(main)> 
[5] pry(main)> MyPackage::Util.new.import_stuff
=> [Java::JavaLang::System]
[6] pry(main)> ::System
=> Java::JavaLang::System
[7] pry(main)> System
=> Java::JavaLang::System
[1] pry(main)> class Util
[1] pry(main)*   def self.import_stuff  
[1] pry(main)*     java_import java.lang.System     
[1] pry(main)*   end    
[1] pry(main)* end  
=> :import_stuff
[2] pry(main)> 
[3] pry(main)> Util.import_stuff
=> [Java::JavaLang::System]
[4] pry(main)> 
[5] pry(main)> System
NameError: uninitialized constant System
from org/jruby/RubyModule.java:3144:in `const_missing'
[6] pry(main)> 

@enebo
Copy link
Member

enebo commented Feb 17, 2017

wiki had been edited since this was opened and I added an additional paragraph about using java_import from within methods (and also recommending people do not do it).

@enebo enebo closed this as completed Feb 17, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants