functions.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. var objects = require('./objects')
  2. exports.id = function (i) {
  3. return i
  4. }
  5. /*
  6. often you want to do something like: map(array, JSON.stringify)
  7. but map calls the iterator with (value, key, object)
  8. which confuses JSON.stringify because it expects a replacer function as the second arg
  9. previously one would do something like this:
  10. map(array, function (x) {return JSON.stringify(x)})
  11. TOO LONG, I can't be bothered!
  12. now use:
  13. map(array, curry(JSON.stringify, 0))
  14. the `0` that thu first arg is in the first position.
  15. non numbers are literal, also, negative numbers count from the end of the array
  16. (handy for ensuring that callbacks are in the right place)
  17. curry(func, 0) // function (a) { return func(a) }
  18. curry(func, 0, 'whatever') // function (a) { return func(a, 'whatever') }
  19. curry(func, 0, 'whatever', -1) // function (a, b) { return func(a, 'whatever', b) }
  20. of course, you cannot use this function to stick in numbers, but what are you, an accountant?
  21. it's really handy though, when you can simplify this:
  22. ctrl.toAsync(function (ls) {
  23. return d.map(ls, function (e) { return path.join(dir, e) } )
  24. }) ]) (dir, cb)
  25. to this:
  26. ctrl.toAsync(d.curry(d.map, 0, d.curry(path.join, dir, 0) ))
  27. */
  28. exports.curry = function (/*funx, args...*/) {
  29. var args = [].slice.call(arguments)
  30. , funx = args.shift()
  31. return function (){
  32. var _args = [].slice.call(arguments)
  33. return funx.apply(this, objects.map(args, function (i) {
  34. return 'number' !== typeof i ? i : _args[i < 0 ? _args.length + i : i]
  35. }))
  36. }
  37. }
  38. exports.deepCurry = function () {
  39. var args = [].slice.call(arguments)
  40. , funx = args.shift()
  41. return function () {
  42. var _args = [].slice.call(arguments)
  43. return funx.apply(this, objects.deepMerge(args, _args))
  44. }
  45. }
  46. /*
  47. before: modify the args to a function before it is called.
  48. I think this is called aspect oriented programming.
  49. the 'before' function returns a function that calls a shim function before a given function,
  50. the 'shim' function is passed the args of the returned function, and may alter them before
  51. the given function is called.
  52. hmm, thats about twice as much english than javascript.
  53. use it like this:
  54. function (x,p,z) {return whatever(z, p, x)}
  55. before(whatever, function (args) { return args.reverse() })
  56. hmm, thats more verbose than the straight forward way...
  57. maybe that function is not such a great idea.
  58. what about beforeCallback?
  59. function (opts, callback) { request (opts, function (err, res, body) { callback(err, body) } }
  60. beforeCallback(request, function (args) { return [args[0], args[2]] })
  61. ah, that is better.
  62. */
  63. var fName = function (f) {
  64. return '"' + (f.name || f.toString().slice(0,100)) + '"'
  65. }
  66. var before = exports.before = function (given, shim) {
  67. return function wrapped () {
  68. return given.apply(this, shim([].slice.call(arguments)))
  69. }
  70. }
  71. //before(whatever, function (a) { return a.reverse() })
  72. var beforeCallback =
  73. exports.beforeCallback = function (async, shim) {
  74. return before(async, function (args) {
  75. args.push(before(args.pop(), shim)); return args
  76. })
  77. }
  78. /*
  79. prevent a function from being called intil some thing important has happened.
  80. (useful for delaying something until a connection is made.)
  81. use like this:
  82. dbSave = defer(dbSave)
  83. bdSave(doc1) //these calls will be buffered
  84. bdSave(doc2)
  85. db.connect()
  86. db.on('connection', dbSave.flush)
  87. db.on('disconnection', function () {dbSave.buffer(); db.reconnect() })
  88. */
  89. var defer =
  90. exports.defer = function (deferred) {
  91. var buffer = []
  92. , buffering = true
  93. function deferrer () {
  94. var args = [].slice.call(arguments)
  95. if(buffering)
  96. buffer.push(args)
  97. else deferred.apply(null, args)
  98. }
  99. deferrer.flush = function () {
  100. buffering = false
  101. while(!buffering && buffer.length) {
  102. deferred.apply(null, buffer.shift())
  103. }
  104. }
  105. deferrer.buffer = function () {
  106. buffering = true
  107. }
  108. return deferrer
  109. }
  110. /*
  111. curryHead(func, tail...)
  112. -> function (head) { return func(head, tail...) }
  113. given a function and tail args,
  114. return a function that takes a head arg,
  115. and applys head.concat(tail) to the given function
  116. */
  117. var curryTail =
  118. exports.curryTail = function (func) {
  119. var args = [].slice.call(arguments, 1)
  120. if(!args.length) return func
  121. return function (a) {
  122. return func.apply(this, [a].concat(args))
  123. }
  124. }
  125. /*
  126. curryHead(func, head)
  127. -> function (tail...) { return func(head, tail...) }
  128. given a function and head arg,
  129. return a function that takes tail args,
  130. and calls the given function with head.concat(tail)
  131. */
  132. var curryHead =
  133. exports.curryHead = function (func, a) {
  134. return function () {
  135. var args = [].slice.call(arguments)
  136. return func.apply(this, [a].concat(args))
  137. }
  138. }
  139. /*
  140. curryTailHead
  141. take a given function and return a function that takes tail args,
  142. and returns a function that takes a head arg,
  143. and that calls the given function with head.concat(tail)
  144. you are certainly deep down the rabit hole when you are using functional composition on curry functions
  145. this is actually gonna be useful for making spec assertions -- with higer order assertions.
  146. so that you can say
  147. equal(x, X)
  148. with:
  149. _equal(X)(x)
  150. */
  151. var curryTailHead = exports.curryTailHead = function (funx) {
  152. return curryHead (curryTail, funx)
  153. }