rantly 0.3.0 → 0.3.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/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: []
|