├── .gitignore ├── .jscsrc ├── .tm_properties ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── bower.json ├── gulpfile.js ├── index.js ├── lib └── underscore.inflection.js ├── package-lock.json ├── package.json ├── script └── hook.sh └── test ├── gsub.js ├── index.html ├── inflector.js ├── irregular.js ├── ordinalize.js ├── plural.js ├── pluralize.js ├── setup.js ├── singular.js ├── singularize.js ├── titleize.js └── uncountable.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by http://www.gitignore.io 2 | 3 | ### Node ### 4 | # Logs 5 | logs 6 | *.log 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | 13 | # Directory for bower dependencies 14 | bower_components 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 23 | .grunt 24 | 25 | # Compiled binary addons (http://nodejs.org/api/addons.html) 26 | build/Release 27 | 28 | # Dependency directory 29 | # Commenting this out is preferred by some people, see 30 | # https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 31 | node_modules 32 | 33 | # Users Environment Variables 34 | .lock-wscript 35 | 36 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "google", 3 | "requireParenthesesAroundIIFE": true, 4 | "requireSpaceAfterLineComment": true, 5 | "requireSpaceBeforeBlockStatements": true, 6 | "maximumLineLength": 120, 7 | "validateLineBreaks": "LF", 8 | "validateIndentation": 2, 9 | "disallowMultipleVarDecl": true, 10 | "disallowEmptyBlocks": true, 11 | "disallowMultipleLineStrings": true, 12 | "disallowSpacesInsideParentheses": true, 13 | "disallowSpacesInsideArrayBrackets": true, 14 | "disallowSpacesInsideObjectBrackets": true, 15 | "disallowPaddingNewlinesInBlocks": true, 16 | "disallowImplicitTypeConversion": [ 17 | "numeric", 18 | "boolean", 19 | "binary", 20 | "string" 21 | ], 22 | "disallowKeywords": [ 23 | "with" 24 | ], 25 | "excludeFiles": [ 26 | "node_modules/**" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /.tm_properties: -------------------------------------------------------------------------------- 1 | excludeInFolderSearch = "{$excludeInFolderSearch,node_modules}" 2 | 3 | excludeInFileChooser = "{$excludeInFileChooser,node_modules}" 4 | 5 | # Settings 6 | softWrap = false 7 | softTabs = true 8 | tabSize = 2 9 | include = "{$include,.jscsrc,.gitignore,.travis.yml}" 10 | 11 | [ text.html.markdown ] 12 | softWrap = true 13 | softTabs = false 14 | tabSize = 2 15 | 16 | [ .jscsrc ] 17 | fileType = "source.json" 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: node_js 3 | sudo: false 4 | node_js: 5 | - "5.0" 6 | - "4.0" 7 | - "0.12" 8 | - "0.11" 9 | - "0.10" 10 | before_script: 11 | - npm run jscov 12 | after_script: 13 | - npm run coverage 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Jeremy Ruppel 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | install: .git/hooks/pre-commit 2 | install: .git/hooks/pre-push 3 | 4 | .git/hooks/pre-commit: script/hook.sh 5 | cp $< $@ 6 | 7 | .git/hooks/pre-push: script/hook.sh 8 | cp $< $@ 9 | 10 | release-patch: 11 | $(shell npm bin)/mversion patch -m 12 | git push origin master 13 | git push origin --tags 14 | npm publish 15 | 16 | release-minor: 17 | $(shell npm bin)/mversion minor -m 18 | git push origin master 19 | git push origin --tags 20 | npm publish 21 | 22 | release-major: 23 | $(shell npm bin)/mversion major -m 24 | git push origin master 25 | git push origin --tags 26 | npm publish 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## underscore.inflection 2 | 3 | > [![NPM version][npm-badge]][npm] 4 | > [![Build Status][travis-badge]][travis-ci] 5 | > [![Coverage Status][coveralls-badge]][coveralls] 6 | 7 | **Another javascript inflector?!** 8 | 9 | I'll be the first to say it; this isn't the first port of [ActiveSupport::Inflector][activesupport] to js. Not by a long shot. But I'll definitely take [underscore][underscore] mixins over extending String.prototype or other clunky implementations any day. 10 | 11 | Also, this one has tests! 12 | 13 | Inflections 14 | ----------- 15 | 16 | The methods listed below are the ones you'll be using 99% of the time. 17 | 18 | ### pluralize 19 | 20 | **Signature:** `_.pluralize(word)` 21 | 22 | `pluralize` pluralizes the string passed to it. 23 | 24 | // functional style 25 | _.pluralize('word'); // => 'words' 26 | 27 | // object-oriented style 28 | _('word').pluralize(); // => 'words' 29 | 30 | It also can accept a number as the second parameter. If a number is provided, it will pluralize the word to match the number. 31 | 32 | _('word').pluralize(0); // => 'words' 33 | _('word').pluralize(1); // => 'word' 34 | _('word').pluralize(1.5); // => 'words' 35 | 36 | Optionally, you can pass `true` as a third parameter. If found, this will include the count with the output. 37 | 38 | _('word').pluralize(0, true); // => '0 words' 39 | _('word').pluralize(1, true); // => '1 word' 40 | 41 | ### singularize 42 | 43 | **Signature:** `_.singularize(word)` 44 | 45 | `singularize` returns the singular version of the plural passed to it. 46 | 47 | // functional style 48 | _.singularize('words'); // => 'word' 49 | 50 | // object-oriented style 51 | _('words').singularize(); // => 'word' 52 | 53 | ### gsub 54 | 55 | **Signature:** `_.gsub(word, rule, replacement)` 56 | 57 | `gsub` is a method that is just slightly different than our standard `String#replace`. The main differences are that it matches globally every time, and if no substitution is made it returns `null`. It accepts a string for `word` and `replacement`, and `rule` can be either a string or a regex. 58 | 59 | // functional style 60 | _.gsub('word', /wo/, 'ne'); // => 'nerd' 61 | 62 | // object-oriented style 63 | _('word').gsub(/wo/, 'ne'); // => 'nerd' 64 | 65 | ### ordinalize 66 | 67 | **Signature:** `_.ordinalize(number)` 68 | 69 | `ordinalize` adds an ordinal suffix to `number`. 70 | 71 | _.ordinalize(1); // => '1st' 72 | _.ordinalize("5"); // => '5th' 73 | _.ordinalize(11); // => '11th' 74 | _.ordinalize(1033); // => '1033rd' 75 | _.ordinalize(-15); // => '-15th' 76 | 77 | ### titleize 78 | 79 | **Signature:** `_.titleize( words )` 80 | 81 | `titleize` capitalizes the first letter of each word in the string `words`. It preserves the existing whitespace. 82 | 83 | _.titleize('banana'); // => 'Banana' 84 | _.titleize('banana potato fork'); // => 'Banana Potato Fork' 85 | _.titleize('banana potato\tfork'); // => 'Banana Potato\tFork' 86 | 87 | ## Configuring the Inflector 88 | 89 | Should you ever need to configure the Inflector beyond the defaults, use these methods to do so: 90 | 91 | ### plural 92 | 93 | **Signature:** `_.plural(rule, replacement)` 94 | 95 | `plural` creates a new pluralization rule for the inflector. `rule` can be either a string or a regex. 96 | 97 | // functional style with explicit string 98 | _.plural('axis', 'axes'); 99 | 100 | // object-oriented style with explicit string 101 | _('axis').plural('axes'); 102 | 103 | // functional style with regex 104 | _.plural(/(ax)is$/i, '$1es'); 105 | 106 | // object-oriented style with regex 107 | _(/(ax)is$/i).plural('$1es'); 108 | 109 | ### singular 110 | 111 | **Signature:** `_.singular(rule, replacement)` 112 | 113 | `singular` creates a new singularization rule for the inflector. `rule` can be either a string or a regex. 114 | 115 | // functional style with explicit string 116 | _.singular('data', 'datum'); 117 | 118 | // object-oriented style with explicit string 119 | _('data').singular('datum'); 120 | 121 | // functional style with regex 122 | _.singular(/(t)a$/i, '$1um'); 123 | 124 | // object-oriented style with regex 125 | _(/(t)a$/i).singular('$1um'); 126 | 127 | ### irregular 128 | 129 | **Signature:** `_.irregular(singular, plural)` 130 | 131 | `irregular` is a shortcut method to create both a pluralization and singularization rule for the word at the same time. You must supply both the singular form and the plural form as explicit strings. 132 | 133 | // functional style 134 | _.irregular('haxor', 'hax0rs!'); 135 | 136 | // object-oriented style 137 | _('haxor').irregular('hax0rs!'); 138 | 139 | ### uncountable 140 | 141 | **Signature:** `_.uncountable(word)` 142 | 143 | `uncountable` creates a new uncountable rule for `word`. Uncountable words do not get pluralized or singularized. 144 | 145 | // functional style 146 | _.uncountable('equipment'); 147 | 148 | // object-oriented style 149 | _('equipment').uncountable(); 150 | 151 | ### resetInflections 152 | 153 | **Signature:** `_.resetInflections()` 154 | 155 | `resetInflections` resets the inflector's rules to their initial state, clearing out any custom rules that have been added. 156 | 157 | ## Thanks to... 158 | 159 | The [Rails][rails] team for [ActiveSupport][activesupport] 160 | 161 | The [DocumentCloud][documentcloud] team for [underscore.js][underscore] 162 | 163 | These other Inflector implementations: 164 | 165 | - [active-support-for-javascript](http://code.google.com/p/active-support-for-javascript/) 166 | - [inflection-js](http://code.google.com/p/inflection-js/) 167 | - [Javascript Rails-like inflector](http://snippets.dzone.com/posts/show/3205) 168 | 169 | Though no code was taken directly from them, they deserve plenty of props for doing it before me. If underscore isn't your thing, check them out! 170 | 171 | ## Contributors 172 | 173 | ``` 174 | 66 Jeremy Ruppel 175 | 7 Landon Schropp 176 | 2 Johnathon Sanders 177 | 2 Seggy Umboh 178 | 1 Sam Dornan 179 | 1 Shane Riley 180 | 1 bramski 181 | 1 maratfakhreev 182 | 1 Daniel Perez 183 | 1 trevor 184 | 1 Dayton Nolan 185 | 1 Joseph Spens 186 | 1 Kris Neuharth 187 | ``` 188 | 189 | ## License 190 | 191 | [MIT License][LICENSE] 192 | 193 | [npm]: http://badge.fury.io/js/underscore.inflection 194 | [npm-badge]: https://badge.fury.io/js/underscore.inflection.svg 195 | [travis-ci]: https://travis-ci.org/jeremyruppel/underscore.inflection 196 | [travis-badge]: https://travis-ci.org/jeremyruppel/underscore.inflection.svg?branch=master 197 | [coveralls]: https://coveralls.io/r/jeremyruppel/underscore.inflection?branch=master 198 | [coveralls-badge]: https://img.shields.io/coveralls/jeremyruppel/underscore.inflection.svg 199 | [rails]: https://github.com/rails/rails 200 | [activesupport]: https://github.com/rails/rails/tree/master/activesupport 201 | [underscore]: http://documentcloud.github.com/underscore/ 202 | [documentcloud]: http://www.documentcloud.org/home 203 | [LICENSE]: https://github.com/jeremyruppel/underscore.inflection/blob/master/LICENSE 204 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "underscore.inflection", 3 | "version": "1.3.2", 4 | "dependencies": { 5 | "underscore": "^1.9.1" 6 | }, 7 | "main": "lib/underscore.inflection.js", 8 | "ignore": [ 9 | "lib-cov", 10 | "test" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var jscs = require('gulp-jscs'); 3 | var jscov = require('gulp-jscoverage'); 4 | var contribs = require('gulp-contribs'); 5 | 6 | gulp.task('jscs', function() { 7 | return gulp.src([ 8 | 'lib/*.js', 9 | 'test/*.js' 10 | ]).pipe(jscs()); 11 | }); 12 | 13 | gulp.task('jscov', function() { 14 | return gulp.src('lib/*.js') 15 | .pipe(jscov('index.js')) 16 | .pipe(gulp.dest('./lib-cov')); 17 | }); 18 | 19 | gulp.task('contribs', function() { 20 | return gulp.src('README.md') 21 | .pipe(contribs()) 22 | .pipe(gulp.dest('./')); 23 | }); 24 | 25 | gulp.task('docs', ['contribs']); 26 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = process.env.JSCOV 2 | ? require('./lib-cov') 3 | : require('./lib/underscore.inflection.js'); 4 | -------------------------------------------------------------------------------- /lib/underscore.inflection.js: -------------------------------------------------------------------------------- 1 | // Underscore.inflection.js 2 | // (c) 2014 Jeremy Ruppel 3 | // Underscore.inflection is freely distributable under the MIT license. 4 | // Portions of Underscore.inflection are inspired or borrowed from ActiveSupport 5 | // Version 1.0.0 6 | 7 | (function(root, factory) { 8 | if (typeof define === 'function' && define.amd) { 9 | // AMD. Register as an anonymous module. 10 | define(['underscore'], factory); 11 | } else if (typeof require === 'function' && typeof exports === 'object') { 12 | // CommonJS 13 | module.exports = factory(require('underscore')); 14 | } else { 15 | // Browser globals (root is window) 16 | factory(root._); 17 | } 18 | })(this, function(_, undefined) { 19 | var plurals = []; 20 | var singulars = []; 21 | var uncountables = []; 22 | 23 | /** 24 | * Inflector 25 | */ 26 | var inflector = { 27 | 28 | /** 29 | * `gsub` is a method that is just slightly different than our 30 | * standard `String#replace`. The main differences are that it 31 | * matches globally every time, and if no substitution is made 32 | * it returns `null`. It accepts a string for `word` and 33 | * `replacement`, and `rule` can be either a string or a regex. 34 | */ 35 | gsub: function(word, rule, replacement) { 36 | var pattern = new RegExp(rule.source || rule, 'gi'); 37 | 38 | return pattern.test(word) ? word.replace(pattern, replacement) : null; 39 | }, 40 | 41 | /** 42 | * `plural` creates a new pluralization rule for the inflector. 43 | * `rule` can be either a string or a regex. 44 | */ 45 | plural: function(rule, replacement) { 46 | plurals.unshift([rule, replacement]); 47 | }, 48 | 49 | /** 50 | * Pluralizes the string passed to it. It also can accept a 51 | * number as the second parameter. If a number is provided, 52 | * it will pluralize the word to match the number. Optionally, 53 | * you can pass `true` as a third parameter. If found, this 54 | * will include the count with the output. 55 | */ 56 | pluralize: function(word, count, includeNumber) { 57 | var result; 58 | 59 | if (count !== undefined) { 60 | count = parseFloat(count); 61 | result = (count === 1) ? this.singularize(word) : this.pluralize(word); 62 | result = (includeNumber) ? [count, result].join(' ') : result; 63 | } else { 64 | if (_(uncountables).include(word)) { 65 | return word; 66 | } 67 | 68 | result = word; 69 | 70 | _(plurals).detect(function(rule) { 71 | var gsub = this.gsub(word, rule[0], rule[1]); 72 | 73 | return gsub ? (result = gsub) : false; 74 | }, 75 | this); 76 | } 77 | 78 | return result; 79 | }, 80 | 81 | /** 82 | * `singular` creates a new singularization rule for the 83 | * inflector. `rule` can be either a string or a regex. 84 | */ 85 | singular: function(rule, replacement) { 86 | singulars.unshift([rule, replacement]); 87 | }, 88 | 89 | /** 90 | * `singularize` returns the singular version of the plural 91 | * passed to it. 92 | */ 93 | singularize: function(word) { 94 | if (_(uncountables).include(word)) { 95 | return word; 96 | } 97 | 98 | var result = word; 99 | 100 | _(singulars).detect(function(rule) { 101 | var gsub = this.gsub(word, rule[0], rule[1]); 102 | 103 | return gsub ? (result = gsub) : false; 104 | }, 105 | this); 106 | 107 | return result; 108 | }, 109 | 110 | /** 111 | * `irregular` is a shortcut method to create both a 112 | * pluralization and singularization rule for the word at 113 | * the same time. You must supply both the singular form 114 | * and the plural form as explicit strings. 115 | */ 116 | irregular: function(singular, plural) { 117 | this.plural('\\b' + singular + '\\b', plural); 118 | this.singular('\\b' + plural + '\\b', singular); 119 | }, 120 | 121 | /** 122 | * `uncountable` creates a new uncountable rule for `word`. 123 | * Uncountable words do not get pluralized or singularized. 124 | */ 125 | uncountable: function(word) { 126 | uncountables.unshift(word); 127 | }, 128 | 129 | /** 130 | * `ordinalize` adds an ordinal suffix to `number`. 131 | */ 132 | ordinalize: function(number) { 133 | if (isNaN(number)) { 134 | return number; 135 | } 136 | 137 | number = number.toString(); 138 | var lastDigit = number.slice(-1); 139 | var lastTwoDigits = number.slice(-2); 140 | 141 | if (lastTwoDigits === '11' || lastTwoDigits === '12' || lastTwoDigits === '13') { 142 | return number + 'th'; 143 | } 144 | 145 | switch (lastDigit) { 146 | case '1': 147 | return number + 'st'; 148 | case '2': 149 | return number + 'nd'; 150 | case '3': 151 | return number + 'rd'; 152 | default: 153 | return number + 'th'; 154 | } 155 | }, 156 | 157 | /** 158 | * `titleize` capitalizes the first letter of each word in 159 | * the string `words`. It preserves the existing whitespace. 160 | */ 161 | titleize: function(words) { 162 | if (typeof words !== 'string') { 163 | return words; 164 | } 165 | 166 | return words.replace(/\S+/g, function(word) { 167 | return word.charAt(0).toUpperCase() + word.slice(1); 168 | }); 169 | }, 170 | 171 | /** 172 | * Resets the inflector's rules to their initial state, 173 | * clearing out any custom rules that have been added. 174 | */ 175 | resetInflections: function() { 176 | plurals = []; 177 | singulars = []; 178 | uncountables = []; 179 | 180 | this.plural(/$/, 's'); 181 | this.plural(/s$/, 's'); 182 | this.plural(/(ax|test)is$/, '$1es'); 183 | this.plural(/(octop|vir)us$/, '$1i'); 184 | this.plural(/(octop|vir)i$/, '$1i'); 185 | this.plural(/(alias|status)$/, '$1es'); 186 | this.plural(/(bu)s$/, '$1ses'); 187 | this.plural(/(buffal|tomat)o$/, '$1oes'); 188 | this.plural(/([ti])um$/, '$1a'); 189 | this.plural(/([ti])a$/, '$1a'); 190 | this.plural(/sis$/, 'ses'); 191 | this.plural(/(?:([^f])fe|([lr])?f)$/, '$1$2ves'); 192 | this.plural(/(hive)$/, '$1s'); 193 | this.plural(/([^aeiouy]|qu)y$/, '$1ies'); 194 | this.plural(/(x|ch|ss|sh)$/, '$1es'); 195 | this.plural(/(matr|vert|ind)(?:ix|ex)$/, '$1ices'); 196 | this.plural(/([m|l])ouse$/, '$1ice'); 197 | this.plural(/([m|l])ice$/, '$1ice'); 198 | this.plural(/^(ox)$/, '$1en'); 199 | this.plural(/^(oxen)$/, '$1'); 200 | this.plural(/(quiz)$/, '$1zes'); 201 | 202 | this.singular(/s$/, ''); 203 | this.singular(/(n)ews$/, '$1ews'); 204 | this.singular(/([ti])a$/, '$1um'); 205 | this.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/, '$1$2sis'); 206 | this.singular(/(^analy)ses$/, '$1sis'); 207 | this.singular(/([^f])ves$/, '$1fe'); 208 | this.singular(/(hive)s$/, '$1'); 209 | this.singular(/(tive)s$/, '$1'); 210 | this.singular(/([lr])ves$/, '$1f'); 211 | this.singular(/([^aeiouy]|qu)ies$/, '$1y'); 212 | this.singular(/(s)eries$/, '$1eries'); 213 | this.singular(/(m)ovies$/, '$1ovie'); 214 | this.singular(/(ss)$/, '$1'); 215 | this.singular(/(x|ch|ss|sh)es$/, '$1'); 216 | this.singular(/([m|l])ice$/, '$1ouse'); 217 | this.singular(/(bus)es$/, '$1'); 218 | this.singular(/(o)es$/, '$1'); 219 | this.singular(/(shoe)s$/, '$1'); 220 | this.singular(/(cris|ax|test)es$/, '$1is'); 221 | this.singular(/(octop|vir)i$/, '$1us'); 222 | this.singular(/(alias|status)es$/, '$1'); 223 | this.singular(/^(ox)en/, '$1'); 224 | this.singular(/(vert|ind)ices$/, '$1ex'); 225 | this.singular(/(matr)ices$/, '$1ix'); 226 | this.singular(/(quiz)zes$/, '$1'); 227 | this.singular(/(database)s$/, '$1'); 228 | 229 | this.irregular('person', 'people'); 230 | this.irregular('man', 'men'); 231 | this.irregular('child', 'children'); 232 | this.irregular('sex', 'sexes'); 233 | this.irregular('move', 'moves'); 234 | this.irregular('cow', 'kine'); 235 | 236 | this.uncountable('equipment'); 237 | this.uncountable('information'); 238 | this.uncountable('rice'); 239 | this.uncountable('money'); 240 | this.uncountable('species'); 241 | this.uncountable('series'); 242 | this.uncountable('fish'); 243 | this.uncountable('sheep'); 244 | this.uncountable('jeans'); 245 | 246 | return this; 247 | } 248 | }; 249 | 250 | /** 251 | * Underscore integration 252 | */ 253 | _.mixin(inflector.resetInflections()); 254 | 255 | return inflector; 256 | }); 257 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "underscore.inflection", 3 | "version": "1.3.3", 4 | "description": "ActiveSupport::Inflector, for underscore!", 5 | "main": "index.js", 6 | "browser": "lib/underscore.inflection.js", 7 | "engines": { 8 | "node": ">0.10.0" 9 | }, 10 | "scripts": { 11 | "test": "mocha", 12 | "browser": "open test/index.html", 13 | "coverage": "JSCOV=1 mocha -R mocha-lcov-reporter | coveralls", 14 | "jscov": "gulp jscov", 15 | "jscs": "gulp jscs", 16 | "docs": "gulp docs" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git://github.com/jeremyruppel/underscore.inflection.git" 21 | }, 22 | "keywords": [ 23 | "underscore", 24 | "inflection", 25 | "inflector", 26 | "activesupport", 27 | "plural", 28 | "pluralize" 29 | ], 30 | "author": "Jeremy Ruppel", 31 | "license": "MIT", 32 | "bugs": { 33 | "url": "https://github.com/jeremyruppel/underscore.inflection/issues" 34 | }, 35 | "homepage": "https://github.com/jeremyruppel/underscore.inflection", 36 | "devDependencies": { 37 | "chai": "~2.3.0", 38 | "coveralls": "~2.11.2", 39 | "gulp": "~3.8.11", 40 | "gulp-contribs": "~0.0.2", 41 | "gulp-jscoverage": "~0.1.0", 42 | "gulp-jscs": "~1.6.0", 43 | "jscoverage": "~0.5.9", 44 | "mocha": "~2.2.5", 45 | "mocha-lcov-reporter": "~0.0.2", 46 | "mversion": "^1.10.1", 47 | "underscore": "^1.9.1" 48 | }, 49 | "dependencies": {} 50 | } 51 | -------------------------------------------------------------------------------- /script/hook.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ex 4 | 5 | npm run jscs 6 | npm test 7 | 8 | node bower.json 9 | node package.json 10 | -------------------------------------------------------------------------------- /test/gsub.js: -------------------------------------------------------------------------------- 1 | describe('#gsub', function() { 2 | describe('with a regex', function() { 3 | it('replaces one instance of the match', function() { 4 | expect(_.gsub('word', /wo/, 'ne')).to.equal('nerd'); 5 | }); 6 | it('replaces two instances of the match', function() { 7 | expect(_.gsub('word word', /wo/, 'ne')).to.equal('nerd nerd'); 8 | }); 9 | it('returns null if no match', function() { 10 | expect(_.gsub('word', /zz/, 'ne')).to.be.null; 11 | }); 12 | }); 13 | describe('with a string', function() { 14 | it('replaces one instance of the match', function() { 15 | expect(_.gsub('word', 'wo', 'ne')).to.equal('nerd'); 16 | }); 17 | it('replaces two instances of the match', function() { 18 | expect(_.gsub('word word', 'wo', 'ne')).to.equal('nerd nerd'); 19 | }); 20 | it('returns null if no match', function() { 21 | expect(_.gsub('word', 'zz', 'ne')).to.be.null; 22 | }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |