// The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without, // and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept // in inflections.coffee // // If you discover an incorrect inflection and require it for your application, you'll need // to correct it yourself (explained below). var util = require('./util'); var inflect = module.exports; // Import [inflections](inflections.html) instance inflect.inflections = require('./inflections'); // Gives easy access to add inflections to this class inflect.inflect = function (fn) { fn(inflect.inflections); }; // By default, _camelize_ converts strings to UpperCamelCase. If the argument to _camelize_ // is set to _false_ then _camelize_ produces lowerCamelCase. // // _camelize_ will also convert '/' to '.' which is useful for converting paths to namespaces. // // "bullet_record".camelize() // => "BulletRecord" // "bullet_record".camelize(false) // => "bulletRecord" // "bullet_record/errors".camelize() // => "BulletRecord.Errors" // "bullet_record/errors".camelize(false) // => "bulletRecord.Errors" // // As a rule of thumb you can think of _camelize_ as the inverse of _underscore_, // though there are cases where that does not hold: // // "SSLError".underscore.camelize // => "SslError" inflect.camelize = function (lower_case_and_underscored_word, first_letter_in_uppercase) { var result; if (first_letter_in_uppercase == null) first_letter_in_uppercase = true; result = util.string.gsub(lower_case_and_underscored_word, /\/(.?)/, function ($) { return '.' + util.string.upcase($[1]); }); result = util.string.gsub(result, /(?:_)(.)/, function ($) { return util.string.upcase($[1]); }); if (first_letter_in_uppercase) { return util.string.upcase(result); } else { return util.string.downcase(result); } }; // Makes an underscored, lowercase form from the expression in the string. // // Changes '.' to '/' to convert namespaces to paths. // // "BulletRecord".underscore() // => "bullet_record" // "BulletRecord.Errors".underscore() // => "bullet_record/errors" // // As a rule of thumb you can think of +underscore+ as the inverse of +camelize+, // though there are cases where that does not hold: // // "SSLError".underscore().camelize() // => "SslError" inflect.underscore = function (camel_cased_word) { var self; self = util.string.gsub(camel_cased_word, /\./, '/'); self = util.string.gsub(self, /([A-Z])([A-Z][a-z])/, '$1_$2'); self = util.string.gsub(self, /([a-z\d])([A-Z])/, '$1_$2'); self = util.string.gsub(self, /-/, '_'); return self.toLowerCase(); }; // Replaces underscores with dashes in the string. // // "puni_puni".dasherize() // => "puni-puni" inflect.dasherize = function (underscored_word) { return util.string.gsub(underscored_word, /_/, '-'); }; // Removes the module part from the expression in the string. // // "BulletRecord.String.Inflections".demodulize() // => "Inflections" // "Inflections".demodulize() // => "Inflections" inflect.demodulize = function (class_name_in_module) { return util.string.gsub(class_name_in_module, /^.*\./, ''); }; // Creates a foreign key name from a class name. // _separate_class_name_and_id_with_underscore_ sets whether // the method should put '_' between the name and 'id'. // // "Message".foreign_key() // => "message_id" // "Message".foreign_key(false) // => "messageid" // "Admin::Post".foreign_key() // => "post_id" inflect.foreign_key = function (class_name, separate_class_name_and_id_with_underscore) { if (separate_class_name_and_id_with_underscore == null) { separate_class_name_and_id_with_underscore = true; } return ( inflect.underscore(inflect.demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? '_id' : 'id') ); }; // Turns a number into an ordinal string used to denote the position in an // ordered sequence such as 1st, 2nd, 3rd, 4th. // // ordinalize(1) // => "1st" // ordinalize(2) // => "2nd" // ordinalize(1002) // => "1002nd" // ordinalize(1003) // => "1003rd" // ordinalize(-11) // => "-11th" // ordinalize(-1021) // => "-1021st" inflect.ordinalize = function (number) { var _ref; number = parseInt(number); if ((_ref = Math.abs(number) % 100) === 11 || _ref === 12 || _ref === 13) { return '' + number + 'th'; } else { switch (Math.abs(number) % 10) { case 1: return '' + number + 'st'; case 2: return '' + number + 'nd'; case 3: return '' + number + 'rd'; default: return '' + number + 'th'; } } }; // Checks a given word for uncountability // // "money".uncountability() // => true // "my money".uncountability() // => true inflect.uncountability = function (word) { return inflect.inflections.uncountables.some(function (ele, ind, arr) { return word.match(new RegExp('(\\b|_)' + ele + '$', 'i')) != null; }); }; // Returns the plural form of the word in the string. // // "post".pluralize() // => "posts" // "octopus".pluralize() // => "octopi" // "sheep".pluralize() // => "sheep" // "words".pluralize() // => "words" // "CamelOctopus".pluralize() // => "CamelOctopi" inflect.pluralize = function (word) { var plural, result; result = word; if (word === '' || inflect.uncountability(word)) { return result; } else { for (var i = 0; i < inflect.inflections.plurals.length; i++) { plural = inflect.inflections.plurals[i]; result = util.string.gsub(result, plural[0], plural[1]); if (word.match(plural[0]) != null) break; } return result; } }; // The reverse of _pluralize_, returns the singular form of a word in a string. // // "posts".singularize() // => "post" // "octopi".singularize() // => "octopus" // "sheep".singularize() // => "sheep" // "word".singularize() // => "word" // "CamelOctopi".singularize() // => "CamelOctopus" inflect.singularize = function (word) { var result, singular; result = word; if (word === '' || inflect.uncountability(word)) { return result; } else { for (var i = 0; i < inflect.inflections.singulars.length; i++) { singular = inflect.inflections.singulars[i]; result = util.string.gsub(result, singular[0], singular[1]); if (word.match(singular[0])) break; } return result; } }; // Capitalizes the first word and turns underscores into spaces and strips a // trailing "_id", if any. Like _titleize_, this is meant for creating pretty output. // // "employee_salary".humanize() // => "Employee salary" // "author_id".humanize() // => "Author" inflect.humanize = function (lower_case_and_underscored_word) { var human, result; result = lower_case_and_underscored_word; for (var i = 0; i < inflect.inflections.humans.length; i++) { human = inflect.inflections.humans[i]; result = util.string.gsub(result, human[0], human[1]); } result = util.string.gsub(result, /_id$/, ''); result = util.string.gsub(result, /_/, ' '); return util.string.capitalize(result, true); }; // Capitalizes all the words and replaces some characters in the string to create // a nicer looking title. _titleize_ is meant for creating pretty output. It is not // used in the Bullet internals. // // // "man from the boondocks".titleize() // => "Man From The Boondocks" // "x-men: the last stand".titleize() // => "X Men: The Last Stand" inflect.titleize = function (word) { var self; self = inflect.humanize(inflect.underscore(word)); return util.string.capitalize(self); }; // Create the name of a table like Bullet does for models to table names. This method // uses the _pluralize_ method on the last word in the string. // // "RawScaledScorer".tableize() // => "raw_scaled_scorers" // "egg_and_ham".tableize() // => "egg_and_hams" // "fancyCategory".tableize() // => "fancy_categories" inflect.tableize = function (class_name) { return inflect.pluralize(inflect.underscore(class_name)); }; // Create a class name from a plural table name like Bullet does for table names to models. // Note that this returns a string and not a Class. // // "egg_and_hams".classify() // => "EggAndHam" // "posts".classify() // => "Post" // // Singular names are not handled correctly: // // "business".classify() // => "Busines" inflect.classify = function (table_name) { return inflect.camelize(inflect.singularize(util.string.gsub(table_name, /^.*\./, ''))); };