-
Notifications
You must be signed in to change notification settings - Fork 234
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Ability to Easily Extend string.js #57
Conversation
I read over your comments and glanced over the code. It looks great. Let me review the code a bit more thoroughly tomorrow morning. This looks like this might be one of the stepping stones that I was looking for on the path to 2.0. Thanks a lot for your contribution! |
Forgot to mention that I tested it with Safari 6.0.5 on Mac OS X 10.7.5 and it passes all tests. Testing with Firefox 23.0.1 on Mac passes all tests except for restorePrototype. This test fails even with the current strings.js 1.5.1 (without my code changes) since Firefox 17 introduced its own endsWith method for the String object. You'll need to change the way your test code checks to make sure the String prototype has not been altered instead of checking for the existence of endsWith. Also tried to test string.js 1.5.1 on Chrome 29.0.1547.65 on Mac and I get a blank page. Didn't look into it, so I'm not sure why. You may want to look into what is going on with your test on Chrome. Haven't had a chance to run the test on any Windows or mobile platforms. Have used Jasmine to test my own string.js extension module with case-insensitive methods and it passes all of my tests in all three browsers mentioned above on Mac. It's a pretty good test of the ability to create extensions using my changes to string.js. Again, I haven't had a chance to run my test on any Windows or mobile platforms. On another note, here is a handy method I added to my string module that you may want to incorporate into string.js: function orPlural (number, pluralSuffix, plural) {
pluralSuffix = pluralSuffix || 's';
return number === 1 ? this : plural ? new this.constructor(plural.toString()) : new this.constructor(this.toString() + pluralSuffix.toString());
} It allows you to write sentences with proper singular or plural words or phrases: 'There ' + S('is').orPlural(foxCount, '', 'are').s + foxCount + S('fox').orPlural(foxCount, 'es').s + '.'
// If foxCount is 1: There is 1 fox.
// Otherwise: There are 5 foxes.
// It defaults to a suffix of 's' for plurals:
S('horse').orPlural(2) // horses
// And it handles the weird plural cases:
S('goose').orPlural(7, '', 'geese') // geese |
Cool, thanks. I'm going to have to push back reviewing this until Monday, Sept 16th. |
No worries. And thank you for developing string.js! |
Add Ability to Easily Extend string.js
Hi, |
I don't think there are any methods that do this. |
It's worth mentioning in this post that for a normal extension (i.e.: not enhancing existing function) do:
|
Here's an updated Extension Example (minus the complicated and unnecessary options) on how to extend this module using native ES6: // Place this in some local file, e.g. `./lib/CustomString.js`.
const StringJs = require('string');
const StringJsConstructor = StringJs('').constructor;
// Unfortunately, the static methods and properties are exported separately
// and not members of the constructor; merge them in.
Object.keys(StringJs).filter(p => StringJs.hasOwnProperty(p)).forEach(p => StringJsConstructor[p] = StringJs[p]);
class CustomString extends StringJsConstructor {
contains(value, caseSensitive = false) {
return caseSensitive ? super.contains(value) : super.contains.call(this.toUpperCase(), value.toUpperCase());
}
}
function S(value) {
return value instanceof CustomString ? value : new CustomString(value);
}
module.exports = S;
module.exports.default = module.exports; // For ES6/TypeScript Example usage: // In other JS files, require the above file.
const S = require('./lib/CustomString.js');
S('This is a test').contains('this'); // true
S('This is a test').contains('this', true); // false A more complex example can be found here: @unicorn-fail/string-extra |
Making string.js More Extensible
I love string.js! However, I'd like to have the ability to easily extend it in a safe, consistent and simple manner.
Why?
Simply stated, I have requirements that string.js does not meet on its own. I'm sure that others do too. Now someone in my position could simply modify string.js and submit a pull request. But what if some of the changes really don't belong in a generic library like string.js? What if they're too specific to a particular application domain? The author, as they should, would nix those changes.
So extending string.js, just like the author has extended the native Javascript String object, is the way to go.
Why Not Just Modify the String.js Prototype?
What if you're working on a large project with several developers or using other libraries which depend on string.js and you modify string.js? Now you're in the same boat as you would be if you modified the String Object prototype. Other modules may use string.js and expect certain behavior that you have modified. Bugs galore!
For an example, one of my requirements is case insensitivity with the ability to use case sensitivity on demand. If I modify the string.js replaceAll method to make it default to case insensitive matches, other code that expects string.js to be case sensitive will break.
How?
While attempting to create such a string.js extension, I found that string.js needed to be modified in four simple ways. The good news is, none of the changes affect the module's interface. Everything works just like before. The changes just make it more generic and extensible.
1. Add an
initialze
function containing the currentS
constructor's code.The
S
constructor and the newsetValue
method (see below) both call this new function.2. Add a
setValue
method.This method allows the constructor in a module which extends string.js to set the string value just like the
S
constructor without knowing how to set the necessary values (s
andorig
) itself.The constructor in this other module would look something like this:
3. Change returned values from methods.
Instead of returning a new S object, methods must return a new object using the given object's constructor. For string.js objects, the constructor will be S. For extended objects, it will be the object's constructor (
ExtendedString
in the example above). This way, string.js methods will always return the same kind of object that they were given.Doing this prevents each extended module from having to jump through hoops to pull in the string.js methods and force them to return its own kind of object. This is what string.js has to do to get the native String object methods to return string.js objects (in the Attach Native JavaScript String Properties section).
So each method that returns a new string, now does this:
Instead of this:
4. Set the constructor to
S
after setting the prototype.When setting the prototype of S to the object containing the methods, the constructor is obliterated. So string.js objects are
instanceof Object
but they're notinstanceof S
when they should be.Extension Example
The following code shows a string.js extension module which creates and manipulates
ExtendedStrings
. It modifies thecontains
string.js method to default to case-insensitive searches and provides callers with a way to force the search to be case sensitive.Example usage: