Ruby objects into JavaScript

Hello,

this is for the gurus that have been using JavaScript in the OOD interactive apps, in the form.js. We are looking at a list of account:partiiton for each of our four clusters, and serving in the form.js only the list that corresponds to the given cluster.

I am dynamically creating the account:partition array for each user via the ood.rb initializer, and store it in four arrays of the object CustomAccPart, CustomAccPart.accpartnp for one cluster, CustomAccPart.accpartkp for another one, etc.

The cluster is defined with an field cluster in form.yml.erb. The account/partition are in field custom_accpart.

Now, for the form.js, I expect that I need to be able to read four CustomAccPart.accpart* and set the $('#batch_connect_session_context_custom_accpart') based on the value of $('#batch_connect_session_context_cluster'). But for that I need to fill the the $('#batch_connect_session_context_custom_accpart') form fields with values from the Ruby object, e.g. CustomAccPart.accpartnp.

I am struggling both with how the Ruby object/array CustomAccPart.accpartnp can be accessed in the JavaScript, and what part of the JavaScript object $('#batch_connect_session_context_custom_accpart') needs to be modified to read in the new account:partition list (supplied by the Ruby CustomAccPart.accpartnp ).

Any thoughts or examples how this can be done?

Thanks,
Martin

Hi Martin,

I think I understand what you’re doing. Can you share your form.yml.erb? If I’m understanding correctly, after seeing the form I think I can provide some help based on what we do. Thanks.

Hi Brandon,

thanks for your willingness to help. Here’s a relevant part of our form.yml.erb (the whole one is somewhat convoluted with some inlined files shared among multiple apps).

title: <%= app_name %>
attributes:
  cluster:
    widget: select
    options:
      <%- CustomQueues.clusters.each do |g| %>
      - "<%= g %>"
      <%- end %>
  custom_accpart:
    widget: select
    options:
      <%- CustomAccPart.accpart.each do |g| %>
      - "<%= g %>"
      <%- end %>

The CustomAccPart.accpart contains the account:partition entries for all the clusters in the above. That will need to get replaced with whatever comes from the JavaScript.

Let me know if this makes sense, if not I’ll expand.

BTW, example of the app with the inline/templated files is at OOD-apps-v3/matlab_app at master · CHPC-UofU/OOD-apps-v3 · GitHub, with the template files being at OOD-apps-v3/app-templates at master · CHPC-UofU/OOD-apps-v3 · GitHub, but, as I said, it’s fairly convoluted on how we patch these together (in an effort so that one change to the form can be done for all the apps we have).

This is my experience at least with 1.8. If you’re on 2.0 your experience may vary. Also, I tend to use the built in chromium developer tools a lot. (You may already know this, but I’ll include it in case anyone else finds it. Right click, then click inspect).

When the form.yml or form.yml.erb gets rendered to the page, each attribute listed in that form creates a <div class="form-group"> tag. Inside of that is usually going to be a label and then some kind of element based on the widget from form.yml. In this case, since we’re talking about a widget: select, the rendered HTML should look something like this.

<div class="form-group">
<label class="control-label" for="batch_connect_session_context_**custom_accpart**">custom_accpart</label>
<select class="form-control" name="batch_connect_session_context[**custom_accpart**]" id="batch_connect_session_context_app_name">
    <option value="option_1">option_1</option> 
    <option value="option_2">option_2</option> 
    <option value="option_3">option_3</option>
</select>
<span class="help-block"><p>some help text</p> </span></div>

Please note that the text between two asterisks will not appear between two asterisks. I put the asterisks around it to emphasize that the values come from your form.yml.

Once you have found the relevant section of rendered HTML, you can then start writing some javascript to get the values of it. I personally prefer JQuery, which is just a javascript library, but you can do vanilla javascript too.

The developer tools in chromium have a cool console app that you can use to write some javascript for quick testing. To get the value of your select widget, you can use something like this in JQuery: ("#batch_connect_session_context_**cluster**").val() or ("#batch_connect_session_context_custom_**accpart**").val(). Again note that the asterisks are not part of the actual code, just an emphasis. This should return the currently selected value of your options dropdown. If you change the selected dropdown value on your screen and rerun this in the console, it should change to the newly selected dropdown value.

You may have already known all that, but I wanted to give that background because it will be important in the next part. You want to set $('#batch_connect_session_context_custom_accpart') based on $('#batch_connect_session_context_cluster') and CustomAccPart.accpartnp/CustomAccPart.accpartkp/etc from form.yml.erb. What I would do is a bit of a hack, but I would define all of your CustomAccPart.acc* values in form.yml.erb and then immediately hide them in form.js. I do not know of a way to directly access Ruby data in JS without “pushing” the Ruby data to the HTML page first. (Unless you have some kind of REST API that you can call with AJAX, but that seems unnecessary when you can just render and hide the values).

You can hide the entire form-group like this: $("#batch_connect_session_context_**CustomAccPart.accpartnp**").parent().hide(). Again, kind of a hack, but it hides the form-group section from the user while providing you access to the Ruby values from form.yml.

Once you have the Ruby values on the page, you can then set an event handler to watch your cluster field so that whenever it changes, you can pull the necessary values from everything and set $('#batch_connect_session_context_custom_accpart').

Here’s what the event handler for a change event might look like:

$("#batch_connect_session_context_**cluster**").change(function() {
  alert( "Handler for .change() called." );
});

You will want to gather any values/do any data manipulation inside of that change() function and then you can set the value of $('#batch_connect_session_context_custom_accpart') at the very end of that function like this: $("#batch_connect_session_context_**accpart**").val("VALUE").

So to put that all together, my form.js would look something like this in the end:

$(document).ready(function() {
  $("#batch_connect_session_context_**CustomAccPart.accpartnp**").parent().hide();
  $("#batch_connect_session_context_**CustomAccPart.accpartkp**").parent().hide();
  $("#batch_connect_session_context_**cluster**").change(function() {
    cluster = $("#batch_connect_session_context_**cluster**").val();
    if (cluster == "CLUSTER_1") {
      NEW_VALUE = $('#batch_connect_session_context_custom_accpartnp').val();
    }
    else if (cluster == "CLUSTER_2") {
      NEW_VALUE = $('#batch_connect_session_context_custom_accpartkp').val();
    }
    $('#batch_connect_session_context_custom_accpart').val(NEW_VALUE);
  });
}

Side note: I set up select attributes a little different than your example. Maybe this is already how you’re doing it, but your form.yml.erb didn’t quite look like mine. Here’s how I set mine up

  app_name:
    label: Application
    widget: select
    options:
    <%- groups.each do |group| -%>
      - ["<%= group %>", "<%= group %>"]
    <%- end -%>
    <%- if groups.length == 0 -%>
      - ["", ""]
    <%- end -%>

This is similar to yours except that my options values are tuples rather than just singular values. Yours may already be tuples, but I wanted to point this out if you’re having some weird issues.

Hopefully this helps. Let me know if anything is unclear or if this wasn’t what you were looking for.

I have to mention that 2.1 is right around the corner and we’re shipping Slurm support for generating both accounts and queues (partitions).

That said - I don’t think we’ll have nicer names as I see you have in some of your apps. For example we’d just show titanx and not GTX Titan X, SP, owner

https://osc.github.io/ood-documentation/develop/release-notes/v2.1-release-notes.html#automatic-form-options

So your form would shrink down to this (at the time of writing the documentation says queue singular when it should be queues plural).

form:
  - auto_accounts
  - auto_queues

That said - you can see our old applications for a reference point. Here is our production Jupyter app, and everything up to the v0.21.3 tag uses a form.js that you can reference. Specifically how we use toggle_options for our node types (batch_connect_session_node_type)

Thanks Brandon and Jeff, it’s looking clear enough. I have a student working on this so we’ll try this and get back.

Jeff, a question on the 2.1 auto_accounts and auto_queues (partitions). We have them tied together for the most part. That is, most accounts only work on specific partitions, not on all available partitions. Would the 2.1 setup reflect that? Or, perhaps even more generally, will the auto_queues list all partitions on the cluster or only those partitions available to the user?

At least from our setup the logic is fairly convoluted, I need to call sacctmgr and pair it with scontrol output to get the right account:partition combination so I am not sure if you’d go that far with OOD to get that.

Yes they’re partitions cluster & account aware and accounts are cluster aware - or at least they should be. We don’t run Slurm in that manner, but I at least tried to account for this use case. So it should unless there are bugs, and if there are bugs, then we’ll fix them.

Well spot checking the code auto_queues may not be fully account aware. It’s cluster aware but could have spotty support for accounts.

If you can share sacctmgr & scontrol output that’d be great, maybe we can account for the more complex cases before lots of folks upgrade. You can email me at johrstrom@osc.edu if you don’t want to share them here.

Hi Jeff,

OK, let me discuss this first with my colleagues who are more familiar with all the account and partition logic, and then share with you if we think it’s worth the effort. I think our situation is fairly unique but they will know better.

I suspect complicated setups are fairly normal. I know UB does something like this and the requester of the original ticket had something similar. I don’t know about how ‘normal’ but I’d wager that there’s sufficient demand for complicated relationships between queue + account + cluster tuples such that we’d have to support it.

I know for sure, when we did auto_qos it had to account for a lot.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.