Recently about JRuby
You've no doubt heard about JRuby, which lets you run Ruby code on the JVM. This is nice, but wouldn't it be nicer if you could write Java code on a Ruby VM? This would let you take advantage of the power of Ruby 1.9's new YARV (Yet Another Ruby VM) interpreter while letting you write code in a statically-typed language. Without further ado, I'd like to introduce RJava, which does just that!
RJava lets you write code in Java and run it on a Ruby VM! And you still get the full benefit of the Java compiler to ensure your code is 100% correct. Of course with Java you also get checked exceptions and proper interfaces and abstract classes to ensure compliance with your design. You no longer need to worry about whether an object responds to a random message, because the Java compiler will enforce that it does.
You get all this and more but on the power and flexibility of a Ruby VM. And because Java does not support closures, you are ensured that everything is properly designed since you'll be able to define interfaces and then implement anonymous inner classes just like you're used to doing! Even when JDK 8 arrives sometime in the future with lambdas, you can rest assured that they will be statically typed.
As a first example, let's see how you could filter a collection in RJava to find only the even numbers from one to ten. In Ruby you'd probably write something like this:
evens = (1..10).find_all { |n| n % 2 == 0 }
With RJava, you'd write this:
List<Integer> evens = new ArrayList<Integer>();
for (int i = 1; i <= 10; i++) {
if (i % 2 == 0) {
evens.add(i);
}
}
This example shows the benefits of declaring variables with specific types, how you can use interfaces (e.g. List in the example) when declaring variables, and shows how you also get the benefits of Java generics to ensure your collections are always type-safe. Without any doubt you know that "evens" is a List containing Integers and that "i" is an int, so you can sleep soundly knowing your code is correct. You can also see Java's powerful "for" loop at work here, to easily traverse from 1 to 10, inclusive. Finally, you saw how to effectively use Java's braces to organize code to clearly show blocks, and semi-colons ensure you always know where lines terminate.
I've just released RJava on GitHub, so go check it out. Please download RJava today and give it a try and let me know what you think!
First... The implementation
This is assuming that you are running JRuby and using a tool like warbler to deploy to a Java server like Tomcat.config/initializers/session_store.rb
# check if we're running in a java container
if defined?($servlet_context)
require 'action_controller/session/java_servlet_store'
# tell rails to use the java container's session store
ActionController::Base.session_store = :java_servlet_store
else
# other session store for development in webrick/mongrel
# setup cookie based session
ActionController::Base.session = {
:key => '_dd_session',
:secret => '<Your really long random secret key>'
}
end
This will store your rails session in Tomcat's session store exactly as if you were running a Java web framework.Second... Why?
There are a few common solutions starting with the rails default.Cookie Store: This is not very secure, limited in size, and sends a lot of data back and forth to the client. It is strongly recommended to not use this for production systems.
File Store: Stores sessions on the file system. Every cookie access involves file system IO. Does not support clustering by default but can be achieved through a SAN or shared storage.
ActiveRecord: Stores sessions in the database. Slow due to database IO. Natively supports clustering.
Memcached: Stores sessions in Memcached instance. Very popular and works well with clusters. Can run into RAM issues on systems with lots of users. Requires an instance of Memcached running.
Java Servlet Store: Works just like your other Java applications. Java Servers can be configured to cluster sessions. Uses the JSESSIONID cookie or url param (in Tomcat)
Conclusion
This won't be the solution for everyone, but if you're deploying a JRuby on Rails app into an infrastructure based on Java, then it provides a simple and efficient solution."We're missing some coverage..."
While creating coverage reports for a fairly new JRuby on Rails project, we noticed that our coverage numbers weren't quite right: certain classes were missing from the coverage reports. Rcov doesn't know about classes unless they are required: not a problem for models, but we were missing tests for some controllers and libraries.
Oops.
To properly correct this problem, I wrote the coverage_helper (to live alongside the test_helper). Basically, this causes all of the classes to be required so that rcov knows about them.
test\coverage_helper.rb
require 'test/unit'
require 'test_helper'
class CoverageHelper < Test::Unit::TestCase
def test_coverage
['app', 'lib'].each {|path| Dir.glob("#{path}/**/*.rb") {|file|
require File.expand_path(file.chomp('.rb'))
}}
assert true
end
end
Simply include this test in your rcov builds and the problem is solved.
Because of how rcov counts lines and the way Ruby class loading works, you'll never see files with 0% coverage. However, at least you will see all of your classes listed and those that aren't covered will have a low percentage.
Is this really necessary?
First, the File.expand_path makes sure that your files are only required once. I hate random warning messages because constants are initialized twice (among other issues).
Second, no, I didn't need to make this a test, but it just seemed nicer to. I added the assert true simply because I didn't feel right about not asserting something in the test.
Third, as long as one uses the Rails scripts to generate the skeletons for your code, this scenario should never happen (because Rails will create all of the appropriate tests). However, there is the tendency not to use the generated scripts when they don't output what you want, which is what we have discovered (Rails and Legacy Database Schemas aren't a perfect fit). Also, sometimes I just forget to use them.
What if you can't run rcov?
One minor glitch of running JRuby on Windows is that the File.separator is technically incorrect (it's '/' instead of '\'). This usually isn't an issue... except when using rcov. Since rcov executes from the shell, the arguments requiring file names and/or directories won't work because the separator is the wrong direction from what Windows is expecting.
The fix is to add a couple of methods to the File class to address the problem.
Windows Separator Fix
class File
@@is_windows = ENV['OS'] &&
(not ENV['OS'].downcase.match(/^windows/).nil?)
def self.fix_name(name)
@@is_windows ? name.tr(File::SEPARATOR, File::ALT_SEPARATOR) : name
end
def self.fixed_join(*files)
self.fix_name(self.join files)
end
end
The reason to do the ENV['OS'] truth check first is that in JRuby on Solaris (where our CI is), that property doesn't exist. We couldn't use the RUBY_PLATFORM variable either, as in JRuby it's always assigned to 'java'.
I should note that I've only use these fixed separator methods when necessary (in my rcov rake task). The 'normal' separator has worked in every other situation I've run into.
I ran into a situation the other day with Groovy that baffled me at first. Let's create a range from 0.0 to 10.0 and then use it to check if a certain number is contained within that range, like this:
groovy:000> r = 0.0..10.0 ===> 0.0..10.0 groovy:000> r.contains(5.6) ===> false
WTF? The number 5.6 is not contained in the range 0.0 to 10.0? I beg to differ. So what's actually going on here? Using the shell we can do some more digging, interrogating the range object to see what its bounds are, what values it contains if you iterate it, and so on:
groovy:000> r = 0.0..10.0
===> 0.0..10.0
groovy:000> r.class
===> class groovy.lang.ObjectRange
groovy:000> r.from.class
===> class java.math.BigDecimal
groovy:000> r.to.class
===> class java.math.BigDecimal
groovy:000> r.each { print " $it " }
0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 ===> 0.0..10.0
groovy:000> r.contains 5
===> true
groovy:000> r.contains 5.0
===> true
groovy:000> r.contains 5.6
===> false
So what did we learn? First, the range is an ObjectRange whose bounds are BigDecimals. Second, that iterating by default iterates in increments of one. And third that the contains method does not quite do what I would expect by default. Looking at Groovy's ObjectRange class makes it clear exactly what's going on, so let's look:
public boolean contains(Object value) {
Iterator it = iterator();
if (value == null) return false;
while (it.hasNext()) {
try {
if (DefaultTypeTransformation.compareEqual(value, it.next())) return true;
} catch (ClassCastException e) {
return false;
}
}
return false;
}
The Groovy ObjectRange's contains method defines containment as whether a value is contained within the values generated by its iterator. By now many of my many millions of readers are about to post a comment telling me the problem, so I'll preempt that temptation by adding a few more lines of interaction with the Groovy shell:
groovy:000> r.containsWithinBounds 5.0 ===> true groovy:000> r.containsWithinBounds 5.6 ===> true
Aha! So contains doesn't do what you might think it should, but containsWithinBounds does. Its JavaDoc says "Checks whether a value is between the from and to values of a Range." Conspicuously there is no JavaDoc on the contains method to tell me that what it actually does is check whether a value is contained within the discrete values generated by the iterator. Let's try more more thing:
groovy:000> r.containsWithinBounds 5
ERROR java.lang.ClassCastException: java.lang.Integer
at groovysh_evaluate.run (groovysh_evaluate:2)
...
Oops! Not only do you need to call containsWithinBounds rather than contains, you also need to call it with the correct type, as there is no coercion going on since it uses Comparable.compareTo() under the covers.
Notwithstanding all the recent activity regarding all the Ruby security flaws recently discovered, how does Ruby handle inclusion of a number within a range? Here's some irb output:
>> r = 0.0..10.0
=> 0.0..10.0
>> r.class
=> Range
>> r.begin.class
=> Float
>> r.end.class
=> Float
>> r.each { |item| print " #{item} " }
TypeError: can't iterate from Float
from (irb):53:in `each'
from (irb):53
>> r.include? 5
=> true
>> r.include? 5.0
=> true
>> r.include? 5.6
=> true
To me this is more semantically and intuitively correct behavior. First, while I can create a range with float bounds, I cannot iterate that range - for non-integral numbers, how exactly can you define the next item after 0.0 for example? 0.1, 0.01, 0.001, and so on till infinity. Second, the include? method behaves as I would expect no matter what type of argument I pass. I am able to iterate ranges of integral numbers, however, which could arguably also be confusing since the behavior of the method depends on the type. Then again, that's pretty much what polymorphic method behavior is about I suppose.
>> r = 0..10
=> 0..10
>> r.each { |item| print " #{item} " }
0 1 2 3 4 5 6 7 8 9 10 => 0..10
In the case of integers Ruby uses an increment of one by default. You could use the step method to get a different increment, e.g.:
>> r.step(2) { |item| print " #{item} " }
0 2 4 6 8 10 => 0..10
So what's the point of all this? That Ruby is better than Groovy? Nope. I really like both languages. I think there are a couple of points that were reinforced to me:
First, RTFM (or source code --> RTFC). Even though Groovy's contains method doesn't behave how I think it should, there is the method I was looking for with containsWithinBounds.
Second, having a shell to play around with short snippets of code is really, really useful, without needing to create a class with a main method just to play around with code.
Third, documentation and semantics matter. If something doesn't feel intuitively correct based on how similar things act, it is more likely to cause confusion and errors. In this case, since my unit test immediately caught the error, I was able to figure the problem out in a few minutes.
Finally, following on from the last point, unit tests continue to remain valuable. Of course anyone who knows me would roll their eyes over my anal-ness (which Mac's dictionary is telling me is not really a word but I don't care at the moment) expecting me to get something about unit testing in somehow.
package pkg;
import org.jruby.Main;
public class JRubyRunner {
public static void main(String[] args) throws Exception {
runJRubyScript("ipc.rb", args);
}
public static void runJRubyScript(String name, String... args) {
// modify the args for jruby
String[] args2 = new String[2 + args.length];
if (args.length > 0) {
System.arraycopy(args, 0, args2, 2, args.length);
}
args2[0] = "-e";
args2[1] = "require '" + name + "'";
// execute the ruby script
Main.main(args2);
}
}
Now, for a few subtle points that you may have missed, especially considering the simplicity of the code. For starters, why you may ask, not just pass in the name of the ruby file? Why use the -e option at all? Well, the reason is because the ruby file is located in the jar file so it is not accessible via java.io.File which JRuby uses to load the file. This makes perfect sense. And since it defeats the purpose to move the ruby file outside of the signed jar, we must find another way to get this to work. You could read in the entire contents of the file from the classpath and pass it into the -e option, but this is ugly and extremely prone to error, so we take advantage of an excellent feature of 'require' in JRuby. Require loads its files from the classpath. This is excellent, because it means that if we require just the file we want to run, it will be loaded from the classpath and and subsequent requires will be loaded from the classpath as well.
