rantly 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +5 -0
- data/README.textile +66 -62
- data/Rakefile +1 -0
- data/VERSION.yml +3 -3
- data/lib/rantly/generator.rb +16 -5
- data/lib/rantly/property.rb +10 -0
- data/rantly.gemspec +1 -1
- metadata +48 -36
- data/.gitignore +0 -5
data/Gemfile
ADDED
data/README.textile
CHANGED
@@ -9,53 +9,44 @@ Its implementation has no alien mathematics inside. Completely side-effect-free-
|
|
9
9
|
h1. Install
|
10
10
|
|
11
11
|
<pre><code>
|
12
|
-
$ gem install
|
12
|
+
$ gem install rantly
|
13
13
|
</code></pre>
|
14
14
|
|
15
15
|
<pre><code>
|
16
|
-
$ irb
|
17
|
-
>
|
18
|
-
> require 'rant'
|
19
|
-
> Rant.gen.value { [integer,float] }
|
16
|
+
$ irb -rrantly
|
17
|
+
> Rantly { [integer,float] } # same as Rantly.value { integer }
|
20
18
|
=> [20991307, 0.025756845811823]
|
21
|
-
>
|
19
|
+
> Rantly { [integer,float]}
|
22
20
|
=> [-376856492, 0.452245765751706]
|
21
|
+
> Rantly(5) { integer } # same as Rantly.map(5) { integer }
|
22
|
+
=> [-1843396915550491870, -1683855015308353854, -2291347782549033959, -951461511269053584, 483265231542292652]
|
23
23
|
</code></pre>
|
24
24
|
|
25
|
-
|
26
25
|
h1. Data Generation
|
27
26
|
|
28
|
-
You can create random generators from the Rant class. Rant.gen is just returns a class instance of Rant.
|
29
|
-
|
30
|
-
<pre><code>
|
31
|
-
> gen = Rant.new
|
32
|
-
> gen.value { [integer,float] }
|
33
|
-
=> [-163260081, 0.356075765934108]
|
34
|
-
</code></pre>
|
35
|
-
|
36
27
|
h2. Getting Random Data Values
|
37
28
|
|
38
29
|
<pre><code>
|
39
|
-
|
30
|
+
Rantly#map(n,limit=10,&block)
|
40
31
|
call the generator n times, and collect values
|
41
|
-
|
32
|
+
Rantly#each(n,limit=10,&block)
|
42
33
|
call a random block n times
|
43
|
-
|
34
|
+
Rantly#value(limit=10,&block)
|
44
35
|
call a random block once, and get its value.
|
45
36
|
</code></pre>
|
46
37
|
|
47
38
|
To collect an array of random data,
|
48
39
|
|
49
40
|
<pre><code>
|
50
|
-
# we want 5
|
51
|
-
>
|
41
|
+
# we want 5 random integers
|
42
|
+
> Rantly(5) { integer }
|
52
43
|
=> [-380638946, -29645239, 344840868, 308052180, -154360970]
|
53
44
|
</code></pre>
|
54
45
|
|
55
46
|
To iterate over random data,
|
56
47
|
|
57
48
|
<pre><code>
|
58
|
-
>
|
49
|
+
> Rantly.each(5) { puts integer }
|
59
50
|
296971291
|
60
51
|
504994512
|
61
52
|
-402790444
|
@@ -67,7 +58,7 @@ To iterate over random data,
|
|
67
58
|
To get one value of random data,
|
68
59
|
|
69
60
|
<pre><code>
|
70
|
-
>
|
61
|
+
> Rantly { integer }
|
71
62
|
=> 278101042
|
72
63
|
</code></pre>
|
73
64
|
|
@@ -76,15 +67,15 @@ The optional argument @limit@ is used with generator guard. By default, if you w
|
|
76
67
|
This almost always succeeds,
|
77
68
|
|
78
69
|
<pre><code>
|
79
|
-
>
|
70
|
+
> Rantly(5) { i = integer; guard i > 0; i }
|
80
71
|
=> [511765059, 250554234, 305947804, 127809156, 285960387]
|
81
72
|
</code></pre>
|
82
73
|
|
83
74
|
This always fails,
|
84
75
|
|
85
76
|
<pre><code>
|
86
|
-
>
|
87
|
-
|
77
|
+
> Rantly(10) { guard integer.is_a?(Float) }
|
78
|
+
Rantly::TooManyTries: Exceed gen limit 100: 101 failed guards)
|
88
79
|
</code></pre>
|
89
80
|
|
90
81
|
h2. Random Generating Methods
|
@@ -94,17 +85,17 @@ The API is similiar to QuickCheck, but not exactly the same. In particular @choo
|
|
94
85
|
h3. Simple Randomness
|
95
86
|
|
96
87
|
<pre><code>
|
97
|
-
|
88
|
+
Rantly#integer(n=nil)
|
98
89
|
random positive or negative integer. Fixnum only.
|
99
|
-
|
90
|
+
Rantly#range(lo,hi)
|
100
91
|
random integer between lo and hi.
|
101
|
-
|
92
|
+
Rantly#float
|
102
93
|
random float
|
103
|
-
|
94
|
+
Rantly#bool
|
104
95
|
true or false
|
105
|
-
|
96
|
+
Rantly#literal(value)
|
106
97
|
No-op. returns value.
|
107
|
-
|
98
|
+
Rantly#choose(*vals)
|
108
99
|
Pick one value from among vals.
|
109
100
|
</code></pre>
|
110
101
|
|
@@ -113,40 +104,40 @@ h3. Meta Randomness
|
|
113
104
|
A rant generator is just a mini interpreter. It's often useful to go meta,
|
114
105
|
|
115
106
|
<pre><code>
|
116
|
-
|
107
|
+
Rantly#call(gen)
|
117
108
|
If gen is a Symbol, just do a method call with send.
|
118
109
|
If gen is an Array, the first element of the array is the method name, the rest are args.
|
119
110
|
If gen is a Proc, instance_eval it with the generator.
|
120
111
|
</code></pre>
|
121
112
|
|
122
113
|
<pre><code>
|
123
|
-
>
|
114
|
+
> Rantly { call(:integer) }
|
124
115
|
=> -240998958
|
125
116
|
</code></pre>
|
126
117
|
|
127
118
|
<pre><code>
|
128
|
-
>
|
119
|
+
> Rantly { call([:range,0,10]) }
|
129
120
|
=> 2
|
130
121
|
</code></pre>
|
131
122
|
|
132
123
|
<pre><code>
|
133
|
-
>
|
124
|
+
> Rantly { call(Proc.new { [integer] })}
|
134
125
|
=> [522807620]
|
135
126
|
</code></pre>
|
136
127
|
|
137
128
|
The @call@ method is useful to implement other abstractions (See next subsection).
|
138
129
|
|
139
130
|
<pre><code>
|
140
|
-
|
141
|
-
Pick a random arg among args, and
|
131
|
+
Rantly#branch(*args)
|
132
|
+
Pick a random arg among args, and Rantly#call it.
|
142
133
|
</code></pre>
|
143
134
|
|
144
135
|
50-50 chance getting an integer or float,
|
145
136
|
|
146
137
|
<pre><code>
|
147
|
-
>
|
138
|
+
> Rantly { branch :integer, :float }
|
148
139
|
=> 0.0489446702931332
|
149
|
-
>
|
140
|
+
> Rantly { branch :integer, :float }
|
150
141
|
=> 494934533
|
151
142
|
</code></pre>
|
152
143
|
|
@@ -154,50 +145,52 @@ Rant#branch(*args)
|
|
154
145
|
h3. Frequencies
|
155
146
|
|
156
147
|
<pre><code>
|
157
|
-
|
158
|
-
Takes a list of 2-tuples, the first of which is the weight, and the second a
|
148
|
+
Rantly#freq(*pairs)
|
149
|
+
Takes a list of 2-tuples, the first of which is the weight, and the second a Rantly#callable value, and returns a random value picked from the pairs. Follows the distribution pattern specified by the weights.
|
159
150
|
</code></pre>
|
160
151
|
|
161
152
|
Twice as likely to get a float than integer. Never gets a ranged integer.
|
162
153
|
|
163
154
|
<pre><code>
|
164
|
-
>
|
155
|
+
> Rantly { freq [1,:integer], [2,:float], [0,:range,0,10] }
|
165
156
|
</code></pre>
|
166
157
|
|
167
158
|
If the "pair" is not an array, but just a symbol, @freq@ assumes that the weight is 1.
|
168
159
|
|
169
160
|
<pre><code>
|
170
161
|
# 50-50 between integer and float
|
171
|
-
>
|
162
|
+
> Rantly { freq :integer, :float }
|
172
163
|
</code></pre>
|
173
164
|
|
174
|
-
If a "pair" is an Array, but the first element is not an Integer, @freq@ assumes that it's a
|
165
|
+
If a "pair" is an Array, but the first element is not an Integer, @freq@ assumes that it's a Rantly method-call with arguments, and the weight is one.
|
175
166
|
|
176
167
|
<pre><code>
|
177
168
|
# 50-50 chance generating integer limited by 10, or by 20.
|
178
|
-
>
|
169
|
+
> Rantly { freq [:integer,10], [:integer 20] }
|
179
170
|
</code></pre>
|
180
171
|
|
181
172
|
|
182
173
|
|
183
174
|
h3. Sized Structure
|
184
175
|
|
185
|
-
A
|
176
|
+
A Rantly generator keeps track of how large a datastructure it should generate with its @size@ attribute.
|
186
177
|
|
187
178
|
<pre><code>
|
188
|
-
|
179
|
+
Rantly#size
|
189
180
|
returns the current size
|
190
|
-
|
181
|
+
Rantly#sized(n,&block)
|
191
182
|
sets the size for the duration of recursive call of block. Block is instance_eval with the generator.
|
192
183
|
</code></pre>
|
193
184
|
|
194
|
-
|
185
|
+
Rantly provides two methods that depends on the size
|
195
186
|
|
196
187
|
<pre><code>
|
197
|
-
|
198
|
-
returns a sized array consisted of elements by
|
199
|
-
|
188
|
+
Rantly#array(size=default_size,&block)
|
189
|
+
returns a sized array consisted of elements by Rantly#calling random branches.
|
190
|
+
Rantly#string(char_class=:print)
|
200
191
|
returns a sized random string, consisted of only chars from a char_class.
|
192
|
+
Rantly#dict(size=default_size,&block)
|
193
|
+
returns a sized random hash. The generator block should generate tuples of keys and values (arrays that have two elements, the first one is used as key, and the second as value).
|
201
194
|
</code></pre>
|
202
195
|
|
203
196
|
The avaiable char classes for strings are:
|
@@ -219,36 +212,47 @@ The avaiable char classes for strings are:
|
|
219
212
|
</code></pre>
|
220
213
|
|
221
214
|
<pre><code>
|
222
|
-
# sized 10 array of
|
223
|
-
>
|
215
|
+
# sized 10 array of integers
|
216
|
+
> Rantly { array(10) { integer }}
|
224
217
|
=> [417733046, -375385433, 0.967812380000118, 26478621, 0.888588160450082, 250944144, 305584916, -151858342, 0.308123867823313, 0.316824642414253]
|
225
218
|
</code></pre>
|
226
219
|
|
227
220
|
If you set the size once, it applies to all subsequent recursive structures. Here's a sized 10 array of sized 10 strings,
|
228
221
|
|
229
222
|
<pre><code>
|
230
|
-
>
|
223
|
+
> Rantly { sized(10) { array {string}} }
|
231
224
|
=> ["1c}C/,9I#}", "hpA/UWPJ\\j", "H'~ERtI`|]", "%OUaW\\%uQZ", "Z2QdY=G~G!", "H<o|<FARGQ", "g>ojnxGDT3", "]a:L[B>bhb", "_Kl=&{tH^<", "ly]Yfb?`6c"]
|
232
225
|
</code></pre>
|
233
226
|
|
234
227
|
Or a sized 10 array of sized 5 strings,
|
235
228
|
|
236
229
|
<pre><code>
|
237
|
-
>
|
230
|
+
> Rantly {array(10){sized(5) {string}}}
|
238
231
|
=> ["S\"jf ", "d\\F-$", "-_8pa", "IN0iF", "SxRV$", ".{kQ7", "6>;fo", "}.D8)", "P(tS'", "y0v/v"]
|
239
232
|
</code></pre>
|
240
233
|
|
241
|
-
|
234
|
+
Generate a hash that has 5 elements,
|
242
235
|
|
243
236
|
<pre><code>
|
244
|
-
>
|
245
|
-
=>
|
237
|
+
> Rantly { dict { [string,integer] }}
|
238
|
+
{"bR\\qHn"=>247003509502595457,
|
239
|
+
"-Mp '."=>653206579583741142,
|
240
|
+
"gY%<SV"=>-888111605212388599,
|
241
|
+
"+SMn:r"=>-1159506450084197716,
|
242
|
+
"^3gYfQ"=>-2154064981943219558,
|
243
|
+
"= :/\\,"=>433790301059833691}
|
246
244
|
</code></pre>
|
247
245
|
|
246
|
+
The @dict@ generator retries if a key is duplicated. If it fails to generate a unique key after too many tries, it gives up by raising an error:
|
247
|
+
|
248
|
+
<pre><code>
|
249
|
+
> Rantly { dict { ["a",integer] }}
|
250
|
+
Rantly::TooManyTries: Exceed gen limit 60: 60 failed guards)
|
251
|
+
</code></pre>
|
248
252
|
|
249
253
|
h1. Property Testing
|
250
254
|
|
251
|
-
|
255
|
+
Rantly extends Test::Unit for property testing. The extension is in its own module. So you need to require it.
|
252
256
|
|
253
257
|
<pre><code>
|
254
258
|
require 'rant/check'
|
@@ -258,7 +262,7 @@ It defines,
|
|
258
262
|
|
259
263
|
<pre><code>
|
260
264
|
Test::Unit::Assertions#property_of(&block)
|
261
|
-
The block is used to generate random data with a generator. The method returns a
|
265
|
+
The block is used to generate random data with a generator. The method returns a Rantly::Property instance, that has the method 'check'.
|
262
266
|
</code></pre>
|
263
267
|
|
264
268
|
It's like this, using the gem 'shoulda'
|
@@ -270,7 +274,7 @@ should "generate Fixnum only" do
|
|
270
274
|
end
|
271
275
|
</code></pre>
|
272
276
|
|
273
|
-
The check block takes the generated data as its argument. One idiom I find useful is to include a parameter of the random data for the check argument. For example, if I want to check that
|
277
|
+
The check block takes the generated data as its argument. One idiom I find useful is to include a parameter of the random data for the check argument. For example, if I want to check that Rantly#array generates the right sized array, I could say,
|
274
278
|
|
275
279
|
<pre><code>
|
276
280
|
should "generate right sized array" do
|
data/Rakefile
CHANGED
data/VERSION.yml
CHANGED
data/lib/rantly/generator.rb
CHANGED
@@ -101,7 +101,11 @@ class Rantly
|
|
101
101
|
end
|
102
102
|
|
103
103
|
def guard(test)
|
104
|
-
|
104
|
+
unless test
|
105
|
+
raise GuardFailure.new
|
106
|
+
else
|
107
|
+
true
|
108
|
+
end
|
105
109
|
end
|
106
110
|
|
107
111
|
def size
|
@@ -205,9 +209,16 @@ class Rantly
|
|
205
209
|
end
|
206
210
|
|
207
211
|
def array(n=self.size,&block)
|
208
|
-
|
209
|
-
|
210
|
-
|
212
|
+
n.times.map { self.instance_eval(&block) }
|
213
|
+
end
|
214
|
+
|
215
|
+
def dict(n=self.size,&block)
|
216
|
+
h = {}
|
217
|
+
each(n) do
|
218
|
+
k,v = instance_eval(&block)
|
219
|
+
h[k] = v if guard(!h.has_key?(k))
|
220
|
+
end
|
221
|
+
h
|
211
222
|
end
|
212
223
|
|
213
224
|
module Chars
|
@@ -219,7 +230,7 @@ class Rantly
|
|
219
230
|
end
|
220
231
|
|
221
232
|
def of(regexp)
|
222
|
-
ASCII.scan(regexp).to_a.map! { |char| char[0] }
|
233
|
+
ASCII.scan(regexp).to_a.map! { |char| char[0].ord }
|
223
234
|
end
|
224
235
|
end
|
225
236
|
|
data/lib/rantly/property.rb
CHANGED
data/rantly.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
|
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Howard Yeh"]
|
12
|
-
s.date = %q{2010-01-
|
12
|
+
s.date = %q{2010-01-22}
|
13
13
|
s.email = %q{hayeah@gmail.com}
|
14
14
|
s.extra_rdoc_files = [
|
15
15
|
"LICENSE",
|
metadata
CHANGED
@@ -1,30 +1,48 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: rantly
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.1
|
5
|
+
prerelease:
|
5
6
|
platform: ruby
|
6
|
-
authors:
|
7
|
+
authors:
|
7
8
|
- Howard Yeh
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
date: 2011-12-15 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: jeweler
|
16
|
+
requirement: &2156578960 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2156578960
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: shoulda
|
27
|
+
requirement: &2156578420 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *2156578420
|
16
36
|
description:
|
17
37
|
email: hayeah@gmail.com
|
18
38
|
executables: []
|
19
|
-
|
20
39
|
extensions: []
|
21
|
-
|
22
|
-
extra_rdoc_files:
|
40
|
+
extra_rdoc_files:
|
23
41
|
- LICENSE
|
24
42
|
- README.textile
|
25
|
-
files:
|
43
|
+
files:
|
26
44
|
- .document
|
27
|
-
-
|
45
|
+
- Gemfile
|
28
46
|
- LICENSE
|
29
47
|
- README.textile
|
30
48
|
- Rakefile
|
@@ -38,34 +56,28 @@ files:
|
|
38
56
|
- rantly.gemspec
|
39
57
|
- test/rantly_test.rb
|
40
58
|
- test/test_helper.rb
|
41
|
-
has_rdoc: true
|
42
59
|
homepage: http://github.com/hayeah/rantly
|
43
60
|
licenses: []
|
44
|
-
|
45
61
|
post_install_message:
|
46
|
-
rdoc_options:
|
47
|
-
|
48
|
-
require_paths:
|
62
|
+
rdoc_options: []
|
63
|
+
require_paths:
|
49
64
|
- lib
|
50
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ! '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
62
77
|
requirements: []
|
63
|
-
|
64
78
|
rubyforge_project:
|
65
|
-
rubygems_version: 1.
|
79
|
+
rubygems_version: 1.8.11
|
66
80
|
signing_key:
|
67
81
|
specification_version: 3
|
68
82
|
summary: Ruby Imperative Random Data Generator and Quickcheck
|
69
|
-
test_files:
|
70
|
-
- test/rantly_test.rb
|
71
|
-
- test/test_helper.rb
|
83
|
+
test_files: []
|