Code coverage report for sc/lang/klass/builder.js

Statements: 100% (71 / 71)      Branches: 100% (22 / 22)      Functions: 100% (21 / 21)      Lines: 100% (71 / 71)      Ignored: none     

All files » sc/lang/klass/ » builder.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 1421     1 1 1   1 1 1   1 112 112 112 112     1 51 14 19 14       51     1 64     1 1578     1 51   51 50 111     51 49 423 423       51     1 27 11 11   27 27     1 13           1 2           1 3           1 1           1 1           1 7           1 3     1 1642 3           1 233 23 27   183     1 1642 1639 922 922 717 233 233   1639 1639     1    
(function(sc) {
  "use strict";
 
  require("./klass");
  require("../dollar");
  require("../fn");
 
  var $ = sc.lang.$;
  var fn = sc.lang.fn;
  var strlib = sc.libs.strlib;
 
  function Builder(constructor) {
    this._className = constructor.prototype.__className;
    this._constructor = constructor;
    this._classMethods    = constructor.metaClass.__MetaSpec.prototype;
    this._instanceMethods = constructor.prototype;
  }
 
  Builder.prototype.init = function(defaults) {
    if (defaults) {
      Object.keys(defaults).forEach(function(name) {
        if (name !== "constructor") {
          this._instanceMethods[name] = defaults[name];
        }
      }, this);
    }
    return this;
  };
 
  Builder.prototype.addClassMethod = function(name, opts, func) {
    return addMethod(this, this._classMethods, name, opts, func);
  };
 
  Builder.prototype.addMethod = function(name, opts, func) {
    return addMethod(this, this._instanceMethods, name, opts, func);
  };
 
  Builder.prototype.addProperty = function(type, name) {
    var attrName = "_$" + name;
 
    if (type.indexOf("<") === 0) {
      this.addMethod(name, {}, function() {
        return this[attrName] || $.nil;
      });
    }
    if (type.indexOf(">") === type.length - 1) {
      this.addMethod(name + "_", {}, function($_) {
        this[attrName] = $_ || $.nil;
        return this;
      });
    }
 
    return this;
  };
 
  function createErrorFunc(errorType, message) {
    var func = function() {
      var errMsg = strlib.format("RECEIVER #{0}: #{1}", this.__className, message);
      throw new Error(errMsg);
    };
    func.__errorType = errorType;
    return func;
  }
 
  Builder.prototype.subclassResponsibility = function(methodName) {
    return this.addMethod(methodName, {}, createErrorFunc(
      sc.ERRID_SUBCLASS_RESPONSIBILITY,
      strlib.format("Message '#{0}' should have been implemented by this subclass.", methodName)
    ));
  };
 
  Builder.prototype.doesNotUnderstand = function(methodName) {
    return this.addMethod(methodName, {}, createErrorFunc(
      sc.ERRID_DOES_NOT_UNDERSTAND,
      strlib.format("Message '#{0}' is not understood.", methodName)
    ));
  };
 
  Builder.prototype.shouldNotImplement = function(methodName) {
    return this.addMethod(methodName, {}, createErrorFunc(
      sc.ERRID_SHOULD_NOT_IMPLEMENT,
      strlib.format("Message '#{0}' not valid for this subclass.", methodName)
    ));
  };
 
  Builder.prototype.notYetImplemented = function(methodName) {
    return this.addMethod(methodName, {}, createErrorFunc(
      sc.ERRID_NOT_YET_IMPLEMENTED,
      strlib.format("Message '#{0}' is not yet implemented.", methodName)
    ));
  };
 
  Builder.prototype.notSupported = function(methodName) {
    return this.addMethod(methodName, {}, createErrorFunc(
      sc.ERRID_NOT_SUPPORTED,
      strlib.format("Message '#{0}' is not supported.", methodName)
    ));
  };
 
  Builder.prototype.shouldUseLiterals = function(methodName) {
    return this.addClassMethod(methodName, {}, createErrorFunc(
      sc.ERRID_SHOULD_USE_LITERALS,
      strlib.format("Message '#{0}' is ILLEGAL, should use literals instead.", methodName)
    ));
  };
 
  function bond(that, methods) {
    return methods === that._classMethods ? "." : "#";
  }
 
  function throwErrorIfAlreadyExists(that, methods, methodName) {
    if (methods.hasOwnProperty(methodName)) {
      throw new Error(strlib.format(
        "#{0} is already defined", (that._className + bond(that, methods) + methodName)
      ));
    }
  }
 
  function choose(type) {
    switch (type) {
    case sc.TRUE : return $.True;
    case sc.FALSE: return $.False;
    }
    return $.DoNothing;
  }
 
  function addMethod(that, methods, name, opts, func) {
    throwErrorIfAlreadyExists(that, methods, name);
    if (typeof opts === "function") {
      func = opts;
      opts = {};
    } else if (typeof func !== "function") {
      func = choose(opts);
      opts = {};
    }
    methods[name] = fn(func, opts.args);
    return that;
  }
 
  sc.lang.klass.Builder = Builder;
})(sc);