New in version 1.2.3.
This document describes how to develop and host your own Apps.
Apps/A Middleware is a glue that makes serving Apps possible. It depends on Node.js so make sure it is installed. Afterwards either checkout or download & unzip the repo then install its dependencies and run the example:
$ wget https://github.com/intermine/apps-a-middleware/archive/master.zip
$ unzip master.zip
$ cd apps-a-middleware-master/
$ npm install
$ PORT=1234 node example/index.js
Visiting 127.0.0.1:1234 should now show a bunch of Apps on a page. They are coming from InterMine Apps/A Sources. The next section will describe how to develop a custom app like one of those on the page.
The middleware accepts one or more paths to a directory with Apps. Above, we have seen an example of loading such a directory. Let us have a look at one of the Apps, a publications-displayer.
The middleware knows of and can load this App because we have created a folder that has a name that can be used in a URL. If we were to add spaces, for example, that would be a big no no.
Next we have a configuration stashed away in a config.js file. This file exports config in a JSON-like syntax. It will have a header with some properties like author, tile, description etc. The more interesting point is the dependencies section. InterMine JavaScript API Loader describes how this section works. It is here that you define what kind of CSS and JS dependencies your App has. These will be automatically resolved before loading the App on a page.
You can fill the file’s config section to show whoever embeds your App what sort of configuration they should be providing. The idea is as follows:
Next up is the body of the App in a presenter.[js|ts|ls|coffee] file. The extensions allure to the fact that you can use the following languages to write your App in:
This file will have all the logic needed to “do something”. It needs to fulfill the following interface:
exports.App = (function() {
function App(config, templates) {}
App.prototype.render = function(target) {};
return App;
})();
Or the same in TypeScript:
export class App {
constructor(config: Object, templates: Object) {
}
render(target: string): void {
}
}
Or the same in CoffeeScript:
class exports.App
constructor: (config, templates) ->
render: (target) ->
Or the same in LiveScript:
class exports.App
(config, templates) ->
render: (target) ->
The constructor takes two parameters:
The render function takes just one parameter:
Next up are templates. They are the place where you put your HTML that will be rendered. You have a choice between two templating languages:
class exports.App
constructor: (config, @templates) ->
render: (target) ->
$(target).html @templates[template_name_with_extension] { 'some': 'data', 'right': [ 'here' ] }
class exports.App
constructor: (config, @templates) ->
render: (target) ->
$(target).html (new Hogan.Template(@templates['template.hogan'])).render { 'some': 'data', 'right': [ 'here' ] }
Finally we might want to style our app. Usually a main style will be defined by a CSS framework required in the config file, but there is always place for that special something. To define a custom style guaranteed to be applicable to your App only, save a CSS or Stylus file as style.[css|styl].
To run it all refer to the example/index.js and example/public/index.html files in the middleware repo.
Extend your config.js with the following key, value pair:
module.exports = {
"appRoot": "app/index" // points to say index.js in "app" folder
}
Use the CommonJS Modules/1.1 require pattern, for example as follows (in TypeScript):
import models = module("models");
var pete = new models.Person('pete');
And in models.ts:
export class Person {
constructor(name: string) { }
}
See also
Take a look at the choose-list app in the intermine-apps-a repo. It shows an example of how different modules can be required.
Each app gets its own internal implementation of require to load its and only its modules. If you have something on window.require it will not get overwritten but it will also not be used by us. Your apps see our version of require, not any other higher up. This is so that multiple apps can live peacefully on a page without deviating from the require spec and having our own prefixes and app silos.
In the future, we will also want to get the compiled app once into the browser and then require it multiple times on a page if need be. Using vanilla module loader would then not work as we would not be getting new instances of modules per app instance.
Suggestions are welcome.
Check the head of the following file to see the regex rules that the builder uses when going through your app sources.
For example:
rules = [
[ /^((presenter|app)(\.coffee))|(\/(.*)\.coffee)/g, 'module', 'coffeescript' ]
[ /^((presenter|app)(\.js))|(\/(.*)\.js)/g, 'module', 'generic' ]
[ /^((presenter|app)(\.ls))|(\/(.*)\.ls)/g, 'module', 'livescript' ]
[ /^((presenter|app)(\.ts))|(\/([^\.]*)\.ts)/g, 'module', 'typescript' ] # skip `.d.ts`
[ /^(.*)\.css/g, 'style', 'generic' ]
[ /^(.*)\.styl/g, 'style', 'stylus' ]
[ /^(.*)\.eco/g, 'template', 'eco' ]
[ /^(.*)\.hogan/g, 'template', 'hogan' ]
]
Would like to add another rule/filetype? Define its rule and add a new handler in apps-a-middleware/builder/types.
Well, the service you are using extends any existing service you are offering to your users. It would not make much sense to fire up a new service instance per “a thing” that you want to offer to your users. By using a middleware concept, you create one Node.js service on one port and it offers various functionalities as specified by its middlewares.
You can just call it “apps”, but as a developer you need some form of a namespace to go on, so we use the Latin alphabet for suffixes. In the future/different version will be called Apps/B and so on.
Down to your preference: