PageRenderTime 53ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/README.markdown

https://github.com/bcoe/node-sexy-args
Markdown | 215 lines | 169 code | 46 blank | 0 comment | 0 complexity | a12972b41e5d8744865cb7343b98e9c1 MD5 | raw file
  1. Sexy Arguments
  2. ==============
  3. Motivation
  4. ----------
  5. I was working on one of my JavaScript libraries and noticed I was doing something silly:
  6. *Karait (https://github.com/bcoe/karait)*
  7. ```javascript
  8. exports.Queue = function(params, onQueueReady) {
  9. if (typeof(params) === 'function') {
  10. onQueueReady = params;
  11. params = {};
  12. }
  13. var defaults = {
  14. host: 'localhost',
  15. port: 27017,
  16. database: 'karait',
  17. queue: 'messages',
  18. averageMessageSize: 8192,
  19. queueSize: 4096
  20. };
  21. extend(this, defaults, params);
  22. }
  23. ```
  24. There's a lot of ritual around dealing with optional arguments and default parameters!
  25. I did a little digging, and found this problem was pretty widespread:
  26. *Node MongoDB Native (https://github.com/christkv/node-mongodb-native)*
  27. ```javascript
  28. Collection.prototype.insertAll = function insertAll (docs, options, callback) {
  29. if('function' === typeof options) callback = options, options = {};
  30. if(options == null) options = {};
  31. if(!('function' === typeof callback)) callback = null;
  32. // ... Function body.
  33. }
  34. ```
  35. *Express (https://github.com/visionmedia/express)*
  36. ```javascript
  37. res.sendfile = function(path, options, fn){
  38. var self = this
  39. , req = self.req
  40. , next = this.req.next
  41. , options = options || {};
  42. // support function as second arg
  43. if ('function' == typeof options) {
  44. fn = options;
  45. options = {};
  46. }
  47. // ... Function body.
  48. };
  49. ```
  50. *JSDom (https://github.com/tmpvar/jsdom)*
  51. ```javascript
  52. exports.jQueryify = exports.jsdom.jQueryify = function (window /* path [optional], callback */) {
  53. var args = Array.prototype.slice.call(arguments),
  54. callback = (typeof(args[args.length - 1]) === 'function') && args.pop(),
  55. path,
  56. jQueryTag = window.document.createElement("script");
  57. if (args.length > 1 && typeof(args[1] === 'string')) {
  58. path = args[1];
  59. }
  60. // ... Function body.
  61. }
  62. ```
  63. The Solution? Sexy Arguments
  64. ----------------------------
  65. sexy-args is DSL for:
  66. * Handling optional parameters.
  67. * Enforcing types.
  68. * Handling default values.
  69. sexy-args enforces sane defaults:
  70. * Arrays default to [].
  71. * Objects default to {}.
  72. * functions default to function() {}.
  73. * Extend is used by default when assigning default values for an object.
  74. * The common [options, callback] method signature is used by default:
  75. So,
  76. ```javascript
  77. exports.func = function(options, callback) {
  78. if (typeof(options) === 'function') {
  79. callback = options;
  80. options = {};
  81. }
  82. callback = callback || function() {};
  83. // ... Function body.
  84. }
  85. ```
  86. Becomes:
  87. ```javascript
  88. exports.func = function(options, callback) {
  89. sexy.args([this], function() {
  90. // ... Function body.
  91. });
  92. }
  93. ```
  94. A World With Sexy Arguments
  95. ---------------------------
  96. Here's what those prior examples would look like if they were using sexy-args:
  97. *Karait*
  98. ```javascript
  99. exports.Queue = function(params, onQueueReady) {
  100. sexy.args([this, ['object1', 'function1'], 'function1'], {
  101. object1: {
  102. host: 'localhost',
  103. port: 27017,
  104. database: 'karait',
  105. queue: 'messages',
  106. averageMessageSize: 8192,
  107. queueSize: 4096
  108. }
  109. }, function() {
  110. sexy.extend(this, params);
  111. });
  112. }
  113. ```
  114. *Express*
  115. ```javascript
  116. res.sendfile = function(path, options, fn){
  117. sexy.args([this, 'string1', ['object1', 'function1'], 'function1'], function() {
  118. var self = this,
  119. req = self.req,
  120. next = this.req.next;
  121. // ... Function body.
  122. });
  123. };
  124. ```
  125. *JSDom*
  126. ```javascript
  127. exports.jQueryify = exports.jsdom.jQueryify = function (window, path, callback) {
  128. sexy.args([this, 'object1', ['string1', 'function1'], 'function1'], function() {
  129. var jQueryTag = window.document.createElement("script");
  130. // ... Function body.
  131. });
  132. }
  133. ```
  134. I think this is much cleaner, which is the goal of sexy-args. Why repeat ritualistic syntax over and over again.
  135. The DSL
  136. -------
  137. * The first parameter to the sexy.args closure is an array describing the method signature.
  138. * subarrays are used to describe optional parameters, e.g., _[this, ['object1', 'function1'], 'function1']_
  139. * Indicates that the first parameter could be either an object or a function.
  140. * If the first parameter is an object, the second parameter can be a function.
  141. Default Values
  142. --------------
  143. * The second parameter given to the sexy.args closure is an object describing default values for each parameter.
  144. * the keys of the object correspond with the method signature, e.g., for [this, ['object1', 'function1'], function1]
  145. * {object1: {foo: 'bar'}} indicates that object1 should default to an object with a single key _foo_ equal to _bar_.
  146. Extends Functionality
  147. ---------------------
  148. To simplify your life, sexy.args exposes a shorthand for extending objects.
  149. ```javascript
  150. exports.foo = function(path, options, fn){
  151. sexy.args([this, 'string1', ['object1', 'function1'], 'function1'], function() {
  152. sexy.extend(this, options);
  153. });
  154. };
  155. ```
  156. The above code would extend an instance of _foo_ with the options object.
  157. Contributing to sexy-args
  158. ----------------------
  159. * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
  160. * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
  161. * Fork the project
  162. * Start a feature/bugfix branch
  163. * Commit and push until you are happy with your contribution
  164. * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
  165. * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
  166. Copyright
  167. ---------
  168. Copyright (c) 2011 Attachments.me. See LICENSE.txt for
  169. further details.