Slacking Off With Komodo

As a relatively new developer, and a complete lightweight when compared to the rest of the Komodo development team, I find myself sharing snippets, errors and diffs for review quite often. Since I like to share (Mom and Dad taught me well), I thought it was important to make it easier to share in Komodo. At one point, a Komodo user was limited to using kopy.io to do this, and only in limited areas of Komodo. In 10.2, we've extended Komodo to allow our users to share more easily and in more ways.

We wanted to add more endpoints (more kopy.ios), allow users to share text from more aspects of Komodos (logs, diffs, files, etc.) as well as allow users to more easily add their own custom endpoints without having to touch any of Komodo UI. We did this with two major additions:

1) A new “Sharing” SDK (I’ll explain)
2) Komodo + Slack integration (uses the above SDK to post content to Slack!)

The Share SDK

var share = require("ko/share");
source code

The logical way to extend the existing UI with more "share points" (menu items to select an output) was to extend the existing kopy.io module's UI integrations and move it into it's own SDK. Thus ko/share was born.

The share module now adds menus to the following source points in Komodo:

All of these interfaces have been augmented with a Share dropdown menu.

Register your Module

At this point you might be thinking "Hey neat Carey, you added another dropdown menu list to Komodo...so what?"

Good question! If it was just a drop down all over the place that would be boring, so next we needed to add items to this list for all output modules dynamically.


share.register(name, namespace, label);

source code

Enter share.register(). It takes a name (e.g. "kopy"), a namespace (e.g. "ko/share/kopy" in order to require your module.), and a label (e.g. "Share code on kopy.io", which is the label shown in the menu list). Komodo uses CommonJS method of getting JS code and that requires a namespace (like the one mentioned above). A basic example you can follow to add your own namespace for your share implementation is as follows: require.setRequirePath("myShareProject","file:///abs/path/to/myShareProject"). The best method to do this would be to include your code in a Komodo Addon but that's to big a topic to cover here. Ask for help in our forums.

Komodo then checks to make sure you implemented a share function in your module that Komodo can call later (this is explained in more detail in the next section). It then cycles through all of the UI sources in Komodo we mentioned above and adds a new menu item for you.

Implement the Share Interface

require("ko/share/kopy").share(data, meta)
source code

Above I mentioned a share function that the share.register method looks for. This is the one and only requirement of your share module--it must have a share function. This is the interface we use to pass content to your module.

The Data

The function should take a string of data and an Object of meta information. The data string is the content that will be posted as text to the output source. For example, in the case of kopy.io, data is the code displayed in the webpage:


#!/usr/bin/env python3
print "why is this broken??"
print("oh...duh...it's Python 3")

The Meta

The meta object holds basic meta information about the data you want to post. You can expect an object as follows:


var meta =
{
    langauge: "javascript",
    title: "MyFile.js"
}

source code

Your Share module can use this information as you see fit. For example, you can post a file to kopy.io and leave it up to kopy.io to figure out what your content might be (text, Javascript, Python?) or you can use the language property and send that along in the API call sent to kopy.io.

Since this module is so new, we are open to ideas for additional properties in the meta object, so please let us know if you have any suggestions.

Slacking

var slack = require("ko/share/slack");
Sorry, no more source links. This stuff's not open sourced.

Now that we have the Share module, the next step was to add some share endpoints. Kopy.io was a breeze since our lead Komodo Developer, Nathan Rijksen, had already written most of the necessary code back in Komodo 9.2.

Since our team and MANY MANY others use Slack on a daily basis, we chose to add Slack.

This project was a lot of firsts for me. I got to augment a Django Python server with a new API endpoint, do funky window management and event handling (imagine having to program your website by starting at the browser window then trying to access the loading page in a particular tab to trigger your onload event...FUN!), and trying to streamline a 100% async user experience. I even got to take a new service for a spin that Nathan added while I was building the Slack integration, ko/simple-storage. Simple-storage allows you to persist information about your addon, Userscript, etc. and have it persist between Komodo restarts. This is the recommended way to persist information rather than the traditional require("ko/prefs") method.

Django API Endpoint

In order for Komodo to post content to your various Slack Teams and Channels it needs to authenticate users. Slack has a system in place which requires you to have a server in the middle of the process to actually send the final key request to the Slack Auth servers.

I started writing this in Node.js since that seems to be the go-to for API servers these days. In the end though, I decided to build it on Django. We already write so much Python in Komodo and we have a few other services built on a Django server. Besides, I've already written a Node API server in class and have never touched Django. Get moderately good at a ton of stuff and never become an expert at anything...that's my advice kids (with tongue firmly pressed against my cheek)!

Without going into too much boring detail, the server handles a request from Komodo with two temporary auth keys (one from Slack and one from Komodo), which are sent to another Slack authorization server that handles permissions. When that is returned from Slack, our server sends the new final auth key back to Komodo and it's encrypted and saved to disk.

The Slack Panel

The panel that appears after you've authenticated and you're picking a channel, message, etc. is plain. There doesn’t appear to be anything special about it. But that’s not true. What is of interest is that it's built completely from the ko/ui SDK, just like the Start Up Wizard (Help > Run first start wizard again). There is ZERO markup involved there.

Here's a sample of what it looks like in the backend. You can copy and paste this code into the Komodo console to try it for yourself (View menu > Tabs & Sidebars > Console, or Ctrl (Cmd on OSX) + Shift + B then click the Console tab):


panel = require("ko/ui/panel").create({
            anchor: require("ko/windows").getMostRecent().document.documentElement,
            attributes: {
                backdrag: true,
                noautohide: true,
                width: "450px",
                class: "dialog"
            }
        });
panel.open();
var options = { attributes:
        {
            placeholder: "Title",
            col: 120,
            flex: 1
        }};
var title = require("ko/ui/textbox").create(options);
panel.add(title);

All the fields that you fill out in the Slack integration panel are saved for use later using the new ko/simple-storage I mentioned above. Let's have a look at that, then I think we should call it a day on this blog...oh ps. you can get rid of that panel I got you to create on your screen by just writing panel.close() in the JS console ;)

Simple Storage

ko/simple-storage is meant to replace anything ko/prefs does that is not actually “preference” related as the standard storage for application, addon, or userscript information. It's persistent and easier to use, which should make anyone customizing Komodo pretty happy.

Here's a small sample of how it works.


var my_SS = require("ko/simple-storage").get("mine");
my_SS.storage.pizza = "It's so good it's scary, Carey!";
console.log("Is pizza good? "+my_SS.storage.pizza);

The storage object is saved to disk and can be reloaded after a restart by using the same code. And when you're done with it, you just remove it.


var my_SS = require("ko/simple-storage").get("mine");
console.log("Is pizza still good? "+my_SS.storage.pizza + " Stop asking stupid questions.");
require("ko/simple-storage").remove("mine");

Just open the Komodo Console(View menu > Tabs & Sidebars > Console, or Ctrl (Cmd on OSX) + Shift + B then click the Console tab) if you'd like to give it a try.

So that was a long blog...but I hope you found it useful and can take advantage of these great ways to share with Komodo. As my mother always said...sharing is caring!