string-mutator v0.2.0
Perform mutation operations on text strings (or text files). Useful for (simple) automatic code refactorings f.ex in code generators.
Getting Started
Install the module with: npm install string-mutator
Install with cli command
$ npm install -g string-mutator
$ string-mutator --help
$ string-mutator --versionBrowserify
$ npm run-script browserDocumentation
Quick start
var mutators = require('mutators');
var sm = mutators.string; // string mutator
var fm = mutators.file; // file mutator
var msg = "Peter has 8 dollars and Jane has 15"
// String mutator
sm.last('Jane has 15').remove().on(msg);
// using content
sm.content(msg).last(/\d+/g).replaceWith('32');
// File mutator - performing string mutations
fm.readFile('test/files/test.txt').perform(function() {
return this.first(/\d+/).prepend('$');
}).write();
console.log('wrote', written, res.lastWritten);
res.write(res.original);String mutator API
The string mutation API uses chaining. To start a chain, use any of:
- first(matchExpr)
- last(matchExpr)
- content(text)
first and last
The first and last functions each take a matching expression that can be a simple string or a regular expression.
The result can chained with any of:
- append
- prepend
- replaceWith
- remove
append and prepend
sm.first(matchExpr).append('<FOUND>', target);
chaining
Both append and prepend can be chained with to like this
sm.first(matchExpr).append("\nEND").to(target);
relaceWith and remove
sm.first(matchExpr).remove(target);
chaining
replace and remove can be chained with on like this:
sm.first(matchExpr).replaceWith(something).on(target);
first.prepend
var sm = require('string-mutator');
var msg = "Peter has 8 dollars and Jane has 15"
var res = sm.first(/\d+/g).prepend('$', msg);
// => "Peter has $8 dollars and Jane has 15"The following pattern valid for any prepend/append action.
`(text).to(content)*
append(text).to(content) or prepend(text).to(content)
var sm = require('../lib/string-mutator.js');
var msg = "Peter has 8 dollars and Jane has 15"
var res = sm.first(/\d+/g).prepend('$').to(msg);
// => "Peter has $8 dollars and Jane has 15"first.append
var msg = "Peter has 8 dollars and Jane has 15"
var res = sm.first(/\d+/g).append('$', msg);
res = sm.first(/\d+/g).append('$').to(msg);
// => "Peter has 8$ dollars and Jane has 15"last.prepend
var msg = "Peter has 8 dollars and Jane has 15"
var res = sm.last(/\d+/g).prepend('$', msg);
// res = sm.last(/\d+/g).prepend('$').to(msg);
// => "Peter has 8 dollars and Jane has $15");last.append
var msg = "Peter has 8 dollars and Jane has 15$"
var res = sm.last(/\d+/g).append('$', msg);
// res = sm.last(/\d+/g).append('$').to(msg);
// => "Peter has 8 dollars and Jane has 15$");replaceWith
var msg = "Peter has 8 dollars and Jane has 15$"
var res = sm.last(/\d+/g).replaceWith('42', msg);
res = sm.first(/\d+/g).replaceWith('42').on(msg);
// => "Peter has 8 dollars and Jane has 42");remove
Replace match with empty content ;)
var msg = "Peter has 8 dollars and Jane has 15"
var res = sm.last('and Jane has 15').remove(msg);
sm.last(/\d+/g).remove().on(msg);
// => "Peter has 8 dollars");Content
An alternative is to start off by wrapping the text in a content object
Content can be chained with any of the following:
- first
- last
- before
- after
- between
- prependTxt
- appendTxt
var msg = "Peter has 8 dollars and Jane has 15"
sm.content(msg).last('Jane has 15').remove();
// => "Peter has 8 dollars");Before
before is chained on a content object and returns a new content object with the text before a match. Optionally it can take a matcher indicator string, which can be set to 'first' or 'last' (effectively: "before first" or "before last")
var msg = "Peter has 15 dollars, Jane has 15 and Paul has 32"
sm.content(msg).before(/Jane/).last(/\d+/g).replaceWith('20');
// => Peter has 20 dollars, Jane has 15 and Paul has 32chaining prependTxt on before prepends the text before the match.
msg = "Peter have 12 dollars, Paul"
var res = sm.content(msg).before(/Paul/).prependTxt('Tina has 7.').result;
// => Peter have 12 dollars, Tina has 7Using `mergeRest()
msg = "Peter have 12 dollars, Paul"
var res = sm.content(msg).before(/Paul/).prependTxt('Tina has 7 and').mergeRest();
// => Peter have 12 dollars, Tina has 7 and Paulbefore last
before (and after) also take an options hash for further control.
The match: 'last' option can be used to find the last match within the scope.
msg = "Peter have 12 dollars, Paul is here Paul goes back"
var res = sm.content(msg).before(/Paul/, {include: true, match: 'last'}).prependTxt('Tina has 7 and').mergeRest();
// => Peter have 12 dollars, Paul is here Tina has 7 and Paul goes backFor convenience beforeLast and afterLast methods are also available.
`var res = sm.content(msg).beforeLast(/Paul/)
After
Same as before but instead "after" ;)
If in doubt, see the test suite in the /test folder.
Between
between is chained on a content object and returns a new content object with the text between two matches.
var msg = "Peter has 15 dollars, Jane has 15 and Paul has 32 or 15"
sm.content(msg).between(/Peter/).and(/Paul/).last(/\d+/g).replaceWith('20');
// => Peter has 15 dollars, Jane has 20 and Paul has 32 or 15inclusive
Both between, and are exclusive by default.
You can now also use an options hash {include: true} to be inclusive.
This option can also be used with before and after.
var msg = "Peter has 15 dollars, Jane has 15 and Paul has 32 or 15"
sm.content(msg).between(/Peter/, {include: true}).and(/Paul/, {include: true}).result;
// => Peter has 15 dollars, Jane has 20 and PaulShorthand inclusive:
`sm.content(msg).betweenIncl(/Peter/).andIncl(/Paul/)
prependTxt
Adds text at beginning of content
`sm.content("Kristian").prependTxt('Hello, Sir ')
appendTxt
Adds text at the end of content
sm.content("Paula").appendTxt(', says Goodbye')
Status
All tests pass :)
More advanced operations
For even more string manipulation, I recommend splitting a string into its multiple parts using a split function, then iterating through them and using this API on each part, then joining them back together.
You are most welcome to propose a nice API to facilitate this.
File mutator API
File mutation always starts with readFile
var fileMutateObj = fm.readFile('test/files/test.txt');
The result of readFile should always be chained with perform, which performs the string mutation on the content read.
perform wraps the read content in a content object (see: String mutator API) which becomes this in the context/scope of the function.
fm.readFile('test/files/test.txt').perform(function() {
return this.first(/\d+/).prepend('$');
})The result of the perform mutation can be chained with any of:
- write(newContent)
- writeFile(fileName, newContent)
- read()
- lastWritten
- original
Complete example:
fm.readFile('test/files/test.txt').perform(function() {
return this.first(/\d+/).prepend('$');
}).write();
console.log('wrote', written, res.lastWritten);
res.write(res.original);
res.writeFile('another_file.txt');TODO
Cleanup and Refactor!!! Lot's of duplication :(
Add more customizability than simply first/last. It would be sweet if user could pass a function that selects one or more matches to operate on/from.
Contributing
Add unit tests for any new or changed functionality. Lint and test your code using Grunt.
License
Copyright (c) 2014 Kristian Mandrup
Licensed under the MIT license.
