Where to put boilerplate code used in templates

The bare minimum that can be done at this point would be to do something similar to https://listsprd.osu.edu/pipermail/ood-users/2018-November/000297.html and reproduced here:

In /etc/ood/config/apps/dashboard/initializers/qlist.rb

module Purdue
  class Qlist
    def self.qlist(cluster)
      @qlist ||= Hash.new do |h, key|
        Rails.logger.warn "executing expensive qlist"
        h[key] = build_qlist(key)
      end

      @qlist[cluster]
    end

    def self.build_qlist(cluster)
      ["#{cluster}-debug", "#{cluster}-serial", "#{cluster}-parallel" ]
    end
  end
end

# call to build cache on initialize (optional)
Purdue::Qlist.qlist('owens')
Purdue::Qlist.qlist('ruby')
Purdue::Qlist.qlist('pitzer')
Purdue::Qlist.qlist('oakley')

In any interactive app web form where ERB is rendered by the Dashboard app you can then do for example:

attributes:
  queue:
    widget: select
    label: "Queue"
    options:
    <% Purdue::Qlist.qlist('owens').each do |queue| %>
      - [ "<%= queue %>",    "<%= queue %>"   ]
    <% end %>

If you are not memo-izing values or have an inexpensive method you could use it without class methods, for example in /etc/ood/config/apps/dashboard/initializers/custom_code.rb:

module OhioSupercomputerCenter
  class Helper
    def num_cores
      {
        widget: "number_field",
        label: "Number of cores",
        value: 1,
        help: "Number of cores on node type (4 GB per core unless requesting whole node). Leave blank if requesting full node.",
        min: 1,
        max: 28,
        step: 1,     
      }.to_json
    end
  end
end

And then in an erb file:

attributes:
  num_cores: <%= OhioSupercomputerCenter::Helper.new.num_cores %>
  jupyterlab_switch:
    widget: "check_box"

For that it might be better to have the helper return a hash and convert it to json string in the erb:

module OhioSupercomputerCenter
  class Helper
    def num_cores
      {
        widget: "number_field",
        label: "Number of cores",
        value: 1,
        help: "Number of cores on node type (4 GB per core unless requesting whole node). Leave blank if requesting full node.",
        min: 1,
        max: 28,
        step: 1,     
      }
    end
  end
end
attributes:
  num_cores: <%= OhioSupercomputerCenter::Helper.new.num_cores.to_json %>
  jupyterlab_switch:
    widget: "check_box"

Then you could replace one or more values of the hash to customize for a particular app like in the case where one app you want the user to be able to configure up to 40 cores:

attributes:
  num_cores: <%= OhioSupercomputerCenter::Helper.new.num_cores.merge("max" => 40).to_json %>
  jupyterlab_switch:
    widget: "check_box"

So there are some ideas there. The important thing is to wrap whatever code you add in an initializer in a module that is a unique name, such as your institution, so that it never conflicts with any code developed upstream in the future.

Is there a doc or guide for this I am missing when I search?

Not yet unfortunately. I opened an issue to add it: Document trick to sharing code between interactive apps · Issue #250 · OSC/ood-documentation · GitHub. If there are examples of specific things you would like to see documented, like how to use a trick like this to remove some type of duplication, let me know.

1 Like