Skip to content

Instantly share code, notes, and snippets.

@spion
Last active February 27, 2016 00:25
Show Gist options
  • Save spion/816baa575454e9d77f81 to your computer and use it in GitHub Desktop.
Save spion/816baa575454e9d77f81 to your computer and use it in GitHub Desktop.

Immediately convert validation errors to Result<T>, as soon as possible. Result<T> would be the sync version of promises, with chain having a similar role to then, and .orElse having a similar role to .catch.

Assuming even crop was converted to not throw an exception but return a Result<T>:

var Transforms = {};

class ImageCropper() {
  constructor(range) {
    this._range = range
  }
  applyTo(image) {
    var r = this._range
    return crop(image, r.x1, r.y1, r.x2, r.y2)
  }
}

Transforms.ImageCropper = ImageCropper;

class FacedetectCropper extends ImageCropper {
  autoDetectFace(image) {
    // runs costly algorithm and returns face range
  }
  applyTo(image) {
    this._range = this.autoDetectFace(image);
    return super.applyTo(image);
  }
}

Transforms.FacedetectCropper = FacedetectCropper;

applyTransforms propagates the Result

function applyTransforms(transforms, image) {
  var result = Result.of(image); // wrap in result
  transforms.forEach(transform => {
    result = result.chain(image => transform.applyTo(result))
  })
  return result;
}

Finally the response handler is this:

// Assume that we already passed through basic request validator for request.body params
// So all transforms are of valid type and have valid options for that route.
function applyUserTransforms(request, response) {
  var transforms = request.body.transforms.map(t => new Transforms[t.type](t.options)));
  applyTransforms(transforms, request.body.image)
  .chain(image => response.answer(200, {..content type..}, image))
  .orElse(error => {
    if (error.type == 'ValidationError') {
      response.answer(400, "This combination of transforms is not acceptable because: " + error.message);
    } else {
      logger.recordError(e);
      response.answer(500, "Unexpected error");
    }
  });
}

Of course, this doesn't qualify as "check your inputs before you pass them".

Note: An interesting observation is that this code looks precisely the same regardless of whether it executes synchronously or asynchronously - even the sequential execution of the transformations :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment