This version of the manual refers to an earlier version of the software.

Delegate Script

The delegate script mechanism enables the use of custom Ruby methods as "hooks" to provide dynamic information back to the image server. A truly customized image server can be created with minimal code.

Delegate methods are invoked by a JRuby interpreter bundled into the image server. There is no need to install an external Ruby environment and no need to know Java or any of the image server's internal API; just type some code into a file, and go.

Prior to version 3.2, the JRuby interpreter was compatible with Ruby 2.2. Since version 3.2, it has been compatible with version 2.3.

The delegate script mechanism is disabled by default. To enable it, follow these steps:

  1. Copy the sample delegate script, delegates.rb.sample, included in the distribution, to delegates.rb.
  2. Reference it from the delegate_script.pathname configuration option.
  3. Set delegate_script.enabled to true.

Rules

While the arguments and return types of each method will vary, all delegate methods must be contained within a Cantaloupe module. Inside a method, anything goes, and you can use any (non-platform-native) gems that you have installed with gem install.

Starting in version 3.3, the delegate script is reloaded whenever the script file changes. (Previously, it was reloaded on each request.) Be aware, though, that code that has already been loaded into the JRuby runtime cannot be unloaded. For example, when a method is changed, the new version will overwrite the old version; but constants cannot be redefined.

Because delegate methods will be called frequently, they should be written with efficiency in mind.

Note: generally, neither method arguments nor return values are sanitized or validated. Be very careful to write defensive, injection-aware code.

Gems

JRuby can load most Ruby gems, except those that have been built with native extensions. To import a gem, use the require statement:

require 'mygem'

require searches for gems based on the $GEM_PATH environment variable, falling back to $GEM_HOME if that is not defined. If JRuby fails to find your gem, check your $GEM_PATH. If you installed the gem using gem install, check the output of gem env (particularly the "gem paths" section) to see where it might have been installed, and ensure that those locations are present in $GEM_PATH.

Caching

Since version 3.3, the delegate_script.cache.enabled option is available to cache the results of delegate method invocations. The cache is an in-memory least-recently-used (LRU) cache with infinite time-to-live and a maximum size auto-computed based on the maximum JVM heap size. When the limit is approached, the oldest invocations will be purged automatically.

Because the cache is in memory and not persisted, it will be cleared when the application is stopped.

Note that cached invocations are not purged when the script file is edited and auto-reloaded.


Example

Here is an example of a script used by FilesystemResolver that performs a Solr query to return a pathname based on an identifier. The documentation in that section describes the contract that this method must abide by: its name, arguments, and return value.

require 'cgi'
require 'net/http'

module Cantaloupe

  module FilesystemResolver
    ##
    # Used by FilesystemResolver's ScriptLookupStrategy.
    #
    # @param identifier [String] Image identifier
    # @return [String,nil] Absolute pathname of the image corresponding to the
    #                      given identifier, or nil if not found.
    #
    def self.get_pathname(identifier)
      uri = 'http://localhost:8983/solr/collection1/select?q=' +
          CGI.escape('id:"' + identifier + '"') +
          '&fl=pathname_si&wt=ruby'
      uri = URI.parse(uri)

      http = Net::HTTP.new(uri.host, uri.port)
      request = Net::HTTP::Get.new(uri.request_uri)
      response = http.request(request)
      return nil if response.code.to_i >= 400

      results = eval(response.body)['response']['docs']
      results.any? ? results.first['pathname_si'] : nil
    end
  end

end

Testing Script Methods

Using the example above, get_pathname() could be tested by adding the following line to the end of the script:

puts Cantaloupe::FilesystemResolver::get_pathname('identifier-to-test')
And running it on the command line with a command like: ruby delegates.rb. The method output will appear in the console.

A catch, however, is that the ruby command will normally invoke the standard ("MRI") Ruby interpreter, and not the JRuby interpreter. While they mostly work the same, one thing to be aware of is that gems with platform-native extensions will not work in JRuby. For that reason, you might want to install a standalone JRuby interpreter and test with that instead. (Something like RVM can make it easier to switch between different versions of the Ruby interpreter.)