Skip to content

Method Signatures and Annotations in JRuby extensions

Martin Prout edited this page Mar 7, 2016 · 26 revisions

Or how to create ruby methods using java see jruby-examples

Class method signatures

# in ruby

def active?
end
// java, note active_p follows the naming convention for a query

@JRubyMethod(name = "active?")
public IRubyObject active_p(ThreadContext context) {
}
# in ruby we might have vector normalize

def normalize
end
// java, norm we will return a normalized copy leaving original unchanged

@JRubyMethod(name = "normalize")
public IRubyObject norm(ThreadContext context) {
}
# in ruby we might have vector normalize, where we change the original

def normalize!
end
// in java, the normalize_bang follows the naming convention for a hard change
// eg in vector normalize the original is changed

@JRubyMethod(name = "normalize!")
public IRubyObject norm_bang(ThreadContext context) {
}

rest arguments

# ruby

def initialize(*args)
end
// java

@JRubyMethod(name = "initialize", rest = true)
public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
}

arguments with default value

# ruby

def get(key, marshal=true)
end
// java (the default value for required and optional is 0)

@JRubyMethod(name = "get", required = 1, optional = 1)
public IRubyObject get(ThreadContext context, IRubyObject[] args) {
    Ruby ruby = context.getRuntime();
    RubyString key = (RubyString) args[0];
    RubyBoolean marshal = ruby.getTrue();
    if (args.length > 1) {
        marshal = args[1];
    }
}

initialize or new

# ruby

attr_reader :x, :y

def initialize(*args)
  if args.length == 2
    @x = args[0]
    @y = args[1]
  else
    @x = 0
    @y = 0
  end
end

Note the new method should be a class method, so it is a static method in Java. Also it is meta method that should be defined on the metaclass (hence meta = true in the @JRubyMethod annotation). The convention is to name the java method rbNew, this is perhaps one the trickiest methods to implement correctly, where you will most likely create some helper methods as we have below.

// java where meta = true mean this method should be defined on the metaclass

/**
 *
 * @param context ThreadContext
 * @param klazz IRubyObject
 * @param args optional (no args jx = 0, jy = 0)
 * @return new Vec2 object (ruby)
 */
@JRubyMethod(name = "new", meta = true, rest = true)
public static final IRubyObject rbNew(ThreadContext context, IRubyObject klazz, IRubyObject[] args) {
    Vec2 vec2 = (Vec2) ((RubyClass) klazz).allocate();
    vec2.init(context, args);
    return vec2;
}

/**
 *
 * @param runtime Ruby
 * @param klass RubyClass
 */
public Vec2(Ruby runtime, RubyClass klass) {
    super(runtime, klass);
}

void init(ThreadContext context, IRubyObject[] args) {
    if (Arity.checkArgumentCount(context.getRuntime(), args, Arity.OPTIONAL.getValue(), 2) == 2) {
        jx = (Double) args[0].toJava(Double.class);
        jy = (Double) args[1].toJava(Double.class);
    }
}

Module method signatures

module Foo 
  def build_string
     return 'This is a new String' 
     end alias_method :build_string :new_string
  end
end

Note in java the module method is a static method, and requires a receiver, also illustrated below is how to create an alias (there is an alias option in jruby annotations but it is not required just name the method as below

/**
*
* @param context ThreadContext
* @param recv the receiver
* @return A RubyString.
*/
@JRubyMethod(name = {"build_string", "new_string"}, module = true)
public static IRubyObject buildString(ThreadContext context, IRubyObject recv) {
    Ruby runtime = context.getRuntime();
    return runtime.newString("This is a new String");
}
Clone this wiki locally