The jenkins-plugin-runtime.rb module defines glue code that bridges extension points defined in Jenkins to Ruby code. Because new extension points are added in the core all the time, this glue layer is always expected to somwhat lag behind in terms of the coverage.

Understanding the glue layer

A typical extension point, defined in the core and other Java plugins, consists of 2 Java classes:

Given those two types, the glue layer consists of 3 Ruby classes:

  • Jenkins::Plugin::Proxies::FooDescriptor (JPP::FooDescriptor for short), which extends the Java class FooDescriptor. This class overrides various methods of the Java Descriptor class to make it work with Ruby implementations.
  • Jenkins::Plugin::Proxies::Foo (JPP::Foo for short), which extends the Java class Foo. As an implementation of the contract Foo, this represents the Ruby implementation of the extension point to the rest of the Jenkins code. The methods defined in Foo should be overrided here and delegated to JM::Foo class, with necessary argument/return value massaging. This is the heart of the glue layer.
  • Jenkins::Model::Foo (JM::Foo for short — although they can be in other packages that mimics the Java package name, such as Jenkins::Task). This class re-implements the contract defined in Foo but in a native Ruby fashion. When you actually implement an extension point in Ruby, this is the class you'll be extending from.

Defining a glue layer for an extension point

Given the terminology above, you need to define JPP::FooDescriptor, JPP::Foo, and JM::Foo to expand the glue layer to cover a new extension point.

The following code shows how you define JPP::FooDescriptor. If FooDescriptor defines additional methods, you'll need to implement them, typically by delegating to the class methods of @impl, which is the Ruby Class object that represents the actual subtype of JM::Foo.

module Jenkins
  class Plugin
    class Proxies
      class FooDescriptor < Java.hudson.matrix.FooDescriptor
        # the following module overrides various methods in Java.hudson.model.Descriptor
        # to make it work with Ruby. You must include this.
        include Jenkins::Model::RubyDescriptor
        
        ...
      end

The following code shows how you define JPP:Foo.

module Jenkins
  class Plugin
    class Proxies
      class Foo < Java.hudson.matrix.Foo
        # the following 2 modules implement the Describable interface
        # You must include this.
        include Jenkins::Plugin::Proxies::Describable
        include Java.jenkins.ruby.Get

        # this module makes this class act as a proxy
        # You must include this, too
        include Jenkins::Plugin::Proxy

        # when the glue layer needs to create a wrapper, it calls this constructor
        # plugin refers to the Ruby plugin object, and the 'object' parameter
        # refers to JM::Foo that it's wrapping.
        def initialize(plugin, object)
          super(plugin, object, ... ) # '...' portion is for the constructor arguments to Foo
        end

        # define methods of Foo and delegate them to @object
        # with whatever argument/return value massaging as needed.
        def doSomething(a,b,c)
          @object.do_something(massage(a),b,c)
      end

The following code shows how you define JM::Foo. This is a plain

class Foo
  # these two modules define necessary class methods.
  # you must include them
  include Jenkins::Model
  include Jenkins::Model::Describable

  # specify the Java type that this Ruby class is mimicking
  describe_as Java.hudson.matrix.Foo
  # specify the Ruby Descriptor subtype for this
  descriptor_is Jenkins::Plugin::Proxies::FooDescriptor

  # mimic contract methods of the Java Foo class here, but in a nicer fashion
  def do_something(a,b,c)
  end

  # associate the Ruby proxy with this
  Jenkins::Plugin::Proxies::register self, Jenkins::Plugin::Proxies::Foo
end