Skip to content

Instantly share code, notes, and snippets.

@phpnode
Created February 25, 2013 09:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save phpnode/2bf86b9633032a51d036 to your computer and use it in GitHub Desktop.
Save phpnode/2bf86b9633032a51d036 to your computer and use it in GitHub Desktop.
###
# Assertations
Extremely simple (and fast) runtime assertions.
This implementation exposes an [[assert()]] function
that takes another function as its first argument.
If that function doesn't return a truthy value,
an [[assert.Error] AssertionError] error will be thrown with a message
indicating the source of the function that failed.
If the [[assert()]] function recieves two arguments, the first should
contain an informative message that provides more context about the failure,
and the assertion function should be passed as the second argument.
Assertions are enabled by default, but can be disabled
globally by setting [[assert.enabled]] to false.
Runtime assertions are not a substitute for proper unit tests. Instead
they can be thought of as a final layer of sanity checking.
It's also important to note that assertions are not a substitute
for user input validation. Assertions should be used to validate the
_developer's_ assumptions about the state of the program at a given point.
By liberally sprinkling assertions in your code, you can help ensure that
faulty assumptions - bugs, are found at their source. The alternative is
waiting (perhaps months or years!) for another function somewhere down the
stack to trigger the bug, something that might only occur under very specific,
hard to replicate circumstances. Assertions help expose these bugs early and
help you write more robust software as a result.
@see http://en.wikipedia.org/wiki/Assertion_(computing)
@name Assert
@example Assert that a function argument is the correct type.
class MyClass
# Initialize our example class
constructor: ->
@children = []
# Add a child but only if it is an instance of MyClass
addChild: (child) ->
assert -> child instanceof MyClass
@children.push child
this
parent = new MyClass
child1 = new MyClass
child2 = {}
parent.addChild child1 # ok
parent.addChild child2 # throws an AssertionError
@it should let successful assertions pass
fn = ->
assert -> true
true
expect(fn()).to.be.true
@it should throw an error on unsuccessful assertions
fn = ->
assert -> false
true
expect(fn).to.throw assert.Error
@it should let me disable and re-enable assertions globally
assert.enabled = false
fn = ->
assert -> false
true
expect(fn()).to.be.true
# now re-enable
assert.enabled = true
expect(fn).to.throw assert.Error
###
module.exports = do (assert = null) ->
###
Declare our custom error class that will be thrown
when an assertion fails
###
class AssertionError extends Error
###
Initializes the assertion error.
@param {String} message the assertion message
@optional @param {Function} caller the original caller of [[assert()]]
this is optional but can really improve the quality of the stack
traces in some environments.
@it should do something correctly
true
###
constructor: (message, caller = arguments.callee) ->
@name = 'AssertionError'
@message = message
Error.call this, message
# Not all environments support captureStackTrace
Error.captureStackTrace? this, caller
###
Our main assert function. Takes
a function as its sole argument.
That function should return something
truthy or the assertion will fail.
@param {Function} assertion the function that should return a truthy value
@throws {AssertionError} the assertion error if the assertion fails
###
assert = (args...) ->
return true unless assert.enabled
if args.length is 2
assertion = args[1]
message = args[0]
else
assertion = args[0]
message = assert.stringify assertion
throw new AssertionError message, arguments.callee if not assertion()
###
Track whether assertions are enabled or disabled
@var {Boolean}
###
assert.enabled = true
###
Make our error class available to the outside so that
it's possible to check for failed assertions.
This is mostly useful for unit testing. You should not
try and catch assertion errors in your application code,
if you're doing that, you're probably using assertions incorrectly.
@var {AssertionError}
###
assert.Error = AssertionError
###
Take an assertion function and return a nicer
human readable version of it. If the function
contains a single return expression, that expression
will be used as the string representation.
@param {Function} fn the function to stringify
@it should return only the last return expression for simple assertions
fn = ->
foo = "something irrelevant"
true
assert.stringify(fn).should.equal "true"
@it should return all the source if there are multiple returns
fn = ->
if true
true
else
false
result = assert.stringify(fn).replace /(\s+)/g, ''
result.should.equal """
if (true) {
return true;
}
else {
return false;
}
""".replace /(\s+)/g, ''
###
assert.stringify = (fn) ->
contents = fn.toString()
contents = (contents.substring contents.indexOf("{") + 1, contents.lastIndexOf("}")).trim()
regex = /^(\s+)?return(\s+)(.*)/mg
returns = []
while (statement = regex.exec contents)
if statement[3].substr(-1) is ';'
returns.push statement[3].substr 0, statement[3].length - 1
else
returns.push statement[3]
if returns.length is 1
returns[0]
else
contents
###
Return the assert function to be exported
###
assert
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment