tom-lord / regexp-examples

Generate strings that match a given regular expression
MIT License
520 stars 31 forks source link
data-generation mri random-string regexp ruby

regexp-examples

Gem Version Build Status Coverage Status Code Climate

Extends the Regexp class with the methods: Regexp#examples and Regexp#random_example

Regexp#examples generates a list of all* strings that will match the given regular expression.

Regexp#random_example returns one, random string (from all possible strings!!) that matches the regex.

* If the regex has an infinite number of possible strings that match it, such as /a*b+c{2,}/, or a huge number of possible matches, such as /.\w/, then only a subset of these will be listed. For more detail on this, see configuration options.

If you'd like to understand how/why this gem works, please check out my blog post about it.

Usage

Regexp#examples

/a*/.examples #=> ['', 'a', 'aa']
/ab+/.examples #=> ['ab', 'abb', 'abbb']
/this|is|awesome/.examples #=> ['this', 'is', 'awesome']
/https?:\/\/(www\.)?github\.com/.examples #=> ['http://github.com',
  # 'http://www.github.com', 'https://github.com', 'https://www.github.com']
/(I(N(C(E(P(T(I(O(N)))))))))*/.examples #=> ["", "INCEPTION", "INCEPTIONINCEPTION"]
/\x74\x68\x69\x73/.examples #=> ["this"]
/what about (backreferences\?) \1/.examples
  #=> ['what about backreferences? backreferences?']
/
  \u{28}\u2022\u{5f}\u2022\u{29}
  |
  \u{28}\u{20}\u2022\u{5f}\u2022\u{29}\u{3e}\u2310\u25a0\u{2d}\u25a0\u{20}
  |
  \u{28}\u2310\u25a0\u{5f}\u25a0\u{29}
/x.examples #=> ["(•_•)", "( •_•)>⌐■-■ ", "(⌐■_■)"]

Regexp#random_example

Obviously, you will get different (random) results if you try these yourself!

/\w{10}@(hotmail|gmail)\.com/.random_example #=> "TTsJsiwzKS@gmail.com"
/5[1-5][0-9]{14}/.random_example #=> "5224028604559821" (A valid MasterCard number)
/\p{Greek}{80}/.random_example
  #=> "ΖΆΧͷᵦμͷηϒϰΟᵝΔ΄θϔζΌψΨεκᴪΓΕπι϶ονϵΓϹᵦΟπᵡήϴϜΦϚϴϑ͵ϴΉϺ͵ϹϰϡᵠϝΤΏΨϹϊϻαώΞΰϰΑͼΈΘͽϙͽξΆΆΡΡΉΓς"
/written by tom lord/i.random_example #=> "WrITtEN bY tOM LORD"

Supported ruby versions

MRI 2.4.0 (oldest non-EOL version) --> 3.0.0 (latest stable version)

MRI 2.0.0 --> 2.3.x were supported until version 1.5.0 of this library. Support was dropped primarily because of the need to use RbConfig::CONFIG['UNICODE_VERSION'], which was added to ruby version 2.4.0.

MRI versions ≤ 1.9.3 were never supported by this library. This is primarily because MRI 2.0.0 introduced a new regexp engine (Oniguruma was replaced by Onigmo -- For example, named properties like /\p{Alpha}/ are illegal syntax on MRI 1.9.3.). Whilst most of this gem could be made to work with MRI 1.9.x (or even 1.8.x), I considered the changes too significant to implement backwards compatability (especially since long-term support for MRI 1.9.3 has long ended).

Other implementations, such as JRuby, could probably work fine - but I haven't fully tried/tested it. Pull requests are welcome.

Installation

Add this line to your application's Gemfile:

gem 'regexp-examples'

And then execute:

$ bundle

Or install it yourself as:

$ gem install regexp-examples

Supported syntax

Short answer: Everything is supported, apart from "irregular" aspects of the regexp language -- see impossible features.

Long answer:

Configuration Options

When generating examples, the gem uses 3 configurable values to limit how many examples are listed:

Rexexp#examples makes use of all these options; Rexexp#random_example only uses max_repeater_variance, since the other options are redundant.

Defining custom configuration values

To use an alternative value, you can either pass the configuration option as a parameter:

/a*/.examples(max_repeater_variance: 5)
  #=> [''. 'a', 'aa', 'aaa', 'aaaa' 'aaaaa']
/[F-X]/.examples(max_group_results: 10)
  #=> ['F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O']
/[ab]{10}/.examples(max_results_limit: 64).length == 64 # NOT 1024
/[slow]{9}/.examples(max_results_limit: 9999999).length == 4 ** 9 == 262144 # Warning - this will take a while!
/.*/.random_example(max_repeater_variance: 50)
  #=> "A very unlikely result!"

Or, set an alternative value within a block:

RegexpExamples::Config.with_configuration(max_repeater_variance: 5) do
  # ...
end

Or, globally set a different default value:

# e.g In a rails project, you may wish to place this in
# config/initializers/regexp_examples.rb
RegexpExamples::Config.max_repeater_variance = 5
RegexpExamples::Config.max_group_results = 10
RegexpExamples::Config.max_results_limit = 20000

A sensible use case might be, for example, to generate all 1-5 digit strings:

/\d{1,5}/.examples(max_repeater_variance: 4, max_group_results: 10, max_results_limit: 100000)
  #=> ['0', '1', '2', ..., '99998', '99999']

Configuration Notes

Due to code optimisation, Regexp#random_example runs pretty fast even on very complex patterns. (I.e. It's typically a lot faster than using /pattern/.examples.sample(1).) For instance, the following takes no more than ~ 1 second on my machine:

/.*\w+\d{100}/.random_example(max_repeater_variance: 1000)

All forms of configuration mentioned above are thread safe.

Bugs and TODOs

There are no known major bugs with this library. However, there are a few obscure issues that you may encounter.

All known bugs/missing features are documented in GitHub. Please discuss known issues there, or raise a new issue if required. Pull requests are welcome!

Some of the most obscure regexp features are not even mentioned in the ruby docs. However, full documentation on all the intricate obscurities in the ruby (version 2.x) regexp parser can be found here.

Impossible features ("illegal syntax")

The following features in the regex language can never be properly implemented into this gem because, put simply, they are not technically "regular"! If you'd like to understand this in more detail, check out what I had to say in my blog post about this gem.

Using any of the following will raise a RegexpExamples::IllegalSyntax exception:

(Note: Backreferences are not really "regular" either, but I got these to work with a bit of hackery.)

Contributing

  1. Fork it ( https://github.com/tom-lord/regexp-examples/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request
  6. Don't forget to add tests!!