PageRenderTime 54ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/backbone.js

https://github.com/gaslight/mongoose-rest
JavaScript | 238 lines | 133 code | 44 blank | 61 comment | 22 complexity | c6799dfcf63954da1f0f54c0475ef272 MD5 | raw file
  1. /*!
  2. * Chris O'Hara
  3. * Copyright(c) 2011 Chris O'Hara <cohara87@gmail.com>
  4. * MIT Licensed
  5. */
  6. /**
  7. * Module dependencies.
  8. */
  9. var models = require('./models')
  10. , fs = require('fs')
  11. , lingo = require('lingo').en;
  12. /**
  13. * Convert a lowercase, underscored string to a proper cased class name.
  14. * e.g. "my_table" => "MyTable"
  15. *
  16. * @param {string} table
  17. * @return {string} class
  18. * @api private
  19. */
  20. function classify (str) {
  21. return str.replace('_', ' ').replace(/(^| )[a-z]/g, function (str) {
  22. return str.toUpperCase();
  23. }).replace(' ', '');
  24. }
  25. /**
  26. * Recursively replace `_id` with `id` in an object.
  27. *
  28. * @param {object} instance
  29. * @api private
  30. */
  31. function convertUnderscoreId (instance) {
  32. for (var attr in instance) {
  33. if (attr == '_id') {
  34. instance.id = instance[attr];
  35. delete instance._id;
  36. } else if (Array.isArray(instance[attr])) {
  37. instance[attr].forEach(function (child) {
  38. if (typeof child === 'object') {
  39. convertUnderscoreId(child);
  40. }
  41. });
  42. } else if (typeof instance[attr] === 'object') {
  43. convertUnderscoreId(instance[attr]);
  44. }
  45. }
  46. }
  47. /**
  48. * Generate backbone models.
  49. *
  50. * @param {string} namespace (optional)
  51. * @return {string} backbone_javascript
  52. * @api public
  53. */
  54. exports.generate = function (namespace) {
  55. namespace = namespace || '';
  56. var backbone = backboneCommon(namespace);
  57. models.getEmbedded().forEach(function (resource) {
  58. backbone += backboneEmbeddedModel(namespace, resource);
  59. });
  60. models.getTopLevel().forEach(function (resource) {
  61. backbone += backboneTopLevelModel(namespace,
  62. resource, models.getChildren(resource));
  63. });
  64. return backbone;
  65. }
  66. /**
  67. * Generate express view helpers for creating backbone models and collections.
  68. *
  69. * @param {HTTPServer} app
  70. * @param {string} namespace (optional)
  71. * @api public
  72. */
  73. exports.helpers = function (app, namespace) {
  74. namespace = namespace || '';
  75. app.dynamicHelpers({
  76. backboneModel: function (request, response) {
  77. return function (resource, var_name) {
  78. var singular = resource
  79. , klass = namespace + classify(resource)
  80. , instance = request.resource(resource);
  81. var_name = var_name || resource;
  82. if (instance.toJSONSafe) {
  83. instance = instance.toJSONSafe();
  84. } else {
  85. instance = instance.toJSON()
  86. }
  87. convertUnderscoreId(instance);
  88. var model = 'var '+var_name+' = '
  89. + 'new '+klass+'(' + JSON.stringify(instance) + ');';
  90. return model;
  91. }
  92. }
  93. , backboneCollection: function (request, response) {
  94. return function (resource, var_name) {
  95. var singular = lingo.singularize(resource)
  96. , klass = namespace + classify(singular)
  97. , instances = request.resource(resource);
  98. var_name = var_name || resource;
  99. for (var i = 0, l = instances.length; i < l; i++) {
  100. if (instances[i].toJSONSafe) {
  101. instances[i] = instances[i].toJSONSafe();
  102. } else {
  103. instances[i] = instances[i].toJSON()
  104. }
  105. convertUnderscoreId(instances[i]);
  106. }
  107. var collection = 'var '+var_name+' = '
  108. + 'new '+klass+'Collection(' + JSON.stringify(instances) + ');';
  109. return collection;
  110. }
  111. }
  112. });
  113. }
  114. /**
  115. * Generate backbone models and write to a file.
  116. *
  117. * @param {string} file
  118. * @param {string} namespace (optional)
  119. * @api public
  120. */
  121. exports.generateFile = function (file, namespace) {
  122. fs.writeFileSync(file, exports.generate(namespace));
  123. }
  124. /**
  125. * Generate common backbone code.
  126. *
  127. * @param {string} namespace (optional)
  128. * @api private
  129. */
  130. function backboneCommon (namespace) {
  131. return 'var '+namespace+'Model = Backbone.Model.extend({\n'
  132. + ' set: function (attributes, options) {\n'
  133. + ' Backbone.Model.prototype.set.call(this, '
  134. + 'attributes, options);\n'
  135. + ' this.pullEmbedded();\n'
  136. + ' }\n'
  137. + ' , pullEmbedded: function () {\n'
  138. + ' for (var attr in this.attributes) {\n'
  139. + ' if (this[attr] && this[attr] instanceof Backbone.Collection) {\n'
  140. + ' for (var i = 0, models = [], model = this[attr].model, '
  141. + 'l = this.attributes[attr].length; i < l; i++) {\n'
  142. + ' models.push(new model(this.attributes[attr][i]));\n'
  143. + ' }\n'
  144. + ' this[attr].reset(models);\n'
  145. + ' delete this.attributes[attr];\n'
  146. + ' }\n'
  147. + ' }\n'
  148. + ' }\n'
  149. + '});\n'
  150. + '\n\n'
  151. + 'var '+namespace+'Collection = Backbone.Collection.extend({});\n\n';
  152. }
  153. /**
  154. * Generate backbone code for embedded models.
  155. *
  156. * @param {string} namespace (optional)
  157. * @api private
  158. */
  159. function backboneEmbeddedModel (namespace, resource) {
  160. var singular = namespace + classify(lingo.singularize(resource));
  161. return 'var '+singular+' = '+namespace+'Model.extend({})\n'
  162. + ' , '+singular+'Collection = '
  163. + namespace+'Collection.extend({ model: '+singular+' });\n\n';
  164. }
  165. /**
  166. * Generate backbone code for top level models.
  167. *
  168. * @param {string} namespace (optional)
  169. * @api private
  170. */
  171. function backboneTopLevelModel (namespace, resource, children) {
  172. var singular = namespace + classify(lingo.singularize(resource))
  173. , plural = lingo.pluralize(resource)
  174. , backbone = '';
  175. backbone += 'var '+singular+' = Model.extend({\n'
  176. + ' urlRoot: \'/'+plural+'\'\n';
  177. if (models.hasChildren(resource)) {
  178. backbone += ' , initialize: function () {\n';
  179. models.getChildren(resource).forEach(function (em) {
  180. backbone += ' this.'+em.attribute+' = new '
  181. + namespace + classify(em.singular) + 'Collection;\n'
  182. + ' this.'+em.attribute+'.url = \'/'+plural
  183. + '/\' + this.id + \'/'+em.plural+'\'\n';
  184. });
  185. backbone += ' this.pullEmbedded();\n'
  186. + ' }\n';
  187. }
  188. backbone += '});\n\n';
  189. backbone += 'var ' + singular + 'Collection = Collection.extend({\n'
  190. + ' model: ' + singular + '\n'
  191. + ' , url: \'/' + plural + '\'\n'
  192. + '});\n\n';
  193. return backbone;
  194. }