UNPKG

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