regularity is a friendly regular expression builder
for Node.
It is a JavaScript port
of the very fine regularity
Ruby gem.
Regular expressions are a powerful way of matching patterns against text, but too often they are write once, read never. After all, who wants to try and decipher
/^(?:[0-9]){3}-(?:[A-Za-z]){2}(?:#)?(?:a|b)(?:a){2,4}\$$/i
when you could express it as
var Regularity = require('regularity');
var regularity = new Regularity();
var myRegexp = regularity
.startWith(3, 'digits')
.then('-')
.then(2, 'letters')
.maybe('#')
.oneOf('a', 'b')
.between([2, 4], 'a')
.endWith('$')
.insensitive()
.done();
While taking up a bit more space, regular expressions created using regularity are much more readable than their cryptic counterparts. But they are still native regular expressions!
To get regularity, you need to have npm installed. It should have been installed along with Node. Once you have it, just run
$ npm install regularity
in your terminal, and it should promptly download into the current folder.
When you require regularity,
all you get is a constructor function.
To start building a regular expression,
you should instantiate an object using new
,
as demonstrated above,
and then call
any of the methods it provides
with the proper arguments.
When you are done building the regular expression,
tell regularity so by calling done
.
This call will return a native RegExp
implementing the pattern which you described
using the methods of the regularity object,
which you can then use
for your own purposes.
Notice that you should probably use one new regularity instance for each regular expression that you want to build. If you keep calling methods on an existing regularity instance, you will be reusing the declarations you made on that object before.
regularity instances expose a set of methods
(the DSL methods)
which allow you to declaratively build
the regular expression you want.
They all return this
,
so they are chainable.
Notice that the order in which you call these methods
determines the order in which the pattern is assembled.
All DSL methods accept at least
one of the following signatures:
either an unnumbered constraint,
which is expected to be a single string,
such as then('xyz')
,
or a numbered constraint,
composed by a count and a pattern,
such as atLeast(2, 'ab')
.
In addition, the following special identifers are supported as a shorthand for some common patterns:
'digit' : '[0-9]'
'lowercase' : '[a-z]'
'uppercase' : '[A-Z]'
'letter' : '[A-Za-z]'
'alphanumeric' : '[A-Za-z0-9]'
'whitespace' : '\s'
'space' : ' '
'tab' : '\t'
Special identifiers may be pluralized,
and regularity will still understand them.
This allows you
to write more meaningful declarations,
because then(2, 'letters')
works
in addition to then(1, 'letter')
.
The following is a more detailed explanation of all the DSL methods and their signatures. Should you have any doubts, please refer to the spec, where you can find examples of all the supported use cases.
Bear in mind that, in what follows,
pattern
stands for any string,
which might or might not be
any of the special identifiers,
and which might include characters
which need escaping (you don't need
to escape them yourself, as regularity
will take care of that),
and n
stands for any positive integer
(that is, any integer
greater than or equal to 1
).
Where n
is optional
(denoted by [n,]
in the signature),
passing 1
as n
is equivalent to not passing n
at all.
-
startWith([n,] pattern)
: Require thatpattern
occur exactlyn
times at the beginning of the input. This method may be called only once. -
append([n,] pattern)
: Require thatpattern
occur exactlyn
times after what has been declared so far and before anything that is declared afterwards. -
then([n,] pattern)
: This is just an alias forappend
. -
endWith([n,] pattern)
: Require thatpattern
occur exactlyn
times at the end of the input. This method may be called only once. -
maybe(pattern)
: Require thatpattern
occur either one or zero times. -
oneOf(firstPattern[, secondPattern[, ...]])
: Require that at least one of the passedpattern
s occur. -
between(range, pattern)
: Require thatpattern
occur a number of consecutive times betweenrange[0]
andrange[1]
, both included.range
is expected to be an array containing two positive integers. -
zeroOrMore(pattern)
: Require thatpattern
occur any number of consecutive times, including zero times. -
oneOrMore(pattern)
: Require thatpattern
occur consecutively at least once. -
atLeast(n, pattern)
: Require thatpattern
occur consecutively at leastn
times. Typically, heren
should be greater than1
(if you want it to be exactly1
, you should useoneOrMore
). -
atMost(n, pattern)
: Require thatpattern
occur consecutively at mostn
times. Typically, heren
should be greater than1
(if you want it to be exactly1
, you should usemaybe
).
Besides the DSL methods, regularity instances also expose the following methods:
-
insensitive()
: Specify that the regular expression mustn't distinguish between uppercacase and lowercase letters. -
global()
: Specify that the regular expression must match against all possible matches in the string (instead of matching just the first, which is the default behaviour). -
multiline()
: Specify that the string against which theRegExp
is to be matched may span multiple lines. -
done()
: Return the nativeRegExp
object representing the pattern which you described by means of the previous calls on that regularity instance.
Original idea and Ruby implementation are by Andrew Berls.
If you are unsure about the RegExp
you just built, regulex
is a great tool which will draw you
a fancy railroad diagram of it.
This project is licensed under the
MIT License.
For more details, see the LICENSE
file
at the root of the repository.