-
-
Notifications
You must be signed in to change notification settings - Fork 925
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 can override constants used in dependencies, causing hard-to-debug errors #3737
Comments
java_import setting a constant in the current namespace in and of itself is maybe confusing but it is not really all that much different than frameworks like Rails which define constants in namespaces for you as a side-effect. In fact, java_import is implemented as a Ruby method. I hope these two examples will show why this is not really something we can provide extra warnings for and how it is really just a Ruby pitfall. Let's say we require set and we java_import java.util.Set. Both define Set in ::Object: require 'java'
require 'set';
java_import 'java.util.Set' When we run this we see:
So Ruby is working here and showing that you are trying to overwrite an existing constant. However, let's now consider we generally work within classes or modules? So we do something like: require 'set'
module Foo
java_import 'java.util.Set'
end In this case we get no warning. If you call Set inside Foo it finds Java and outside Foo it finds Ruby's set. This is really identical to: require 'set'
module Foo
Set = 1
end If you would like some more clarity or perhaps you have some suggestion in how we can make this better then please comment and potentially reopen. |
@enebo thanks for the rapid, thorough, and cogent response, I appreciate it! I still think that the JRuby project could help out Ruby newbies by updating the docs section Using a Java Class Without The Full Path Name to mention explicitly that Thanks! |
@aviflax One thing which confuses me about what you are saying is when you say 'the global context'. Are you saying it is more confusing that you can redefine a constant in Ruby in top-level like in a one file script than in a nested scope like within a module? Regardless of that question, I do find that section to be pretty lacking. It says in that example that it is put in 'global name space' but it is just that particular example and I do not think global name space is a good description. I think top-level since it implies a topology (and really we are putting it on the type of the top-level object which happens to be ::Object). I tell you what. I will make a quick edit to that section and I can ask you whether it is more clear or not. I fear I am too well versed in this to objectively judge whether it is easy to understand. |
@aviflax ok made a pass over it. I think I covered the two problems people could run into and I hope explained better how java_import works. In addition, I removed mention to 'import' to even further move us from ever mentioning it. Let me know if anything should change or even change it yourself. Thanks for bringing this up. I think the documentation got a little better as a result at least. |
@enebo thanks, that's a huge improvement! Great work. I can think of only 2 possible tweaks:
Thanks again! |
I did one more round on this. I feel going any further on warning against this would be turning into a how Ruby works section. So I merely included a nice article on the complexities of Constant resolution. You recommendation is a good one of course in anything which is not a single file script you should probably not be defining constants in the top-scope past namespacing gems. Of course this is true of all constants in Ruby, but I think it is easy to think of including a Java class as something special so pointing it out is good. |
@enebo great work, thanks so much! |
I’m new to Ruby and JRuby, so please forgive any errors.
I’m working on a JRuby project with a co-worker, and we encountered some very strange behavior in our app. It took him awhile to track it down, but eventually he found that we had inadvertently used
java_import
to redefine the Ruby constantSet
and thereby broken a dependency of a dependency.Just to be clear:
app.rb
calledrequire 'faye/websocket'
and thenjava_import java.util.Set
app.rb
called some code infaye/websocket
that in turn calledSet.new
inheaders.rb
which returned a new instance ofjava.util.Set
Set
from the built-in moduleset
that ships with the Ruby standard libraryadd?
to be called on the value returned in step 2.java.util.Set
has noadd?
method, an exception was raised.Once we figured this out, we were quite surprised. We had had no idea that
java_import
would redefineSet
globally, even in modules and packages that had already been loaded. Now that I’ve had some time to think it over I think I can see why this would be the case in Ruby, wherein most things by default seem to be in the global scope. But my coworker and I are newbies to Ruby, and I suspect we were mentally mappingjava_import
to howimport
works in Java — now clearly a mistake.OK, so on to my point: given this behavior,
java_import
seems really dangerous. It redefines any existing constant in the global scope, and given modern software development practices wherein people regularly require many levels of dependencies and can’t be expected to be familiar with all the symbols used by all those dependencies, it seems quite easy for someone to inadvertently break a dependency, like we did. And it took us a non-neglibile amount of time to debug this situation.So:
java_import
so that it works more likeimport
in Java, merely acting as a shorthand for a symbol, and only in the current file?java_import
by adding a strong warning about this problem and maybe even advising users to avoidjava_import
.Other than this, JRuby is working great so far in our project — thank you for such an impressive system!
The text was updated successfully, but these errors were encountered: