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 |
|
8 | var util = require('./util');
|
9 |
|
10 | var inflect = module.exports;
|
11 |
|
12 | // Import [inflections](inflections.html) instance
|
13 | inflect.inflections = require('./inflections')
|
14 |
|
15 | // Gives easy access to add inflections to this class
|
16 | inflect.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"
|
34 | inflect.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"
|
61 | inflect.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"
|
73 | inflect.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"
|
81 | inflect.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"
|
92 | inflect.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"
|
108 | inflect.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
|
131 | inflect.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"
|
144 | inflect.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"
|
166 | inflect.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"
|
186 | inflect.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"
|
205 | inflect.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"
|
218 | inflect.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"
|
231 | inflect.classify = function (table_name) {
|
232 | return inflect.camelize(inflect.singularize(util.string.gsub(table_name, /.*\./, '')));
|
233 | }
|