oVirt.js - API & implementation design summary

Hello, this is a follow-up to my oVirt.js announcement email, I'd like to give you a quick summary of API & implementation design. In other words, I'll try to describe key decisions in oVirt.js API & implementation. 1, General design We're trying to follow best practices in JavaScript development, such as: - containing the whole SDK in a single global variable (because until ES6 we cannot truly avoid global variables) and using IIFE/closure for better information hiding - using ES5 "use strict" (Strict Mode) on function level (so that Strict Mode compliant environments will detect problems early) while not relying on semantical differences introduced by Strict Mode (main reason = IE9) 2, API design As presented in earlier "oVirt JavaScript SDK: Design Proposal" session, our main goals here are API usability and readability. (If you missed this session before, let me know and I'll forward you the slides.) - using fluent interface concept, for example: ovirt.api.datacenters.list().success(dcsListedCallback).run(); - separating the concept of resource (collection) vs. operation (operation = reusable RESTful method abstraction) - separating three logical namespaces: * ovirt.svc -- services used by API implementation Each service should be an object containing reusable methods. 1. existing services can be overridden to achieve SDK portability for given runtime environment. 2. existing services can be used and new services can be added by SDK extensions or clients. * ovirt.util -- utilities used by API implementation Unlike services, utilities are meant to assist with writing more efficient JavaScript code. * ovirt.api -- API as the main entry point to SDK functionality 3, Implementation design I've tried to catch up with my JavaScript knowledge recently, but I'm not an expert, I'm still learning things. Following are ideas which I took into practice within oVirt.js API implementation: - creating objects via mixins, each mixin is a function which gets some object ("that") and adds new properties to that object (augmentation) - simple dependency injection mechanism for mixins, with dependencies resolved against "ovirt.svc" namespace, for example: // Create new object, "spec" = information needed for object creation. function makeSomeObject (spec) { return mixins.mix(spec, [mixinOne, mixinTwo], // array of mixin functions ['depNameOne', 'depNameTwo'] // array of dependency names ); } // "that" = object to augment. // "spec" = information provided for object creation. // "my" = object containing shared secrets, for more details see [1] function mixinOne (that, spec, my) { // Augment "that" with "quack" method, utilize closure to capture // "spec.name" without exposing it to users of "that" object. that.quack = function () { console.log(spec.name + ' says quack!'); // Consume "depNameOne" service, resolved from ovirt.svc namespace that.doSomething = function () { my.depNameOne.whatever(); } } [1] JavaScript: The Good Parts (2008) by Douglas Crockford, chapter "Inheritance", section "Functional" - explanation of "my" object within functional constructor pattern We are NOT using "new" operator in JavaScript. We are also NOT relying on object's prototype chain. We are simply creating useful objects via mixins, which is in my opinion the most simple and flexible way. Regards, Vojtech
participants (1)
-
Vojtech Szocs