and we're good. tests passing and everything

This commit is contained in:
deltreey
2015-07-16 01:15:00 -04:00
parent f389e84fd9
commit c9c75a2d2e
3 changed files with 537 additions and 0 deletions

80
README.md Normal file
View File

@@ -0,0 +1,80 @@
# wildstring
Simple String Wildcard Handling
[![build status](https://secure.travis-ci.org/deltreey/wildstring.png)](http://travis-ci.org/deltreey/wildstring)
## Shake it shake it
Installing wildstring is a snap. wildstring has no dependencies, so you don't need anything else to run it. If you want to use tools though, here's some tips on how to install it with popular installers.
#### node.js
``` bash
npm install wildstring
```
then:
``` js
var wildstring = require('wildstring');
```
#### bower
``` bash
bower install wildstring
```
#### html
make sure you rename the `index.js` file to `wildstring.js` when you add it to your project
``` html
<script src="wildstring.js"></script>
```
## Hold me tight
Especially with something that does something new, it's important to see how it works. Below are some examples, but here's a brief explanation as well.
In this explanation, I'll use `*` as my wildcard for simplicity. If you put a wildcard at the beginning, for example `*Thing` then you can match anything or nothing before your string. So your string could be `Wild Thing` or just `Thing` and it would match fine. The same is true for the end. `Wild*` would match `Wild Thing` or just `Wild`. If you want to match text in the middle of the string, it works the same way. `Wild*Thing` matches both `WildThing` and `Wild and crazy Thing`.
``` js
wildstring.match('Test*', 'Testing'); // true, wildcard matches 'ing'
wildstring.match('*ing', 'testing'); // true, wildcard matches 'test'
wildstring.match('Test*', 'Test'); // true, wildcard can match empty strings
wildstring.match('*ing', 'Testing it'); // false, no wildcard do match ' it'
wildstring.match('Test', 'Testing'); // false, no wildcard to match 'ing'
wildstring.match('Test*ing', 'Testing this thing'); // true, matches 'Test' and the end of 'thing', the rest is wildcard matched
wildstring.match('*))))))*', ')))))'); // false, not enough parenthesis
```
### You make my heart sing
You can use wildstring for string interpolation, which makes for an easier interface to parse data from users who maybe don't know regular expressions.
``` js
wildstring.replace('I * node.*', [ 'love', 'js' ]); // 'I love node.js'
wildstring.replace('I * node.*', 'script'); // 'I script node.script' * this behavior is the same as "I * node.*".replace("*", "script") and actually uses that method
wildstring.replace('I * node.*', [ 'love' ]); // THROMS AN ERROR, wildcard count and number of strings to insert must match
wildstring.replace('*/*/*', [ new Date.getMonth() + 1, new Date.getDate(), new Date.getFullYear]);
// 7/15/2015 (or whatever day it is), probably better to learn the js date parser though
```
### You make everything, groovy
You can use your wildcards with wildstring, so you can wildstring everything. You can even turn off case sensitive matching if you want.
``` js
wildstring.wildcard = 'stuff';
wildstring.match('Test stuff', 'Test wild'); // true, wildcard 'stuff' matches 'wild'
wildstring.replace('stuff stuff', [ 'WILD', 'thing' ]); // 'WILD thing'
// turn off case sensitive matching
wildstring.caseSensitive = false;
wildstring.match('tEsT', 'TeSt'); // true, 'test' matches 'test'
```
## I think I love you
If you want to contribute to wildstring, it's really easy. Just make sure you have [nodejs](https://nodejs.org/) installed and do the following.
``` bash
git clone https://github.com/deltreey/wildstring
# npm install -g grunt-cli # if you don't have it
npm install
grunt
```
grunt will run all the tests and jshint, so just make sure it passes before submitting a pull request

147
index.js Normal file
View File

@@ -0,0 +1,147 @@
'use strict';
var wildstring = {
wildcard: '*',
caseSensitive: true
};
function checkRollbackStrings (rollbackStrings, patternSubstrings) {
for (var s = 0; s < rollbackStrings.length; ++s) {
var currentString = rollbackStrings[s].string; // starting with the rolled back string
var patternIndex = rollbackStrings[s].index;
while (patternIndex < patternSubstrings.length) {
if (currentString.indexOf(patternSubstrings[patternIndex]) === -1) {
break;
}
var testString = currentString.substr(1); //remove just one char to retest
rollbackStrings.push({ string: testString, index: patternIndex });
if (testString.indexOf(patternSubstrings[patternIndex]) === -1) {
rollbackStrings.pop();
break;
}
currentString = currentString.substr(
currentString.indexOf(patternSubstrings[patternIndex]) + patternSubstrings[patternIndex].length
);
patternIndex++;
while (patternSubstrings[patternIndex] === '') {
patternIndex++;
}
if (patternIndex >= patternSubstrings.length) {
if (patternSubstrings[patternSubstrings.length - 1] !== '' &&
currentString.length > 0) {
// not ending with a wildcard, we need to backtrack
break;
}
else {
return true;
}
}
}
}
return false;
}
wildstring.match = function (pattern, string) {
// if there are no wildcards, must be exact
if (pattern.indexOf(wildstring.wildcard) === -1) {
return pattern === string;
}
if (!wildstring.caseSensitive) {
pattern = pattern.toLowerCase();
string = string.toLowerCase();
}
var patternSubstrings = pattern.split(wildstring.wildcard);
var patternIndex = 0;
var currentString = string;
// find pattern beginning
while (patternSubstrings[patternIndex] === '') {
patternIndex++;
// if the pattern is just wildcards, it matches
if (patternIndex === pattern.length) {
return true;
}
}
if (patternIndex === 0 && string.indexOf(patternSubstrings[0]) !== 0) {
// not starting with a wildcard
return false;
}
var rollbackStrings = [];
while (patternIndex < patternSubstrings.length) {
if (currentString.indexOf(patternSubstrings[patternIndex]) === -1) {
return checkRollbackStrings(rollbackStrings, patternSubstrings);
}
// create a queue of strings to roll back and try again if we fail later
var testString = currentString.substr(1); //remove just one char to retest
rollbackStrings.push({ string: testString, index: patternIndex });
if (testString.indexOf(patternSubstrings[patternIndex]) === -1) {
rollbackStrings.pop();
}
currentString = currentString.substr(
currentString.indexOf(patternSubstrings[patternIndex]) + patternSubstrings[patternIndex].length
);
patternIndex++;
while (patternSubstrings[patternIndex] === '') {
patternIndex++;
}
}
if (patternIndex >= patternSubstrings.length &&
patternSubstrings[patternSubstrings.length - 1] !== '' &&
currentString.length > 0) {
// not ending with a wildcard, we need to backtrack
if (currentString === string) { // this string doesn't even match a little
return false;
}
return checkRollbackStrings(rollbackStrings, patternSubstrings);
}
return true;
};
wildstring.replace = function (pattern, strings) {
if (pattern === undefined || strings === undefined) {
throw new Error('wildstring.replace takes the pattern as one parameter and either a string or an array of strings as the second. You didn\'t pass enough parameters.');
}
if (typeof(strings) === typeof('')) {
return pattern.replace(wildstring.wildcard, strings);
}
if (!Array.isArray(strings) || typeof(pattern) !== typeof('')) {
throw new Error('wildstring.replace takes the pattern as one parameter and either a string or an array of strings as the second. Your parameter types are incorrect.');
}
if (pattern.indexOf(wildstring.wildcard) === -1) {
return pattern; // if there are no wildcards, just return the pattern
}
var patternSubstrings = pattern.split(wildstring.wildcard);
if (patternSubstrings.length - 1 !== strings.length) {
var message = 'There are a different number of wildcards than strings to replace them. You have ' +
wildstring.wildcard +' wildcards in "' + wildstring.wildcard + '" and ' + wildstring.wildcard +
' replacement strings.';
throw new Error(wildstring.replace(message, [ patternSubstrings.length - 1, pattern, strings.length ]));
}
var result = '';
for (var s = 0; s < strings.length; ++s) {
result += patternSubstrings[s] + strings[s];
}
return result;
};
if (module) { module.exports = wildstring; }

310
test/test.js Normal file
View File

@@ -0,0 +1,310 @@
'use strict';
var assert = require('assert'),
wildstring = require('../');
describe('wildstring', function() {
it('should create an object and set a default wildcard', function() {
assert.equal(wildstring.wildcard, '*');
});
describe('#match', function() {
it('should match exactly when no wildcard is given', function() {
// Given: a string and a pattern that match
var pattern = 'test',
string = 'test';
// When: we call wildcard.match
var result = wildstring.match(pattern, string);
// Then: we should see that they match
assert.equal(result, true);
});
it('should return false if the string doesn\'t match the pattern and no wildcard is given', function() {
// Given: a string and a pattern that don't match
var pattern = 'test',
string = 'testing';
// When: we call wildcard.match
var result = wildstring.match(pattern, string);
// Then: we should see that they don't match
assert.equal(result, false);
});
it('should return false if the string doesn\'t match the pattern and no wildcard is given, even when shorter', function() {
// Given: a string and a pattern that don't match
var pattern = 'testing',
string = '';
// When: we call wildcard.match
var result = wildstring.match(pattern, string);
// Then: we should see that they don't match
assert.equal(result, false);
});
it('should match everything if the pattern is only wildcards', function() {
// Given: any string and a pattern that is only wildcards
var pattern = '***',
string = 'test';
// When: we call wildstring.match
var result = wildstring.match(pattern, string);
// Then: we should see that they match
assert.equal(result, true);
});
it('should match longer strings if the pattern ends with a wildcard', function() {
// Given: a string that is longer than the pattern, but matches up to the wildcard, and the pattern
var pattern = 'test*',
string = 'testing';
// When: we call wildstring.match
var result = wildstring.match(pattern, string);
// Then: we should see that they match
assert.equal(result, true);
});
it('should match longer strings if the pattern begins with a wildcard', function() {
// Given: a string that is longer than the pattern, but matches after to the wildcard, and the pattern
var pattern = '*ing',
string = 'testing';
// When: we call wildstring.match
var result = wildstring.match(pattern, string);
// Then: we should see that they match
assert.equal(result, true);
});
it('should match longer strings if the pattern begins with multiple wildcards', function() {
// Given: a string that is longer than the pattern, but matches after to the wildcard, and the pattern
var pattern = '***ing',
string = 'testing';
// When: we call wildstring.match
var result = wildstring.match(pattern, string);
// Then: we should see that they match
assert.equal(result, true);
});
it('should match matching strings even if the pattern ends with a wildcard', function() {
// Given: a string that is longer than the pattern, but matches up to the wildcard, and the pattern
var pattern = 'test*',
string = 'test';
// When: we call wildstring.match
var result = wildstring.match(pattern, string);
// Then: we should see that they match
assert.equal(result, true);
});
it('should match matching strings even if the pattern ends with multiple wildcards', function() {
// Given: a string that is longer than the pattern, but matches up to the wildcard, and the pattern
var pattern = 'test***',
string = 'test';
// When: we call wildstring.match
var result = wildstring.match(pattern, string);
// Then: we should see that they match
assert.equal(result, true);
});
it('should match matching strings even if the pattern begins with a wildcard', function() {
// Given: a string that is longer than the pattern, but matches after to the wildcard, and the pattern
var pattern = '*ing',
string = 'testing';
// When: we call wildstring.match
var result = wildstring.match(pattern, string);
// Then: we should see that they match
assert.equal(result, true);
});
it('should not match strings that have extra characters at the end when the pattern doesn\'t end with a wildcard', function() {
// Given: a string that is longer than the pattern, but matches after to the wildcard, and the pattern
var pattern = '*ing',
string = 'ings';
// When: we call wildstring.match
var result = wildstring.match(pattern, string);
// Then: we should see that they match
assert.equal(result, false);
});
it('should not match strings that have extra characters at the beginning when the pattern doesn\'t begin with a wildcard', function() {
// Given: a string that is longer than the pattern, but matches after to the wildcard, and the pattern
var pattern = 'ing*',
string = 'testing';
// When: we call wildstring.match
var result = wildstring.match(pattern, string);
// Then: we should see that they match
assert.equal(result, false);
});
it('should match strings that match the beginning and end with a wildcard in the middle', function() {
// Given: a string that is longer than the pattern, but matches after to the wildcard, and the pattern
var pattern = 'bow*ing',
string = 'bowstring';
// When: we call wildstring.match
var result = wildstring.match(pattern, string);
// Then: we should see that they match
assert.equal(result, true);
});
it('should match matching strings even if there\'s a wildcard in the middle', function() {
// Given: a string that is longer than the pattern, but matches after to the wildcard, and the pattern
var pattern = 'test*ing',
string = 'testing';
// When: we call wildstring.match
var result = wildstring.match(pattern, string);
// Then: we should see that they match
assert.equal(result, true);
});
it('should work with multiple wildcards in the middle', function() {
// Given: a string that is longer than the pattern, but matches after to the wildcard, and the pattern
var pattern = 'te*st*ing',
string = 'tea string';
// When: we call wildstring.match
var result = wildstring.match(pattern, string);
// Then: we should see that they match
assert.equal(result, true);
});
it('should work with multiple wildcards in the middle and at the beginning', function() {
// Given: a string that is longer than the pattern, but matches after to the wildcard, and the pattern
var pattern = '*test*ing',
string = 'I\'m testing';
// When: we call wildstring.match
var result = wildstring.match(pattern, string);
// Then: we should see that they match
assert.equal(result, true);
});
it('should work with multiple wildcards in the middle and at the end', function() {
// Given: a string that is longer than the pattern, but matches after to the wildcard, and the pattern
var pattern = 'te*st*ing*',
string = 'tea stings';
// When: we call wildstring.match
var result = wildstring.match(pattern, string);
// Then: we should see that they match
assert.equal(result, true);
});
it('should move on if a wildcard doesn\'t continue to match but can later', function() {
// Given: a string that has a pattern after the wildcard twice
var pattern = '*test*ing',
string = 'I\'m testing this thing';
// When: we call wildstring.match
var result = wildstring.match(pattern, string);
// Then: we should see that they match
assert.equal(result, true);
});
it('should handle wildcarding duplicate characters well', function() {
// Given: a string that is longer than the pattern, but matches after to the wildcard, and the pattern
var pattern = '*||test*',
string = '|||||testing';
// When: we call wildstring.match
var result = wildstring.match(pattern, string);
// Then: we should see that they match
assert.equal(result, true);
});
it('should fail correctly with duplicate characters', function() {
// Given: a string that is longer than the pattern, but matches after to the wildcard, and the pattern
var pattern = '*))))))*',
string = ')))))';
// When: we call wildstring.match
var result = wildstring.match(pattern, string);
// Then: we should see that they match
assert.equal(result, false);
});
it('should work with case sensitivity off', function() {
// Given: a string that does not match the case of the pattern, and case sensitivity is off
wildstring.caseSensitive = false;
var pattern = '*TEST',
string = 'TeSt';
// When: we call wildstring.match
var result = wildstring.match(pattern, string);
// Then: we should see that they match
assert.equal(result, true);
});
});
describe('#replace', function() {
it('sholud return the pattern when no wildcard is given', function() {
// Given: a string array and a pattern with no wildcards
var pattern = 'test',
strings = ['testing'];
// When: we call wildstring.replace
var result = wildstring.replace(pattern, strings);
// Then: we should see that the pattern is unchanged
assert.equal(result, pattern);
});
it('should do ok as a date parser', function() {
var date = new Date(2015, 6, 15); // month is 0 based for some reason
var strings = [ date.getMonth() + 1, date.getDate(), date.getFullYear() ];
var pattern = '*/*/*';
var result = wildstring.replace(pattern, strings);
assert.equal(result, '7/15/2015');
});
it('should tell you when you add too many strings', function() {
// Given: more strings than wildcards in the pattern
var pattern = 'Test *',
strings = [ 'testing', 'strings' ];
// When: we call wildstring.replace with the pattern and strings
assert.throws(function() { wildstring.replace(pattern, strings); });
// Then: we should get an error
});
it('should tell you when you add too many wildcards', function() {
// Given: more strings than wildcards in the pattern
var pattern = '* Te*st *',
strings = [ 'testing', 'strings' ];
// When: we call wildstring.replace with the pattern and strings
assert.throws(function() { wildstring.replace(pattern, strings); });
// Then: we should get an error
});
});
});