randall 0.1.1
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/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
|
+
|