randall 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.rdoc +87 -0
- data/lib/randall/charclass.rb +186 -0
- data/lib/randall/monkey_patch.rb +35 -0
- data/lib/randall/sugar.rb +149 -0
- data/lib/randall.rb +195 -0
- data/lib/regexp.treetop +180 -0
- metadata +110 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
copyright (c) 2010, Diego Che
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
6
|
+
in the Software without restriction, including without limitation the rights
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
9
|
+
furnished to do so, subject to the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be included in
|
12
|
+
all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
= Randall
|
2
|
+
|
3
|
+
*{Randomly generates instances of any class}*
|
4
|
+
|
5
|
+
== Dependencies
|
6
|
+
|
7
|
+
Beautiful treetop[http://treetop.rubyforge.org/] is needed for parsing
|
8
|
+
regular expressions, so we can generate random character strings matching
|
9
|
+
a regexp.
|
10
|
+
|
11
|
+
Slim bacon[http://rubygems.org/gems/bacon/] is used for executing specs.
|
12
|
+
You need not install bacon to use Randall.
|
13
|
+
|
14
|
+
== Install
|
15
|
+
|
16
|
+
gem install randall
|
17
|
+
|
18
|
+
Randall uses Fiber, so can be used on Ruby 1.9 or JRuby only.
|
19
|
+
|
20
|
+
Tested on:
|
21
|
+
- ruby 1.9.1 p378
|
22
|
+
- jruby 1.5.0 with option --1.9
|
23
|
+
|
24
|
+
== Examples
|
25
|
+
|
26
|
+
=== Canonical approach
|
27
|
+
|
28
|
+
=== Numbers
|
29
|
+
|
30
|
+
=== String
|
31
|
+
|
32
|
+
=== Array and Hash
|
33
|
+
|
34
|
+
|
35
|
+
=== Objects
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
=== Syntax sugars
|
40
|
+
|
41
|
+
Some methods are provided to specify the type and restrictions.
|
42
|
+
|
43
|
+
r = Randall
|
44
|
+
r.integers.less_than(100) # Same as r.generate(Integer, :less_than => 100)
|
45
|
+
r.floats.gt(100) # => r.generate(Float, :greater_than => 100)
|
46
|
+
r.floats.clost_to(0)
|
47
|
+
r.integers.in_range(1..2)
|
48
|
+
|
49
|
+
For generating strings.
|
50
|
+
number = Randall.strings.that.match(/[0-9]+/)
|
51
|
+
|
52
|
+
Above example has the same effect as
|
53
|
+
<code>number = Randall.new(String, :like => /[0-9]+/)</code>.
|
54
|
+
|
55
|
+
Generate Arrays whose elements are generated by +number+.
|
56
|
+
Randall.arrays.of(number).size(100)
|
57
|
+
Generate Hashes whose keys are
|
58
|
+
Randall.hashes.from(number).to(String).size(10)
|
59
|
+
|
60
|
+
==== Some monkey patching
|
61
|
+
|
62
|
+
- +Array#pick+ randomly pick an element from the receiver.
|
63
|
+
- +Regexp#rand+ generates a String that matches the receiver.
|
64
|
+
|
65
|
+
Monkey patching is disabled by default. You must patch explicitly, by calling
|
66
|
+
+RandallMonkey.patch+.
|
67
|
+
|
68
|
+
== Limitations on Generating Strings for Regular Expressions
|
69
|
+
|
70
|
+
* Anchors, except +^+ and +$+, are ignored.
|
71
|
+
* +\p{*}+ and +\P{*}+ are not supported.
|
72
|
+
* Unicode characters are not supported.
|
73
|
+
* +(?*)+ extension is not supported.
|
74
|
+
* Back-reference is not supported.
|
75
|
+
|
76
|
+
== License
|
77
|
+
|
78
|
+
The MIT license.
|
79
|
+
|
80
|
+
Copyright (2010), Diego Che (chekenan@gmail.com)
|
81
|
+
|
82
|
+
== Links
|
83
|
+
|
84
|
+
* randexp[http://github.com/benburkert/randexp] - generates random data from a Regular Expression. The +Array#pick+ is from here.
|
85
|
+
|
86
|
+
* {Source Repository}[http://github.com/benburkert/randexp] - You can also report bugs to the {github issue tracker}[http://github.com/dche/randall/issues].
|
87
|
+
|
@@ -0,0 +1,186 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# charclass.rb -- Character classes parsing for treetop parser.
|
4
|
+
#
|
5
|
+
# copyright (c) 2010, Diego Che (chekenan@gmail.com)
|
6
|
+
#
|
7
|
+
|
8
|
+
# The grammar module.
|
9
|
+
module RandallRegExp # :nodoc: all
|
10
|
+
module CharSets
|
11
|
+
|
12
|
+
def strseq(from, to)
|
13
|
+
(from..to).map(&:chr)
|
14
|
+
end
|
15
|
+
|
16
|
+
def all_chars
|
17
|
+
@@ascii ||= strseq(0, 0x7f)
|
18
|
+
end
|
19
|
+
alias :ascii :all_chars
|
20
|
+
|
21
|
+
def graph
|
22
|
+
@@graph ||= strseq(0x21, 0x7e)
|
23
|
+
end
|
24
|
+
|
25
|
+
def control
|
26
|
+
@@control ||= strseq(0, 0x1f) << 0x7f
|
27
|
+
end
|
28
|
+
|
29
|
+
def decimal_number
|
30
|
+
@@decimal_number ||= (0..9).to_a.map(&:to_s)
|
31
|
+
end
|
32
|
+
alias :digit :decimal_number
|
33
|
+
|
34
|
+
def lower_letter
|
35
|
+
@@lower_letter ||= 26.times.map do |i|
|
36
|
+
('a'.ord + i).chr
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def upper_letter
|
41
|
+
@@upper_letter ||= self.lower_letter.map(&:upcase)
|
42
|
+
end
|
43
|
+
|
44
|
+
def letter
|
45
|
+
@@letter ||= self.lower_letter + self.upper_letter
|
46
|
+
end
|
47
|
+
|
48
|
+
def space
|
49
|
+
@@space ||= ["\s", "\t", "\r", "\n", "\v", "\f"]
|
50
|
+
end
|
51
|
+
|
52
|
+
def punctuation
|
53
|
+
# CHECK: which chars belong to this set?
|
54
|
+
@@punct ||= ['!', "\"", '#', #'$',
|
55
|
+
'%', '&', "'", '(', ')',
|
56
|
+
'*', #'+',
|
57
|
+
'-', '.', '/', ':', ';', '<', #'=', '>',
|
58
|
+
'?', '@', '[', '\\', ']', #'^',
|
59
|
+
'_', #'`',
|
60
|
+
'{', #'|',
|
61
|
+
'}', #'~'
|
62
|
+
]
|
63
|
+
end
|
64
|
+
|
65
|
+
def xchar
|
66
|
+
c = ['a', 'b', 'c', 'd', 'e']
|
67
|
+
c += c.map(&:upcase)
|
68
|
+
end
|
69
|
+
|
70
|
+
def xdigit
|
71
|
+
@@xdigit ||= self.digit + self.xchar
|
72
|
+
end
|
73
|
+
|
74
|
+
def word
|
75
|
+
@@word ||= self.letter + self.digit << '_'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class CharClass < Treetop::Runtime::SyntaxNode
|
80
|
+
include CharSets
|
81
|
+
|
82
|
+
# '[' neg:'^'? comp:(cc_comp / char_class)+ amps:('&&' char_class)? ']'
|
83
|
+
def chars
|
84
|
+
return @chars if @chars
|
85
|
+
|
86
|
+
# build charset
|
87
|
+
@chars = comp.elements.map(&:chars).reduce([], :+).uniq
|
88
|
+
@chars &= amps.elements[1].chars unless amps.empty?
|
89
|
+
@chars = self.all_chars - @chars unless neg.empty?
|
90
|
+
|
91
|
+
@chars
|
92
|
+
end
|
93
|
+
|
94
|
+
def rand
|
95
|
+
self.chars
|
96
|
+
@chars.empty? ? '' : @chars[Kernel.rand(@chars.size)]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class PosixCharClass < CharClass
|
101
|
+
|
102
|
+
def chars
|
103
|
+
return @chars if @chars
|
104
|
+
|
105
|
+
@chars = case pcc_name.text_value
|
106
|
+
when 'alnum'
|
107
|
+
self.letter + self.digit
|
108
|
+
when 'alpha'
|
109
|
+
self.letter
|
110
|
+
when 'ascii'
|
111
|
+
self.ascii
|
112
|
+
when 'blank'
|
113
|
+
["\s", "\t"]
|
114
|
+
when 'cntrl'
|
115
|
+
self.control
|
116
|
+
when 'digit'
|
117
|
+
self.digit
|
118
|
+
when 'graph'
|
119
|
+
self.graph
|
120
|
+
when 'lower'
|
121
|
+
self.lower
|
122
|
+
when 'print'
|
123
|
+
self.graph << "\s"
|
124
|
+
when 'punct'
|
125
|
+
self.punctuation
|
126
|
+
when 'space'
|
127
|
+
self.space
|
128
|
+
when 'upper'
|
129
|
+
self.upper
|
130
|
+
when 'xdigit'
|
131
|
+
self.xdigit
|
132
|
+
when 'word'
|
133
|
+
self.word
|
134
|
+
else
|
135
|
+
[]
|
136
|
+
end
|
137
|
+
|
138
|
+
@chars = self.all_chars - @chars unless neg.empty?
|
139
|
+
@chars
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
class PerlCharClass < CharClass
|
144
|
+
def chars
|
145
|
+
@chars ||= case self.text_value
|
146
|
+
when '\s'
|
147
|
+
self.space
|
148
|
+
when '\S'
|
149
|
+
self.all_chars - self.space
|
150
|
+
when '\d'
|
151
|
+
self.digit
|
152
|
+
when '\D'
|
153
|
+
self.all_chars - self.digit
|
154
|
+
when '\h'
|
155
|
+
self.xdigit
|
156
|
+
when '\H'
|
157
|
+
self.all_chars - self.xdigit
|
158
|
+
when '\w'
|
159
|
+
self.word
|
160
|
+
when '\W'
|
161
|
+
self.all_chars - self.word
|
162
|
+
else
|
163
|
+
[]
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
class SpanCharClass < CharClass
|
169
|
+
|
170
|
+
def chars
|
171
|
+
@chars ||= case self.text_value
|
172
|
+
when /([0-9a-zA-Z])\-([0-9a-zA-Z])/
|
173
|
+
$1 <= $2 ? self.strseq($1, $2) : ''
|
174
|
+
else
|
175
|
+
self.text_value.split('').uniq
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def strseq(from, to)
|
180
|
+
(to.ord - from.ord).times.map do |i|
|
181
|
+
(from.ord + i).chr
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# copyright (c) 2010, Diego Che
|
4
|
+
#
|
5
|
+
|
6
|
+
module RandallMonkey
|
7
|
+
|
8
|
+
module PickableArray
|
9
|
+
# Randomly pick an element from the receiver.
|
10
|
+
def pick
|
11
|
+
self[Kernel.rand(self.size)]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module RandableRegexp
|
16
|
+
# Generate a String that matches the receiver.
|
17
|
+
def rand
|
18
|
+
@randall ||= Randall.new(String, :like => self)
|
19
|
+
@randall.next
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Enable the monkey patch.
|
24
|
+
# After this message, you have +Array#pick+ and +Regexp#rand+.
|
25
|
+
def self.patch
|
26
|
+
Array.class_eval do
|
27
|
+
include PickableArray
|
28
|
+
end
|
29
|
+
|
30
|
+
Regexp.class_eval do
|
31
|
+
include RandableRegexp
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# copyright (c) 2010, Diego Che
|
4
|
+
#
|
5
|
+
|
6
|
+
module RandallClassSugar
|
7
|
+
def strings
|
8
|
+
self.new(String)
|
9
|
+
end
|
10
|
+
|
11
|
+
def integers
|
12
|
+
self.new(Integer)
|
13
|
+
end
|
14
|
+
|
15
|
+
def floats
|
16
|
+
self.new(Float)
|
17
|
+
end
|
18
|
+
|
19
|
+
def arrays
|
20
|
+
self.new(Array)
|
21
|
+
end
|
22
|
+
|
23
|
+
def hashes
|
24
|
+
self.new(Hash)
|
25
|
+
end
|
26
|
+
|
27
|
+
def instances_of(cls)
|
28
|
+
self.new(cls)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module RandallInstanceSugar
|
33
|
+
def that
|
34
|
+
self
|
35
|
+
end
|
36
|
+
alias :with :that
|
37
|
+
alias :and :that
|
38
|
+
|
39
|
+
def match(re)
|
40
|
+
unless @type == String then
|
41
|
+
raise ArgumentError, 'Applied to generator for String only.'
|
42
|
+
end
|
43
|
+
|
44
|
+
self.restrict :like => re
|
45
|
+
end
|
46
|
+
|
47
|
+
def less_than(number)
|
48
|
+
unless @type == Float or @type == Integer then
|
49
|
+
raise ArgumentError, 'Applied to generator for String only.'
|
50
|
+
end
|
51
|
+
|
52
|
+
@options[:less_than] = number
|
53
|
+
self
|
54
|
+
end
|
55
|
+
alias :lt :less_than
|
56
|
+
|
57
|
+
def greater_than(number)
|
58
|
+
unless @type == Float or @type == Integer then
|
59
|
+
raise ArgumentError, 'Applied to generator for number only.'
|
60
|
+
end
|
61
|
+
|
62
|
+
@options[:greater_than] = number
|
63
|
+
self
|
64
|
+
end
|
65
|
+
alias :gt :greater_than
|
66
|
+
|
67
|
+
def close_to(f)
|
68
|
+
unless @type == Float then
|
69
|
+
raise ArgumentError, 'Applied to generator for number only.'
|
70
|
+
end
|
71
|
+
|
72
|
+
@options[:close_to] = f
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
def in_range(rg)
|
77
|
+
unless @type == Float or @type == Integer then
|
78
|
+
raise ArgumentError, 'Applied to generator for number only.'
|
79
|
+
end
|
80
|
+
|
81
|
+
@options[:range] = rg
|
82
|
+
self
|
83
|
+
end
|
84
|
+
alias :between :in_range
|
85
|
+
|
86
|
+
def of(type)
|
87
|
+
unless @type == Array then
|
88
|
+
raise ArgumentError, 'Applied to generator for Array only.'
|
89
|
+
end
|
90
|
+
|
91
|
+
case type
|
92
|
+
when Randall
|
93
|
+
@options[:item] = type
|
94
|
+
when Class
|
95
|
+
@options[:type] = type
|
96
|
+
else
|
97
|
+
@options[:type] = type.class
|
98
|
+
end
|
99
|
+
|
100
|
+
self
|
101
|
+
end
|
102
|
+
|
103
|
+
def with_size(number)
|
104
|
+
unless @type == Hash or @type == Array then
|
105
|
+
raise ArgumentError, 'Applied to generator for Hash and Array only.'
|
106
|
+
end
|
107
|
+
|
108
|
+
@options[:size] = number
|
109
|
+
self
|
110
|
+
end
|
111
|
+
|
112
|
+
def from(type)
|
113
|
+
unless @type == Hash then
|
114
|
+
raise ArgumentError, 'Applied to generator for Hash only.'
|
115
|
+
end
|
116
|
+
|
117
|
+
case type
|
118
|
+
when Class
|
119
|
+
@options[:key_type] = type
|
120
|
+
when Randall
|
121
|
+
@options[:key] = type
|
122
|
+
else
|
123
|
+
@options[:key_type] = type.class
|
124
|
+
end
|
125
|
+
self
|
126
|
+
end
|
127
|
+
|
128
|
+
def to(type)
|
129
|
+
unless @type == Hash then
|
130
|
+
raise ArgumentError, 'Applied to generator for Hash only.'
|
131
|
+
end
|
132
|
+
|
133
|
+
case type
|
134
|
+
when Class
|
135
|
+
@options[:value_type] = type
|
136
|
+
when Randall
|
137
|
+
@options[:value] = type
|
138
|
+
else
|
139
|
+
@options[:value_type] = type.class
|
140
|
+
end
|
141
|
+
|
142
|
+
self
|
143
|
+
end
|
144
|
+
|
145
|
+
def with_arguments(*args)
|
146
|
+
self.restrict :args => args
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
data/lib/randall.rb
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# randall.rb
|
4
|
+
#
|
5
|
+
# copyright (c) 2010, Diego Che (chekenan@gmail.com)
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'treetop'
|
9
|
+
|
10
|
+
require File.join(File.dirname(__FILE__), 'randall/charclass')
|
11
|
+
Treetop.load File.join(File.dirname(__FILE__), 'regexp')
|
12
|
+
|
13
|
+
require File.join(File.dirname(__FILE__), 'randall/sugar')
|
14
|
+
require File.join(File.dirname(__FILE__), 'randall/monkey_patch')
|
15
|
+
|
16
|
+
# A random value generator.
|
17
|
+
#
|
18
|
+
#
|
19
|
+
class Randall
|
20
|
+
include RandallInstanceSugar
|
21
|
+
class <<self; self; end.class_eval do
|
22
|
+
include RandallClassSugar
|
23
|
+
end
|
24
|
+
|
25
|
+
# The generated random value. It is changed after +rand+ or +next+ are
|
26
|
+
# called.
|
27
|
+
attr_reader :value
|
28
|
+
# The +Class+ for which the receiver should generate instances.
|
29
|
+
attr_reader :type
|
30
|
+
|
31
|
+
# The regexp parser for generating random String.
|
32
|
+
@@reparser = RandallRegExpParser.new
|
33
|
+
|
34
|
+
# Creates a Randall object.
|
35
|
+
#
|
36
|
+
# [type] Type of the values the Randall object should generate.
|
37
|
+
# [opts] Optional. The restrictions the generated random values should
|
38
|
+
# satisfy. See documents of +Randall+ for restrictions
|
39
|
+
# of each supported types, and how this arguments is
|
40
|
+
# specified.
|
41
|
+
def initialize(type = Integer, opts = {})
|
42
|
+
@type = type
|
43
|
+
|
44
|
+
self.restrict opts
|
45
|
+
|
46
|
+
@worker = Fiber.new do
|
47
|
+
loop do
|
48
|
+
if String == @type
|
49
|
+
@value = g_str
|
50
|
+
elsif Integer == @type
|
51
|
+
@value = g_int
|
52
|
+
elsif Float == @type
|
53
|
+
@value = g_float
|
54
|
+
elsif Array == @type
|
55
|
+
@value = g_array
|
56
|
+
elsif Hash == @type
|
57
|
+
@value = g_hash
|
58
|
+
elsif Class === @type
|
59
|
+
@value = g_any
|
60
|
+
else
|
61
|
+
@value = nil
|
62
|
+
end
|
63
|
+
|
64
|
+
Fiber.yield @value
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Same as +self.value+.
|
70
|
+
def v
|
71
|
+
self.value
|
72
|
+
end
|
73
|
+
|
74
|
+
# Generates value for given type and restrictions.
|
75
|
+
# This method is used when you want to reuse a Randall object to generate
|
76
|
+
# values for alternate type.
|
77
|
+
def rand(type, opts = {})
|
78
|
+
@type = type
|
79
|
+
|
80
|
+
self.restrict opts
|
81
|
+
@worker.resume
|
82
|
+
end
|
83
|
+
alias :r :rand
|
84
|
+
|
85
|
+
# Set the restriction. You use this method to change the restrictions
|
86
|
+
# the generated values should satisfy.
|
87
|
+
def restrict(opts)
|
88
|
+
@options = opts
|
89
|
+
parse_regexp if @type == String
|
90
|
+
|
91
|
+
self
|
92
|
+
end
|
93
|
+
|
94
|
+
# Generate another value under current type and restrictions.
|
95
|
+
def next
|
96
|
+
@worker.resume
|
97
|
+
end
|
98
|
+
alias :n :next
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def g_str
|
103
|
+
@str_gen.rand
|
104
|
+
end
|
105
|
+
|
106
|
+
def g_int
|
107
|
+
@options.delete :close_to
|
108
|
+
Integer(g_float)
|
109
|
+
end
|
110
|
+
|
111
|
+
def g_float
|
112
|
+
if @options[:close_to]
|
113
|
+
epsilon = @options[:epsilon] || 1e-3
|
114
|
+
@options[:close_to] - epsilon + Kernel.rand * (2 * epsilon)
|
115
|
+
elsif @options[:range]
|
116
|
+
min = @options[:range].min
|
117
|
+
max = @options[:range].max
|
118
|
+
|
119
|
+
min + Kernel.rand * (max - min)
|
120
|
+
elsif @options[:greater_than] or @options[:less_than]
|
121
|
+
max = @options[:less_than] || 10 ** 24
|
122
|
+
min = @options[:greater_than] || max - 10 ** 24
|
123
|
+
|
124
|
+
if min >= max
|
125
|
+
if @options[:greater_than] and @options[:less_than]
|
126
|
+
raise ArgumentError, ":less_than(#{max}) should be greater than :grater_than(#{min})."
|
127
|
+
elsif @options[:greater_than]
|
128
|
+
max = min + 10 ** 24
|
129
|
+
end
|
130
|
+
end
|
131
|
+
min + Kernel.rand * (max - min)
|
132
|
+
else
|
133
|
+
Kernel.rand * (10 ** 6)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def g_array
|
138
|
+
sz = @options[:size] || rand_size
|
139
|
+
item_gen = @options[:item] || self.class.new(@options[:type] || rand_type)
|
140
|
+
|
141
|
+
v = []
|
142
|
+
sz.times do
|
143
|
+
v << item_gen.next
|
144
|
+
end
|
145
|
+
v
|
146
|
+
end
|
147
|
+
|
148
|
+
def g_hash
|
149
|
+
sz = @options[:size] || rand_size
|
150
|
+
key_gen = @options[:key] || self.class.new(@options[:key_type] || rand_type)
|
151
|
+
value_gen = @options[:value] || self.class.new(@options[:value_type] || rand_type)
|
152
|
+
|
153
|
+
v = {}
|
154
|
+
dup_retry = 3
|
155
|
+
sz.times do
|
156
|
+
k = key_gen.next while v[k] && (dup_retry -= 1) > 0
|
157
|
+
v[key_gen.next] = value_gen.next
|
158
|
+
end
|
159
|
+
if v.size < sz && @options[:size]
|
160
|
+
$stderr.puts "[Warning]: The key space is too small to generate #{sz} pairs."
|
161
|
+
$stderr.puts key_gen.type
|
162
|
+
end
|
163
|
+
v
|
164
|
+
end
|
165
|
+
|
166
|
+
# Generate an Object.
|
167
|
+
def g_any
|
168
|
+
begin
|
169
|
+
args = @options[:args] || []
|
170
|
+
args = [] unless args.is_a? Array
|
171
|
+
|
172
|
+
@type.new *args
|
173
|
+
rescue Exception # catch all exceptions
|
174
|
+
nil
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def rand_size(max = 100)
|
179
|
+
Kernel.rand max
|
180
|
+
end
|
181
|
+
|
182
|
+
def rand_type
|
183
|
+
[Integer, Float, String][Kernel.rand 3]
|
184
|
+
end
|
185
|
+
|
186
|
+
def parse_regexp
|
187
|
+
return self unless @type == String
|
188
|
+
re = @options[:like].is_a?(Regexp) ? @options[:like] : /.+/
|
189
|
+
|
190
|
+
@str_gen = @@reparser.parse re.source
|
191
|
+
raise RuntimeError, "Randall is not strong enough (yet) to handle regexp: #{re.inspect}" if @str_gen.nil?
|
192
|
+
|
193
|
+
self
|
194
|
+
end
|
195
|
+
end
|
data/lib/regexp.treetop
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# copyright (c) 2010, Diego Che (chekenan@gmail.com)
|
4
|
+
#
|
5
|
+
|
6
|
+
grammar RandallRegExp
|
7
|
+
rule regexp
|
8
|
+
pre:'^'? exp_body post:'$'? {
|
9
|
+
include CharSets
|
10
|
+
|
11
|
+
def rand
|
12
|
+
(pre.empty? ? self.any_str : '') +
|
13
|
+
exp_body.rand +
|
14
|
+
(post.empty? ? self.any_str : '')
|
15
|
+
end
|
16
|
+
|
17
|
+
def any_str
|
18
|
+
''
|
19
|
+
end
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
rule exp_body
|
24
|
+
clause rc:clause* sel:choice? {
|
25
|
+
def rand
|
26
|
+
head = clause.rand + rc.elements.map(&:rand).reduce('', :+)
|
27
|
+
unless sel.empty? then
|
28
|
+
Kernel.rand(2) == 0 ? head : sel.rand
|
29
|
+
else
|
30
|
+
head
|
31
|
+
end
|
32
|
+
end
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
rule clause
|
37
|
+
distinguishable q:quantifier? opt:'?'? {
|
38
|
+
def rand
|
39
|
+
(q.empty? ? 1 : q.repeatition).times.map do
|
40
|
+
distinguishable.rand
|
41
|
+
end.reduce('', :+)
|
42
|
+
end
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
rule distinguishable
|
47
|
+
group / constants / char_class / escapes / '.' {
|
48
|
+
include CharSets
|
49
|
+
|
50
|
+
def rand
|
51
|
+
g = self.graph
|
52
|
+
g[Kernel.rand(g.size)]
|
53
|
+
end
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
rule group
|
58
|
+
'(' exp_body ')' {
|
59
|
+
def rand
|
60
|
+
exp_body.rand
|
61
|
+
end
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
rule choice
|
66
|
+
'|' exp_body {
|
67
|
+
def rand
|
68
|
+
exp_body.rand
|
69
|
+
end
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
rule constants
|
74
|
+
[^\\\[\(\{\*\.\+\?\]\}\)\^\$\|] {
|
75
|
+
def rand
|
76
|
+
self.text_value
|
77
|
+
end
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
rule char_class
|
82
|
+
'\\' char_class_escape <PerlCharClass>
|
83
|
+
/ '[' neg:'^'? comp:(cc_comp / char_class)+ amps:('&&' char_class)? ']' <CharClass>
|
84
|
+
end
|
85
|
+
|
86
|
+
rule char_class_escape
|
87
|
+
'd' / 'D' / 'h' / 'H' / 's' / 'S' / 'w' / 'W' / encoding_property
|
88
|
+
end
|
89
|
+
|
90
|
+
rule encoding_property
|
91
|
+
('p' / 'P') '{' neg:'^'? encoding '}'
|
92
|
+
end
|
93
|
+
|
94
|
+
rule encoding
|
95
|
+
'Alnum' / 'Alpha' / 'Blank' / 'Cntrl' / 'Digit' / 'Graph'
|
96
|
+
/ 'Lower' / 'Print' / 'Punct' / 'Space' / 'Upper' / 'XDigit'
|
97
|
+
/ 'Word' / 'ASCII' / [a-zA-Z]+
|
98
|
+
end
|
99
|
+
|
100
|
+
rule cc_comp
|
101
|
+
posix_char_class / span_char_class / escaped_chars
|
102
|
+
end
|
103
|
+
|
104
|
+
rule posix_char_class
|
105
|
+
'[:' neg:'^'? pcc_name ':]' <PosixCharClass>
|
106
|
+
end
|
107
|
+
|
108
|
+
rule pcc_name
|
109
|
+
'alnum' / 'alpha' / 'ascii' / 'blank' / 'cntrl' / 'digit'/ 'graph'
|
110
|
+
/ 'lower' / 'print' / 'punct' / 'space' / 'uppder' / 'xdigit' / 'word'
|
111
|
+
end
|
112
|
+
|
113
|
+
rule span_char_class
|
114
|
+
[0-9] '-' [0-9] <SpanCharClass>
|
115
|
+
/ [a-z] '-' [a-z] <SpanCharClass>
|
116
|
+
/ [A-Z] '-' [A-Z] <SpanCharClass>
|
117
|
+
/ [0-9a-zA-Z^$.+*(){}]+ <SpanCharClass> # TODO: incomplete chars.
|
118
|
+
end
|
119
|
+
|
120
|
+
rule escaped_chars
|
121
|
+
('\[' / '\]' / '\-' / '\/') {
|
122
|
+
def chars
|
123
|
+
[self.text_value]
|
124
|
+
end
|
125
|
+
|
126
|
+
def rand
|
127
|
+
self.chars
|
128
|
+
self.text_value[1]
|
129
|
+
end
|
130
|
+
}
|
131
|
+
end
|
132
|
+
|
133
|
+
rule escapes
|
134
|
+
'\\' [^dDhHsSwWpPx] {
|
135
|
+
def rand
|
136
|
+
case self.text_value
|
137
|
+
when /\\[aAzZbB]/
|
138
|
+
''
|
139
|
+
when /\\x(\d+)/
|
140
|
+
# TODO: ascii only. To support unicode.
|
141
|
+
$1.to_a.chr
|
142
|
+
when '\r'
|
143
|
+
"\r"
|
144
|
+
when '\n'
|
145
|
+
"\n"
|
146
|
+
when '\t'
|
147
|
+
"\t"
|
148
|
+
else
|
149
|
+
self.text_value[1]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
}
|
153
|
+
end
|
154
|
+
|
155
|
+
rule quantifier
|
156
|
+
('*' / '+' / '?' / '{' ','? [0-9]+ '}' / '{' [0-9]+ ',' ([0-9]+)? '}') {
|
157
|
+
def repeatition
|
158
|
+
case self.text_value
|
159
|
+
when '*'
|
160
|
+
Kernel.rand(20)
|
161
|
+
when '+'
|
162
|
+
1 + Kernel.rand(20)
|
163
|
+
when '?'
|
164
|
+
Kernel.rand(2)
|
165
|
+
when /\{\,([0-9]+)\}/
|
166
|
+
Kernel.rand($1.to_i + 1)
|
167
|
+
when /\{([0-9]+)\}/
|
168
|
+
$1.to_i
|
169
|
+
when /\{([0-9]+)\,\}/
|
170
|
+
$1.to_i + Kernel.rand(20)
|
171
|
+
when /\{([0-9]+)\,([0-9]+)\}/
|
172
|
+
# TODO: ensure $2 >= $1
|
173
|
+
$1.to_i + Kernel.rand($2.to_i - $1.to_i + 1)
|
174
|
+
else
|
175
|
+
0
|
176
|
+
end
|
177
|
+
end
|
178
|
+
}
|
179
|
+
end
|
180
|
+
end
|
metadata
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: randall
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 1
|
10
|
+
version: 0.1.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Diego Che
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-06-03 00:00:00 +08:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: treetop
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 9
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 4
|
33
|
+
- 7
|
34
|
+
version: 1.4.7
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: bacon
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 19
|
46
|
+
segments:
|
47
|
+
- 1
|
48
|
+
- 1
|
49
|
+
- 0
|
50
|
+
version: 1.1.0
|
51
|
+
type: :development
|
52
|
+
version_requirements: *id002
|
53
|
+
description: Generate random instances of any classes.
|
54
|
+
email: chekenan@gmail.com
|
55
|
+
executables: []
|
56
|
+
|
57
|
+
extensions: []
|
58
|
+
|
59
|
+
extra_rdoc_files: []
|
60
|
+
|
61
|
+
files:
|
62
|
+
- README.rdoc
|
63
|
+
- LICENSE
|
64
|
+
- lib/regexp.treetop
|
65
|
+
- lib/randall/charclass.rb
|
66
|
+
- lib/randall/monkey_patch.rb
|
67
|
+
- lib/randall/sugar.rb
|
68
|
+
- lib/randall.rb
|
69
|
+
has_rdoc: true
|
70
|
+
homepage:
|
71
|
+
licenses: []
|
72
|
+
|
73
|
+
post_install_message:
|
74
|
+
rdoc_options:
|
75
|
+
- --title
|
76
|
+
- Randall
|
77
|
+
- --main
|
78
|
+
- README.rdoc
|
79
|
+
- --line-numbers
|
80
|
+
- --exclude="spec/*"
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
hash: 29
|
89
|
+
segments:
|
90
|
+
- 1
|
91
|
+
- 9
|
92
|
+
version: "1.9"
|
93
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
hash: 3
|
99
|
+
segments:
|
100
|
+
- 0
|
101
|
+
version: "0"
|
102
|
+
requirements: []
|
103
|
+
|
104
|
+
rubyforge_project:
|
105
|
+
rubygems_version: 1.3.7
|
106
|
+
signing_key:
|
107
|
+
specification_version: 3
|
108
|
+
summary: Generate random instances of any classes.
|
109
|
+
test_files: []
|
110
|
+
|