index.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. var util = require('util')
  2. module.exports = ifAsync
  3. var OK = 0
  4. var EXPECT_THEN = 1
  5. function ifAsync() {
  6. var clauses = toArray(arguments)
  7. var elseClause = elseNoop
  8. var fluentState = OK
  9. if (clauses.length === 0) {
  10. throw new Error('at least one predicate and one consequent are required')
  11. }
  12. // using fluent interface, we expect the user will call then() before invoking the functor
  13. if (clauses.length === 1) {
  14. fluentState = EXPECT_THEN
  15. } else if (clauses.length % 2 === 1) {
  16. elseClause = clauses.pop()
  17. }
  18. var functor = function() {
  19. if (fluentState !== OK) {
  20. throw new Error('missing at least one consequent, you forgot to call then() ?')
  21. }
  22. var args = arguments
  23. var callback = args[args.length - 1]
  24. if (typeof callback !== 'function') {
  25. throw new Error('missing callback argument')
  26. }
  27. var predicate = clauses.shift()
  28. if (!predicate) {
  29. return elseClause.apply(null, args)
  30. }
  31. var consequent = clauses.shift()
  32. var replacedCallbackArgs = toArray(args)
  33. replacedCallbackArgs.pop()
  34. replacedCallbackArgs.push(predicateCallback)
  35. predicate.apply(null, replacedCallbackArgs)
  36. function predicateCallback(err, result) {
  37. if (err) return callback(err)
  38. if (result) {
  39. return consequent.apply(null, args)
  40. } else {
  41. functor.apply(null, args)
  42. }
  43. }
  44. }
  45. functor.then = function(fn) {
  46. if (fluentState !== EXPECT_THEN) {
  47. throw new Error('not expecting a then() call now')
  48. }
  49. fluentState = OK
  50. clauses.push(fn)
  51. return functor
  52. }
  53. functor.and = function(fn) {
  54. var predicate = clauses.pop()
  55. // logical AND using ifAsync
  56. clauses.push(
  57. ifAsync(predicate)
  58. .then(fn)
  59. .else(callbackFalse)
  60. )
  61. return functor
  62. }
  63. functor.and.not = function(predicate) {
  64. return functor.and(not(predicate))
  65. }
  66. functor.or = function(fn) {
  67. var predicate = clauses.pop()
  68. // logical OR
  69. clauses.push(
  70. ifAsync(predicate)
  71. .then(callbackTrue)
  72. .elseIf(fn)
  73. .then(callbackTrue)
  74. .else(callbackFalse)
  75. )
  76. return functor
  77. }
  78. functor.or.not = function(predicate) {
  79. return functor.or(not(predicate))
  80. }
  81. functor.else = function(predicate) {
  82. if (fluentState === EXPECT_THEN) {
  83. throw new Error('only then() may be called after elseIf()')
  84. }
  85. elseClause = predicate
  86. return functor
  87. }
  88. functor.elseif = functor.elseIf = function(predicate) {
  89. if (fluentState === EXPECT_THEN) {
  90. throw new Error('only then() may be called after elseIf()')
  91. }
  92. clauses.push(predicate)
  93. // allow only then after a call to elseif
  94. fluentState = EXPECT_THEN
  95. return functor
  96. }
  97. functor.elseif.not = function(predicate) {
  98. return functor.elseIf(not(predicate))
  99. }
  100. return functor
  101. }
  102. ifAsync.not = function(predicate) {
  103. if (typeof predicate !== 'function') {
  104. throw new Error('argument must be a predicate function')
  105. }
  106. return ifAsync(not(predicate))
  107. }
  108. function not(predicate) {
  109. return function () {
  110. var args = toArray(arguments)
  111. var callback = args.pop()
  112. if (typeof callback !== 'function') {
  113. throw new Error('expected a callback but instead got ' + typeof callback)
  114. }
  115. args.push(function(err, result) {
  116. callback(err, !result)
  117. })
  118. predicate.apply(null, args)
  119. }
  120. }
  121. function elseNoop() {
  122. var args = toArray(arguments)
  123. var callback = args.pop()
  124. args.unshift(null)
  125. if (typeof callback !== 'function') {
  126. throw new Error('expected a callback function')
  127. }
  128. setImmediate(function () {
  129. callback.apply(null, args)
  130. })
  131. }
  132. function callbackTrue() {
  133. var callback = arguments[arguments.length - 1]
  134. if (typeof callback !== 'function') {
  135. throw new Error('expected a callback function')
  136. }
  137. callback(null, true)
  138. }
  139. function callbackFalse() {
  140. var callback = arguments[arguments.length - 1]
  141. if (typeof callback !== 'function') {
  142. throw new Error('expected a callback function')
  143. }
  144. callback(null, false)
  145. }
  146. function toArray(args) {
  147. return Array.prototype.slice.call(args, 0)
  148. }