methods.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. // The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without,
  2. // and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept
  3. // in inflections.coffee
  4. //
  5. // If you discover an incorrect inflection and require it for your application, you'll need
  6. // to correct it yourself (explained below).
  7. var util = require('./util');
  8. var inflect = module.exports;
  9. // Import [inflections](inflections.html) instance
  10. inflect.inflections = require('./inflections');
  11. // Gives easy access to add inflections to this class
  12. inflect.inflect = function (fn) {
  13. fn(inflect.inflections);
  14. };
  15. // By default, _camelize_ converts strings to UpperCamelCase. If the argument to _camelize_
  16. // is set to _false_ then _camelize_ produces lowerCamelCase.
  17. //
  18. // _camelize_ will also convert '/' to '.' which is useful for converting paths to namespaces.
  19. //
  20. // "bullet_record".camelize() // => "BulletRecord"
  21. // "bullet_record".camelize(false) // => "bulletRecord"
  22. // "bullet_record/errors".camelize() // => "BulletRecord.Errors"
  23. // "bullet_record/errors".camelize(false) // => "bulletRecord.Errors"
  24. //
  25. // As a rule of thumb you can think of _camelize_ as the inverse of _underscore_,
  26. // though there are cases where that does not hold:
  27. //
  28. // "SSLError".underscore.camelize // => "SslError"
  29. inflect.camelize = function (lower_case_and_underscored_word, first_letter_in_uppercase) {
  30. var result;
  31. if (first_letter_in_uppercase == null) first_letter_in_uppercase = true;
  32. result = util.string.gsub(lower_case_and_underscored_word, /\/(.?)/, function ($) {
  33. return '.' + util.string.upcase($[1]);
  34. });
  35. result = util.string.gsub(result, /(?:_)(.)/, function ($) {
  36. return util.string.upcase($[1]);
  37. });
  38. if (first_letter_in_uppercase) {
  39. return util.string.upcase(result);
  40. } else {
  41. return util.string.downcase(result);
  42. }
  43. };
  44. // Makes an underscored, lowercase form from the expression in the string.
  45. //
  46. // Changes '.' to '/' to convert namespaces to paths.
  47. //
  48. // "BulletRecord".underscore() // => "bullet_record"
  49. // "BulletRecord.Errors".underscore() // => "bullet_record/errors"
  50. //
  51. // As a rule of thumb you can think of +underscore+ as the inverse of +camelize+,
  52. // though there are cases where that does not hold:
  53. //
  54. // "SSLError".underscore().camelize() // => "SslError"
  55. inflect.underscore = function (camel_cased_word) {
  56. var self;
  57. self = util.string.gsub(camel_cased_word, /\./, '/');
  58. self = util.string.gsub(self, /([A-Z])([A-Z][a-z])/, '$1_$2');
  59. self = util.string.gsub(self, /([a-z\d])([A-Z])/, '$1_$2');
  60. self = util.string.gsub(self, /-/, '_');
  61. return self.toLowerCase();
  62. };
  63. // Replaces underscores with dashes in the string.
  64. //
  65. // "puni_puni".dasherize() // => "puni-puni"
  66. inflect.dasherize = function (underscored_word) {
  67. return util.string.gsub(underscored_word, /_/, '-');
  68. };
  69. // Removes the module part from the expression in the string.
  70. //
  71. // "BulletRecord.String.Inflections".demodulize() // => "Inflections"
  72. // "Inflections".demodulize() // => "Inflections"
  73. inflect.demodulize = function (class_name_in_module) {
  74. return util.string.gsub(class_name_in_module, /^.*\./, '');
  75. };
  76. // Creates a foreign key name from a class name.
  77. // _separate_class_name_and_id_with_underscore_ sets whether
  78. // the method should put '_' between the name and 'id'.
  79. //
  80. // "Message".foreign_key() // => "message_id"
  81. // "Message".foreign_key(false) // => "messageid"
  82. // "Admin::Post".foreign_key() // => "post_id"
  83. inflect.foreign_key = function (class_name, separate_class_name_and_id_with_underscore) {
  84. if (separate_class_name_and_id_with_underscore == null) {
  85. separate_class_name_and_id_with_underscore = true;
  86. }
  87. return (
  88. inflect.underscore(inflect.demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? '_id' : 'id')
  89. );
  90. };
  91. // Turns a number into an ordinal string used to denote the position in an
  92. // ordered sequence such as 1st, 2nd, 3rd, 4th.
  93. //
  94. // ordinalize(1) // => "1st"
  95. // ordinalize(2) // => "2nd"
  96. // ordinalize(1002) // => "1002nd"
  97. // ordinalize(1003) // => "1003rd"
  98. // ordinalize(-11) // => "-11th"
  99. // ordinalize(-1021) // => "-1021st"
  100. inflect.ordinalize = function (number) {
  101. var _ref;
  102. number = parseInt(number);
  103. if ((_ref = Math.abs(number) % 100) === 11 || _ref === 12 || _ref === 13) {
  104. return '' + number + 'th';
  105. } else {
  106. switch (Math.abs(number) % 10) {
  107. case 1:
  108. return '' + number + 'st';
  109. case 2:
  110. return '' + number + 'nd';
  111. case 3:
  112. return '' + number + 'rd';
  113. default:
  114. return '' + number + 'th';
  115. }
  116. }
  117. };
  118. // Checks a given word for uncountability
  119. //
  120. // "money".uncountability() // => true
  121. // "my money".uncountability() // => true
  122. inflect.uncountability = function (word) {
  123. return inflect.inflections.uncountables.some(function (ele, ind, arr) {
  124. return word.match(new RegExp('(\\b|_)' + ele + '$', 'i')) != null;
  125. });
  126. };
  127. // Returns the plural form of the word in the string.
  128. //
  129. // "post".pluralize() // => "posts"
  130. // "octopus".pluralize() // => "octopi"
  131. // "sheep".pluralize() // => "sheep"
  132. // "words".pluralize() // => "words"
  133. // "CamelOctopus".pluralize() // => "CamelOctopi"
  134. inflect.pluralize = function (word) {
  135. var plural, result;
  136. result = word;
  137. if (word === '' || inflect.uncountability(word)) {
  138. return result;
  139. } else {
  140. for (var i = 0; i < inflect.inflections.plurals.length; i++) {
  141. plural = inflect.inflections.plurals[i];
  142. result = util.string.gsub(result, plural[0], plural[1]);
  143. if (word.match(plural[0]) != null) break;
  144. }
  145. return result;
  146. }
  147. };
  148. // The reverse of _pluralize_, returns the singular form of a word in a string.
  149. //
  150. // "posts".singularize() // => "post"
  151. // "octopi".singularize() // => "octopus"
  152. // "sheep".singularize() // => "sheep"
  153. // "word".singularize() // => "word"
  154. // "CamelOctopi".singularize() // => "CamelOctopus"
  155. inflect.singularize = function (word) {
  156. var result, singular;
  157. result = word;
  158. if (word === '' || inflect.uncountability(word)) {
  159. return result;
  160. } else {
  161. for (var i = 0; i < inflect.inflections.singulars.length; i++) {
  162. singular = inflect.inflections.singulars[i];
  163. result = util.string.gsub(result, singular[0], singular[1]);
  164. if (word.match(singular[0])) break;
  165. }
  166. return result;
  167. }
  168. };
  169. // Capitalizes the first word and turns underscores into spaces and strips a
  170. // trailing "_id", if any. Like _titleize_, this is meant for creating pretty output.
  171. //
  172. // "employee_salary".humanize() // => "Employee salary"
  173. // "author_id".humanize() // => "Author"
  174. inflect.humanize = function (lower_case_and_underscored_word) {
  175. var human, result;
  176. result = lower_case_and_underscored_word;
  177. for (var i = 0; i < inflect.inflections.humans.length; i++) {
  178. human = inflect.inflections.humans[i];
  179. result = util.string.gsub(result, human[0], human[1]);
  180. }
  181. result = util.string.gsub(result, /_id$/, '');
  182. result = util.string.gsub(result, /_/, ' ');
  183. return util.string.capitalize(result, true);
  184. };
  185. // Capitalizes all the words and replaces some characters in the string to create
  186. // a nicer looking title. _titleize_ is meant for creating pretty output. It is not
  187. // used in the Bullet internals.
  188. //
  189. //
  190. // "man from the boondocks".titleize() // => "Man From The Boondocks"
  191. // "x-men: the last stand".titleize() // => "X Men: The Last Stand"
  192. inflect.titleize = function (word) {
  193. var self;
  194. self = inflect.humanize(inflect.underscore(word));
  195. return util.string.capitalize(self);
  196. };
  197. // Create the name of a table like Bullet does for models to table names. This method
  198. // uses the _pluralize_ method on the last word in the string.
  199. //
  200. // "RawScaledScorer".tableize() // => "raw_scaled_scorers"
  201. // "egg_and_ham".tableize() // => "egg_and_hams"
  202. // "fancyCategory".tableize() // => "fancy_categories"
  203. inflect.tableize = function (class_name) {
  204. return inflect.pluralize(inflect.underscore(class_name));
  205. };
  206. // Create a class name from a plural table name like Bullet does for table names to models.
  207. // Note that this returns a string and not a Class.
  208. //
  209. // "egg_and_hams".classify() // => "EggAndHam"
  210. // "posts".classify() // => "Post"
  211. //
  212. // Singular names are not handled correctly:
  213. //
  214. // "business".classify() // => "Busines"
  215. inflect.classify = function (table_name) {
  216. return inflect.camelize(inflect.singularize(util.string.gsub(table_name, /^.*\./, '')));
  217. };