randexp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +5 -0
- data/LICENSE +20 -0
- data/README +83 -0
- data/Rakefile +111 -0
- data/TODO +5 -0
- data/lib/core_ext/array.rb +5 -0
- data/lib/core_ext/integer.rb +5 -0
- data/lib/core_ext/range.rb +5 -0
- data/lib/core_ext/regexp.rb +7 -0
- data/lib/core_ext.rb +4 -0
- data/lib/dictionary.rb +24 -0
- data/lib/randexp/parser.rb +72 -0
- data/lib/randexp/reducer.rb +102 -0
- data/lib/randexp.rb +17 -0
- data/lib/randgen.rb +52 -0
- data/spec/regression/regexp_spec.rb +139 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/unit/core_ext/regexp_spec.rb +9 -0
- data/spec/unit/randexp/parser_spec.rb +77 -0
- data/spec/unit/randexp/reducer_spec.rb +224 -0
- data/spec/unit/randexp_spec.rb +158 -0
- data/spec/unit/randgen_spec.rb +110 -0
- metadata +82 -0
data/CHANGELOG
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Ben Burkert
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
Randexp
|
2
|
+
by Ben Burkert
|
3
|
+
http://github.com/benburkert/randexp
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
andexp makes it easy to generate random string from most regular expressions.
|
8
|
+
|
9
|
+
== REQUIREMENTS:
|
10
|
+
|
11
|
+
* none!
|
12
|
+
|
13
|
+
== INSTALL:
|
14
|
+
|
15
|
+
$ gem sources -a http://gems.github.com/ (you only need to do this once)
|
16
|
+
$ gem install benburkert-randexp
|
17
|
+
|
18
|
+
== USAGE:
|
19
|
+
|
20
|
+
Randexp adds the #generate (or #gen, for short) method to the Regexp class,
|
21
|
+
which generates a 'random' string that will match your regular expression.
|
22
|
+
|
23
|
+
/abc|def/.gen
|
24
|
+
# => "def"
|
25
|
+
|
26
|
+
== Valid Regexp's
|
27
|
+
|
28
|
+
Randexp can only generate matching string from simple regular expression.
|
29
|
+
Except for a few circumstances, wildcards are generally not allowed in the
|
30
|
+
regular expression. is pretty domain specific, so trying to guess when to
|
31
|
+
terminate a random pattern would produce unhelpful data:
|
32
|
+
|
33
|
+
>> /Aa{3}h*!/.gen
|
34
|
+
# => RuntimeError: Sorry, "h*" is too vague, try setting a range: "h{0,3}"
|
35
|
+
>> /Aa{3}h{3,15}!/.gen
|
36
|
+
=> "Aaaahhhhh!"
|
37
|
+
|
38
|
+
>> /(never gonna (give you up|let you down), )*/.gen
|
39
|
+
=> RuntimeError: Sorry, "(...)*" is too vague, try setting a range: "(...){0, 3}"
|
40
|
+
>> /(never gonna (give you up|let you down), ){3,5}/.gen
|
41
|
+
=> "never gonna give you up, never gonna let you down, never gonna give you up, never gonna give you up, "
|
42
|
+
|
43
|
+
The exception being word characters (\w), which generate a random word from the Dictionary class.
|
44
|
+
|
45
|
+
>> /\w+/.gen
|
46
|
+
=> "groveling"
|
47
|
+
|
48
|
+
= Primitives & Complex matches
|
49
|
+
|
50
|
+
The single character matchers supported are words(\w), whitespace(\s), and digits(\d).
|
51
|
+
|
52
|
+
>> /\d{50}/.gen
|
53
|
+
=> "50315410741096763188525493528315906035878741451037"
|
54
|
+
|
55
|
+
When a multiplicity constraint is placed on a word character, a word with the valid length is generated.
|
56
|
+
|
57
|
+
>> /\w{10}/.gen # a word with 10 letters
|
58
|
+
=> "Chaucerism"
|
59
|
+
|
60
|
+
>> /\w{5,15}/.gen
|
61
|
+
=> "cabalistic"
|
62
|
+
|
63
|
+
Complex matchers use the [:...:] syntax within the regular expression.
|
64
|
+
|
65
|
+
>> /[:sentence:]/.gen
|
66
|
+
=> "Nonhearer demetricize toppiece filicic possessedness rhodizite zoomagnetism earwigginess steady"
|
67
|
+
|
68
|
+
Complex matchers can also be added by extending the Randgen class.
|
69
|
+
|
70
|
+
class Randgen
|
71
|
+
def self.serial_number(options = {})
|
72
|
+
/XX\d{4}-\w-\d{5}/.gen
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
>> /[:serial_number:]/.gen
|
77
|
+
=> "XX3770-M-33114"
|
78
|
+
|
79
|
+
= Dictionary
|
80
|
+
|
81
|
+
The Dictionary loads the local users' words file, allowing randomly generated words to be chosen from
|
82
|
+
thousands of entries to the words file. Words are mapped by their length to allow words to be randomly
|
83
|
+
chosen based on size.
|
data/Rakefile
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
require 'rubygems/specification'
|
4
|
+
require 'date'
|
5
|
+
require "spec/rake/spectask"
|
6
|
+
require 'rake/rdoctask'
|
7
|
+
|
8
|
+
PROJECT_NAME = "randexp"
|
9
|
+
GEM = "randexp"
|
10
|
+
GEM_VERSION = "0.1.0"
|
11
|
+
AUTHOR = "Ben Burkert"
|
12
|
+
EMAIL = "ben@benburkert.com"
|
13
|
+
HOMEPAGE = "http://github.com/benburkert/randexp"
|
14
|
+
TITLE = "Randexp Gem"
|
15
|
+
SUMMARY = "Library for generating random strings."
|
16
|
+
FILES = %w(LICENSE README README Rakefile TODO CHANGELOG) + Dir.glob("{lib,spec}/**/*")
|
17
|
+
RDOC_FILES = %w(LICENSE README README Rakefile TODO CHANGELOG) + Dir.glob("lib/**/*")
|
18
|
+
|
19
|
+
RUBYFORGE_USER = "benburkert"
|
20
|
+
|
21
|
+
spec = Gem::Specification.new do |s|
|
22
|
+
s.name = GEM
|
23
|
+
s.version = GEM_VERSION
|
24
|
+
s.platform = Gem::Platform::RUBY
|
25
|
+
s.has_rdoc = true
|
26
|
+
s.extra_rdoc_files = ["README", "LICENSE", 'TODO']
|
27
|
+
s.summary = SUMMARY
|
28
|
+
s.description = s.summary
|
29
|
+
s.author = AUTHOR
|
30
|
+
s.email = EMAIL
|
31
|
+
s.homepage = HOMEPAGE
|
32
|
+
|
33
|
+
s.require_path = 'lib'
|
34
|
+
s.autorequire = GEM
|
35
|
+
s.files = FILES
|
36
|
+
end
|
37
|
+
|
38
|
+
Rake::GemPackageTask.new(spec) do |package|
|
39
|
+
package.gem_spec = spec
|
40
|
+
package.need_zip = true
|
41
|
+
package.need_tar = true
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "install the gem locally"
|
45
|
+
task :install => [:package] do
|
46
|
+
sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "create a gemspec file"
|
50
|
+
task :make_spec do
|
51
|
+
File.open("#{GEM}.gemspec", "w") do |file|
|
52
|
+
file.puts spec.to_ruby
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
##############################################################################
|
57
|
+
# rSpec & rcov
|
58
|
+
##############################################################################
|
59
|
+
desc "Run all unit specs"
|
60
|
+
Spec::Rake::SpecTask.new("specs:unit") do |t|
|
61
|
+
t.spec_opts = ["--format", "specdoc", "--colour"]
|
62
|
+
t.spec_files = Dir["spec/unit/**/*_spec.rb"].sort
|
63
|
+
t.rcov = true
|
64
|
+
t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
|
65
|
+
t.rcov_opts << '--only-uncovered'
|
66
|
+
t.rcov_opts << '--output coverage/unit'
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
desc "Run all regression specs"
|
71
|
+
Spec::Rake::SpecTask.new("specs:regression") do |t|
|
72
|
+
t.spec_opts = ["--format", "specdoc", "--colour"]
|
73
|
+
t.spec_files = Dir["spec/regression/**/*_spec.rb"].sort
|
74
|
+
t.rcov = true
|
75
|
+
t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
|
76
|
+
t.rcov_opts << '--only-uncovered'
|
77
|
+
t.rcov_opts << '--output coverage/integration'
|
78
|
+
end
|
79
|
+
|
80
|
+
task :specs => ['specs:unit', 'specs:regression']
|
81
|
+
|
82
|
+
##############################################################################
|
83
|
+
# Documentation
|
84
|
+
##############################################################################
|
85
|
+
task :doc => "doc:rerdoc"
|
86
|
+
namespace :doc do
|
87
|
+
|
88
|
+
Rake::RDocTask.new do |rdoc|
|
89
|
+
rdoc.rdoc_files.add(RDOC_FILES)
|
90
|
+
rdoc.main = 'README'
|
91
|
+
rdoc.title = TITLE
|
92
|
+
rdoc.rdoc_dir = "rdoc"
|
93
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
94
|
+
end
|
95
|
+
|
96
|
+
desc "rdoc to rubyforge"
|
97
|
+
task :rubyforge => :doc do
|
98
|
+
sh %{chmod -R 755 rdoc}
|
99
|
+
sh %{/usr/bin/scp -r -p rdoc/* #{RUBYFORGE_USER}@rubyforge.org:/var/www/gforge-projects/#{PROJECT_NAME}/#{GEM}}
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
##############################################################################
|
104
|
+
# release
|
105
|
+
##############################################################################
|
106
|
+
task :release => [:specs, "doc:rubyforge", :package] do
|
107
|
+
sh %{rubyforge add_release #{PROJECT_NAME} #{GEM} "#{GEM_VERSION}" pkg/#{GEM}-#{GEM_VERSION}.gem}
|
108
|
+
%w[zip tgz].each do |ext|
|
109
|
+
sh %{rubyforge add_file #{PROJECT_NAME} #{GEM} "#{GEM_VERSION}" pkg/#{GEM}-#{GEM_VERSION}.#{ext}}
|
110
|
+
end
|
111
|
+
end
|
data/TODO
ADDED
data/lib/core_ext.rb
ADDED
data/lib/dictionary.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
class Dictionary
|
2
|
+
def self.load_dictionary
|
3
|
+
if File.exists?("/usr/share/dict/words")
|
4
|
+
File.read("/usr/share/dict/words").split
|
5
|
+
elsif File.exists?("/usr/dict/words")
|
6
|
+
File.read("/usr/dict/words").split
|
7
|
+
else
|
8
|
+
raise "words file not found"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.words(options = {})
|
13
|
+
case
|
14
|
+
when options.has_key?(:length)
|
15
|
+
words_by_length[options[:length]]
|
16
|
+
else
|
17
|
+
@@words ||= load_dictionary
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.words_by_length
|
22
|
+
@@words_by_length ||= words.inject({}) {|h, w| (h[w.size] ||= []) << w; h }
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
class Randexp
|
2
|
+
class Parser
|
3
|
+
def self.parse(source)
|
4
|
+
case source
|
5
|
+
when /^\(([^()]*|\(.*\))\)$/ then union(parse($1))
|
6
|
+
when /(.*)\|(?:(\({2,}.*\){2,}|[^()]*))$/ then intersection(parse($1), parse($2))
|
7
|
+
when /(.*)\|(\(.*\))$/ then intersection(parse($1), parse($2))
|
8
|
+
when /(.*)\(([^()]*)\)(\*|\*\?|\+|\+\?|\?)$/ then union(parse($1), quantify(parse($2), $3.to_sym))
|
9
|
+
when /(.*)\(([^()]*)\)\{(\d+)\,(\d+)\}$/ then union(parse($1), quantify(parse($2), ($3.to_i)..($4.to_i)))
|
10
|
+
when /(.*)\(([^()]*)\)\{(\d+)\}$/ then union(parse($1), quantify(parse($2), $3.to_i))
|
11
|
+
when /(.*)\(([^()]*)\)$/ then union(parse($1), parse($2))
|
12
|
+
when /(.*)\[:(\w+):\](\*|\*\?|\+|\+\?|\?)$/ then union(parse($1), quantify(random($2), $3.to_sym))
|
13
|
+
when /(.*)\[:(\w+):\]\{(\d+)\,(\d+)\}$/ then union(parse($1), quantify(random($2), ($3.to_i)..($4.to_i)))
|
14
|
+
when /(.*)\[:(\w+):\]\{(\d+)\}$/ then union(parse($1), quantify(random($2), $3.to_i))
|
15
|
+
when /(.*)\[:(\w+):\]$/ then union(parse($1), random($2))
|
16
|
+
when /(.*)\\([wsdc])(\*|\*\?|\+|\+\?|\?)$/ then union(parse($1), quantify(random($2), $3.to_sym))
|
17
|
+
when /(.*)\\([wsdc])\{(\d+)\,(\d+)\}$/ then union(parse($1), quantify(random($2), ($3.to_i)..($4.to_i)))
|
18
|
+
when /(.*)\\([wsdc])\{(\d+)\}$/ then union(parse($1), quantify(random($2), $3.to_i))
|
19
|
+
when /(.*)\\([wsdc])$/ then union(parse($1), random($2))
|
20
|
+
when /\((.*)\)(\*|\*\?|\+|\+\?|\?)$/ then quantify(parse($1), $2.to_sym)
|
21
|
+
when /\((.*)\)\{(\d+)\,(\d+)\}$/ then quantify(parse($1), ($2.to_i)..($3.to_i))
|
22
|
+
when /\((.*)\)\{(\d+)\}$/ then quantify(parse($1), $3.to_i)
|
23
|
+
when /(.*)(.|\s)(\*|\*\?|\+|\+\?|\?)$/ then union(parse($1), quantify(literal($2), $3.to_sym))
|
24
|
+
when /(.*)(.|\s)\{(\d+)\,(\d+)\}$/ then union(parse($1), quantify(literal($2), ($3.to_i)..($4.to_i)))
|
25
|
+
when /(.*)(.|\s)\{(\d+)\}$/ then union(parse($1), quantify(literal($2), $3.to_i))
|
26
|
+
when /(.*)\\([.\/])(\*|\*\?|\+|\+\?|\?)$/ then union(parse($1), quantify(literal($2), $3.to_sym))
|
27
|
+
when /(.*)\\([.\/])\{(\d+)\,(\d+)\}$/ then union(parse($1), quantify(literal($2), ($3.to_i)..($4.to_i)))
|
28
|
+
when /(.*)\\([.\/])\{(\d+)\}$/ then union(parse($1), quantify(literal($2), $3.to_i))
|
29
|
+
when /(.*)\\([.\/])$/ then union(parse($1), literal($2))
|
30
|
+
when /(.*)(.|\s)$/ then union(parse($1), literal($2))
|
31
|
+
else nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class << self
|
36
|
+
alias_method :[], :parse
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.quantify(lhs, sym)
|
40
|
+
[:quantify, lhs, sym]
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.union(lhs, *rhs)
|
44
|
+
if lhs.nil?
|
45
|
+
union(*rhs)
|
46
|
+
elsif rhs.empty?
|
47
|
+
lhs
|
48
|
+
elsif lhs.first == :union
|
49
|
+
rhs.each {|s| lhs << s}
|
50
|
+
lhs
|
51
|
+
else
|
52
|
+
[:union, lhs, *rhs]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.intersection(lhs, rhs)
|
57
|
+
if rhs.first == :intersection
|
58
|
+
[:intersection, lhs] + rhs[1..-1]
|
59
|
+
else
|
60
|
+
[:intersection, lhs, rhs]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.random(char)
|
65
|
+
[:random, char.to_sym]
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.literal(word)
|
69
|
+
[:literal, word]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
class Randexp
|
2
|
+
class Reducer
|
3
|
+
def self.reduce(sexp, quantity = nil)
|
4
|
+
send(sexp.first, sexp[1..-1], quantity)
|
5
|
+
end
|
6
|
+
|
7
|
+
class << self
|
8
|
+
alias_method :[], :reduce
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.quantify(sexp, old_quantity)
|
12
|
+
reduce(*sexp)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.random(sexpish, quantity)
|
16
|
+
case s = sexpish.first
|
17
|
+
when :w then char(quantity)
|
18
|
+
when :s then whitespace(quantity)
|
19
|
+
when :d then digit(quantity)
|
20
|
+
else randgen(s, quantity)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.literal(cell, quantity = nil)
|
25
|
+
case quantity
|
26
|
+
when :'?' then ([''] + cell).pick * ''
|
27
|
+
when :+, :'+?' then raise "Sorry, \"#{cell * ''}+\" is too vague, try setting a range: \"#{cell * ''}{1,3}\""
|
28
|
+
when :*, :'*?' then raise "Sorry, \"#{cell * ''}*\" is too vague, try setting a range: \"#{cell * ''}{0,3}\""
|
29
|
+
when Range then quantity.pick.of { cell * '' } * ''
|
30
|
+
when Integer then quantity.of { cell * '' } * ''
|
31
|
+
when nil then cell * ''
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.intersection(cell, quantity)
|
36
|
+
case quantity
|
37
|
+
when :'?' then ['', cell.map {|s| reduce(s)}.pick].pick
|
38
|
+
when :+, :'+?' then raise "Sorry, \"((...)|(...))+\" is too vague, try setting a range: \"((...)|(...)){1, 3}\""
|
39
|
+
when :*, :'*?' then raise "Sorry, \"((...)|(...))*\" is too vague, try setting a range: \"((...)|(...)){0, 3}\""
|
40
|
+
when Range then quantity.pick.of { cell.map {|s| reduce(s)}.pick } * ''
|
41
|
+
when Integer then quantity.of { cell.map {|s| reduce(s)}.pick } * ''
|
42
|
+
when nil then cell.map {|s| reduce(s)}.pick
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.union(cell, quantity)
|
47
|
+
case quantity
|
48
|
+
when :'?' then ['', cell.map {|s| reduce(s)} * ''].pick
|
49
|
+
when :+, :'+?' then raise "Sorry, \"(...)+\" is too vague, try setting a range: \"(...){1, 3}\""
|
50
|
+
when :*, :'*?' then raise "Sorry, \"(...)*\" is too vague, try setting a range: \"(...){0, 3}\""
|
51
|
+
when Range then quantity.pick.of { cell.map {|s| reduce(s)} * '' } * ''
|
52
|
+
when Integer then quantity.of { cell.map {|s| reduce(s)} * '' } * ''
|
53
|
+
when nil then cell.map {|s| reduce(s)} * ''
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.char(quantity)
|
58
|
+
case quantity
|
59
|
+
when :'?' then ['', Randgen.char].pick
|
60
|
+
when :+, :'+?' then Randgen.word
|
61
|
+
when :*, :'*?' then ['', Randgen.word].pick
|
62
|
+
when Range then Randgen.word(:length => quantity.pick)
|
63
|
+
when 1, nil then Randgen.char
|
64
|
+
when Integer then Randgen.word(:length => quantity)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.whitespace(quantity)
|
69
|
+
case quantity
|
70
|
+
when :'?' then ['', Randgen.whitespace].pick
|
71
|
+
when :+, :'+?' then raise "Sorry, \"\\s+\" is too vague, try setting a range: \"\\s{1, 5}\""
|
72
|
+
when :*, :'*?' then raise "Sorry, \"\\s*\" is too vague, try setting a range: \"\\s{0, 5}\""
|
73
|
+
when Range then quantity.pick.of { Randgen.whitespace } * ''
|
74
|
+
when Integer then quantity.of { Randgen.whitespace } * ''
|
75
|
+
when nil then Randgen.whitespace
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.digit(quantity)
|
80
|
+
case quantity
|
81
|
+
when :'?' then ['', Randgen.digit].pick
|
82
|
+
when :+, :'+?' then raise "Sorry, \"\\d+\" is too vague, try setting a range: \"\\d{1, 5}\""
|
83
|
+
when :*, :'*?' then raise "Sorry, \"\\d*\" is too vague, try setting a range: \"\\d{0, 5}\""
|
84
|
+
when Range then quantity.pick.of { Randgen.digit } * ''
|
85
|
+
when Integer then quantity.of { Randgen.digit } * ''
|
86
|
+
when nil then Randgen.digit
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.randgen(args, quantity)
|
91
|
+
method_name = *args
|
92
|
+
case quantity
|
93
|
+
when :'?' then ['', Randgen.send(method_name, :length => 1)].pick
|
94
|
+
when :+, :'+?' then Randgen.send(method_name)
|
95
|
+
when :*, :'*?' then ['', Randgen.send(method_name)].pick
|
96
|
+
when Range then Randgen.send(method_name, :length => quantity.pick)
|
97
|
+
when 1, nil then Randgen.send(method_name)
|
98
|
+
when Integer then Randgen.send(method_name, :length => quantity)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/randexp.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
class Randexp
|
2
|
+
attr_accessor :sexp
|
3
|
+
|
4
|
+
def initialize(source)
|
5
|
+
@sexp = Randexp::Parser[source]
|
6
|
+
end
|
7
|
+
|
8
|
+
def reduce
|
9
|
+
Reducer[@sexp.dup]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'core_ext'
|
14
|
+
require 'randexp/parser'
|
15
|
+
require 'randexp/reducer'
|
16
|
+
require 'randgen'
|
17
|
+
require 'dictionary'
|
data/lib/randgen.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
class Randgen
|
2
|
+
WORDS_PER_SENTENCE = 3..20
|
3
|
+
SENTENCES_PER_PARAGRAPH = 3..8
|
4
|
+
|
5
|
+
def self.bool(options = {})
|
6
|
+
['true', 'false'].pick
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.lchar(options = {})
|
10
|
+
('a'..'z').to_a.pick
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.uchar(options = {})
|
14
|
+
('A'..'Z').to_a.pick
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.char(options = {})
|
18
|
+
[lchar, uchar].pick
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.whitespace(options = {})
|
22
|
+
["\t", "\n", "\r", "\f"].pick
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.digit(options = {})
|
26
|
+
('0'..'9').to_a.pick
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.alpha_numeric(options = {})
|
30
|
+
[char, digit].pick
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.word(options = {})
|
34
|
+
Dictionary.words(options).pick
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.sentence(options = {})
|
38
|
+
((options[:length] || WORDS_PER_SENTENCE.pick).of { word } * " ").capitalize
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.paragraph(options = {})
|
42
|
+
((options[:length] || SENTENCES_PER_PARAGRAPH.pick).of { sentence } * ". ") + "."
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.phone_number(options = {})
|
46
|
+
case options[:length]
|
47
|
+
when 7 then /\d{3}-\d{4}/.gen
|
48
|
+
when 10 then /\d{3}-\d{3}-\d{4}/.gen
|
49
|
+
else /(\d{3}-)?\d{3}-\d{4}/.gen
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe "#{'*' * 80}\nRegression Specs:" do
|
4
|
+
it "/abcd/ => 'abcd'" do
|
5
|
+
100.times do
|
6
|
+
/abcd/.gen.should == 'abcd'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
it "/(abcd)|(defg)/ => ['abcd', 'defg']" do
|
11
|
+
100.times do
|
12
|
+
['abcd', 'defg'].should include(/(abcd)|(defg)/.gen)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it "/(abcd)|(defg)|(hijk)/ => ['abcd', 'defg', 'hijk']" do
|
17
|
+
100.times do
|
18
|
+
['abcd', 'defg', 'hijk'].should include(/(abcd)|(defg)|(hijk)/.gen)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it "/((abcd)|(defg))|(hijk)/ => ['abcd', 'defg', 'hijk']" do
|
23
|
+
100.times do
|
24
|
+
['abcd', 'defg', 'hijk'].should include(/((abcd)|(defg))|(hijk)/.gen)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it "/(abcd)|((defg)|(hijk))/ => ['abcd', 'defg', 'hijk']" do
|
29
|
+
100.times do
|
30
|
+
['abcd', 'defg', 'hijk'].should include(/(abcd)|((defg)|(hijk))/.gen)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it "/(abc)def(ghi)/ => 'abcdefghi'" do
|
35
|
+
100.times do
|
36
|
+
/(abc)def(ghi)/.gen.should == 'abcdefghi'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it "/(((abc)))/ => 'abc'" do
|
41
|
+
100.times do
|
42
|
+
/(((abc)))/.gen.should == 'abc'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it "/(\\w+)/ => /\\w+/" do
|
47
|
+
100.times do
|
48
|
+
/(\w+)/.gen.should =~ /\w+/
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it "/\\w+ \\w+/ => /\\w+\\s\\w+/" do
|
53
|
+
100.times do
|
54
|
+
/\w+ \w+/.gen.should =~ /\w+\s\w+/
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it "/\\w*/ => /(\\w+)|/" do
|
59
|
+
100.times do
|
60
|
+
/\w*/.gen.should =~ /(\w+)|/
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it "/\\w{2,5}/ => /\\w{2,5}/" do
|
65
|
+
100.times do
|
66
|
+
/\w{2,5}/.gen.should =~ /\w{2,5}/
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
it "/\\w{1}/ => /\\w/" do
|
71
|
+
100.times do
|
72
|
+
/\w{1}/.gen.should =~ /\w/
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it "/\\w{4}/ => /\\w{4}/" do
|
77
|
+
100.times do
|
78
|
+
/\w{4}/.gen.should =~ /\w{4}/
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
it "/[:word:]/ => /\\w+/" do
|
83
|
+
100.times do
|
84
|
+
/[:word:]/.gen.should =~ /\w+/
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
it "/[:bool:]/ => /true|false/" do
|
89
|
+
/[:bool:]/.gen.should =~ /true|false/
|
90
|
+
end
|
91
|
+
|
92
|
+
it "/[:sentence:]/ => /(\w+ )*/" do
|
93
|
+
100.times do
|
94
|
+
/[:sentence:]/.gen.should =~ /(\w+ )*/
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
it "/[:paragraph:]/ => /( (\w+ )*\.)*/" do
|
99
|
+
100.times do
|
100
|
+
/[:paragraph:]/.gen.should =~ /(\w+ )*/
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
it "/(abc|def){1,2}/ => ['abc', 'def', 'abcabc', 'abcdef', 'defabc', 'defdef']" do
|
105
|
+
100.times do
|
106
|
+
['abc', 'def', 'abcabc', 'abcdef', 'defabc', 'defdef'].should include(/(abc|def){1,2}/.gen)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
it "/(\\d{3}-)?\\d{3}-\\d{4}/ => /(\\d{3}-)?\\d{3}-\\d{4}/" do
|
111
|
+
100.times do
|
112
|
+
/(\d{3}-)?\d{3}-\d{4}/.gen.should =~ /(\d{3}-)?\d{3}-\d{4}/
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
it "/[:phone_number:]/ => /(\\d{3}-)?\\d{3}-\\d{4}/" do
|
117
|
+
100.times do
|
118
|
+
/[:phone_number:]/.gen.should =~ /(\d{3}-)?\d{3}-\d{4}/
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
it "/[:phone_number:]{7}/ => /\\d{3}-\\d{4}/" do
|
123
|
+
100.times do
|
124
|
+
/[:phone_number:]{7}/.gen.should =~ /\d{3}-\d{4}/
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
it "/[:phone_number:]{10}/ => /\\d{3}-\\d{3}-\\d{4}/" do
|
129
|
+
100.times do
|
130
|
+
/[:phone_number:]{10}/.gen.should =~ /\d{3}-\d{3}-\d{4}/
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
it "/\\w+@\\w+\\.(com|org|net)/ => /\\w+@\\w+\\.(com|org|net)/.gen" do
|
135
|
+
100.times do
|
136
|
+
/\w+@\w+\.(com|org|net)/.gen.should =~ /\w+@\w+\.(com|org|net)/
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|