propr 0.2.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/NOTES.md +62 -0
- data/README.md +553 -0
- data/Rakefile +83 -0
- data/TODO.md +64 -0
- data/lib/propr.rb +123 -0
- data/lib/propr/dsl.rb +6 -0
- data/lib/propr/dsl/check.rb +49 -0
- data/lib/propr/dsl/property.rb +62 -0
- data/lib/propr/property.rb +23 -0
- data/lib/propr/random.rb +143 -0
- data/lib/propr/random/array.rb +19 -0
- data/lib/propr/random/bigdecimal.rb +43 -0
- data/lib/propr/random/boolean.rb +7 -0
- data/lib/propr/random/complex.rb +0 -0
- data/lib/propr/random/date.rb +17 -0
- data/lib/propr/random/float.rb +60 -0
- data/lib/propr/random/hash.rb +55 -0
- data/lib/propr/random/integer.rb +38 -0
- data/lib/propr/random/maybe.rb +0 -0
- data/lib/propr/random/nil.rb +8 -0
- data/lib/propr/random/range.rb +32 -0
- data/lib/propr/random/rational.rb +0 -0
- data/lib/propr/random/set.rb +22 -0
- data/lib/propr/random/string.rb +41 -0
- data/lib/propr/random/symbol.rb +13 -0
- data/lib/propr/random/time.rb +14 -0
- data/lib/propr/rspec.rb +97 -0
- data/lib/propr/runner.rb +53 -0
- data/lib/propr/shrink/array.rb +16 -0
- data/lib/propr/shrink/bigdecimal.rb +17 -0
- data/lib/propr/shrink/boolean.rb +11 -0
- data/lib/propr/shrink/complex.rb +0 -0
- data/lib/propr/shrink/date.rb +12 -0
- data/lib/propr/shrink/float.rb +17 -0
- data/lib/propr/shrink/hash.rb +18 -0
- data/lib/propr/shrink/integer.rb +10 -0
- data/lib/propr/shrink/maybe.rb +11 -0
- data/lib/propr/shrink/nil.rb +5 -0
- data/lib/propr/shrink/object.rb +5 -0
- data/lib/propr/shrink/range.rb +4 -0
- data/lib/propr/shrink/rational.rb +4 -0
- data/lib/propr/shrink/set.rb +18 -0
- data/lib/propr/shrink/string.rb +19 -0
- data/lib/propr/shrink/symbol.rb +5 -0
- data/lib/propr/shrink/time.rb +9 -0
- data/spec/examples/choose/array.example +12 -0
- data/spec/examples/choose/hash.example +12 -0
- data/spec/examples/choose/range.example +13 -0
- data/spec/examples/choose/set.example +12 -0
- data/spec/examples/guard.example +38 -0
- data/spec/examples/random/array.example +38 -0
- data/spec/examples/random/hash.example +18 -0
- data/spec/examples/random/integer.example +23 -0
- data/spec/examples/random/range.example +43 -0
- data/spec/examples/scale.example +17 -0
- data/spec/examples/shrink/array.example +20 -0
- data/spec/examples/shrink/bigdecimal.example +20 -0
- data/spec/examples/shrink/float.example +20 -0
- data/spec/examples/shrink/hash.example +20 -0
- data/spec/examples/shrink/integer.example +21 -0
- data/spec/examples/shrink/maybe.example +24 -0
- data/spec/examples/shrink/set.example +21 -0
- data/spec/examples/shrink/string.example +17 -0
- data/spec/issues/003.example +9 -0
- data/spec/spec_helper.rb +24 -0
- metadata +143 -0
data/Rakefile
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require "pathname"
|
2
|
+
abspath = Pathname.new(File.dirname(__FILE__)).expand_path
|
3
|
+
relpath = abspath.relative_path_from(Pathname.pwd)
|
4
|
+
|
5
|
+
begin
|
6
|
+
require "rubygems"
|
7
|
+
require "bundler/setup"
|
8
|
+
rescue LoadError
|
9
|
+
warn "couldn't load bundler:"
|
10
|
+
warn " #{$!}"
|
11
|
+
end
|
12
|
+
|
13
|
+
task :console do
|
14
|
+
exec *%w(irb -I lib -r propr)
|
15
|
+
end
|
16
|
+
|
17
|
+
begin
|
18
|
+
require "rspec/core/rake_task"
|
19
|
+
|
20
|
+
RSpec::Core::RakeTask.new do |t|
|
21
|
+
t.verbose = false
|
22
|
+
t.pattern = "#{relpath}/spec/examples/**/*.example"
|
23
|
+
|
24
|
+
t.rspec_opts = %w(--color)
|
25
|
+
t.rspec_opts << "--profile"
|
26
|
+
t.rspec_opts << "-I#{abspath}/spec"
|
27
|
+
end
|
28
|
+
rescue LoadError
|
29
|
+
task :spec do
|
30
|
+
warn "couldn't load rspec"
|
31
|
+
warn " #{$!}"
|
32
|
+
exit 1
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
begin
|
37
|
+
require "rcov"
|
38
|
+
begin
|
39
|
+
require "rspec/core/rake_task"
|
40
|
+
RSpec::Core::RakeTask.new(:rcov) do |t|
|
41
|
+
t.rcov = true
|
42
|
+
t.rcov_opts = "--exclude spec/,gems/,00401"
|
43
|
+
|
44
|
+
t.verbose = false
|
45
|
+
t.pattern = "#{relpath}/spec/examples/**/*.example"
|
46
|
+
|
47
|
+
t.rspec_opts = %w(--color --format p)
|
48
|
+
t.rspec_opts << "-I#{abspath}/spec"
|
49
|
+
end
|
50
|
+
rescue LoadError
|
51
|
+
task :rcov do
|
52
|
+
warn "couldn't load rspec"
|
53
|
+
warn " #{$!}"
|
54
|
+
exit 1
|
55
|
+
end
|
56
|
+
end
|
57
|
+
rescue LoadError
|
58
|
+
task :rcov do
|
59
|
+
warn "couldn't load rcov:"
|
60
|
+
warn " #{$!}"
|
61
|
+
exit 1
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
begin
|
66
|
+
require "yard"
|
67
|
+
|
68
|
+
# Note options are loaded from .yardopts
|
69
|
+
YARD::Rake::YardocTask.new(:yard => :clobber_yard)
|
70
|
+
|
71
|
+
task :clobber_yard do
|
72
|
+
rm_rf "#{relpath}/doc/generated"
|
73
|
+
mkdir_p "#{relpath}/doc/generated/images"
|
74
|
+
end
|
75
|
+
rescue LoadError
|
76
|
+
task :yard do
|
77
|
+
warn "couldn't load yard:"
|
78
|
+
warn " #{$!}"
|
79
|
+
exit 1
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
task :default => :spec
|
data/TODO.md
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
Priorities
|
2
|
+
|
3
|
+
1. Write unit tests
|
4
|
+
2. Write property tests
|
5
|
+
3. Write example properties and subclasses (markup)
|
6
|
+
4. API documentation
|
7
|
+
5. User documentation
|
8
|
+
6. Optimization (drop Ruby 1.8)
|
9
|
+
7. Publish as rubygem
|
10
|
+
8. Update Stupidedi
|
11
|
+
|
12
|
+
Specifics
|
13
|
+
|
14
|
+
* Organization of common properties
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
module ShrinkSpecs
|
18
|
+
def self
|
19
|
+
# must hold for all implementations
|
20
|
+
property("no value is smaller than itself"){|x| not x.shrink.member?(x) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe String, "#shrink" do
|
25
|
+
# property holds for all implementations
|
26
|
+
ShrinkSpecs.self
|
27
|
+
.check { String.random ... }
|
28
|
+
|
29
|
+
# property of the String#shrink implementation
|
30
|
+
property("empty"){|s| s.shrink.member?("") }
|
31
|
+
.check { String.random ... }
|
32
|
+
|
33
|
+
# property of the String#shrink implementation
|
34
|
+
property("shorter"){|s| s.shrink.all?{|x| x.length < s.length }}
|
35
|
+
.check { String.random ... }
|
36
|
+
end
|
37
|
+
|
38
|
+
describe Integer, "#shrink" do
|
39
|
+
# property holds for all implementations
|
40
|
+
ShrinkSpecs.self
|
41
|
+
.check { Integer.random ... }
|
42
|
+
|
43
|
+
# property of the Integer#shrink implementation
|
44
|
+
property("smaller"){|n| n.shrink.all?{|m| m.abs < n.abs }}
|
45
|
+
.check { Integer.random ... }
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
* Stateful random generation
|
50
|
+
* <del>Re-consider sized values (magntitude, center) or range?</del>
|
51
|
+
* <del>Re-implement sized values</del>
|
52
|
+
* Steal `collect` and `classify` from QuickCheck
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
property("foo") { ... }
|
56
|
+
.check{|rand| rand.integer.tap{|n| classify(n < 0, "negative") }
|
57
|
+
.tap{|n| classify(n > 0, "positive") }}
|
58
|
+
|
59
|
+
property("bar") { ... }
|
60
|
+
check{|rand| rand.array.tap{|xs| collect xs.length }}
|
61
|
+
```
|
62
|
+
|
63
|
+
* <del>Shrink input with breadth first</del>
|
64
|
+
* See also: smallcheck, deepcheck
|
data/lib/propr.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
module Propr
|
2
|
+
autoload :Property, "propr/property"
|
3
|
+
autoload :Dsl, "propr/dsl"
|
4
|
+
autoload :Runner, "propr/runner"
|
5
|
+
autoload :RSpec, "propr/rspec"
|
6
|
+
autoload :RSpecAdapter, "propr/rspec"
|
7
|
+
|
8
|
+
require "fr"
|
9
|
+
require "propr/random"
|
10
|
+
|
11
|
+
def self.wrap(object)
|
12
|
+
if object.nil?
|
13
|
+
[]
|
14
|
+
elsif object.respond_to?(:to_ary)
|
15
|
+
object.to_ary || [object]
|
16
|
+
else
|
17
|
+
[object]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class GuardFailure < StandardError
|
22
|
+
end
|
23
|
+
|
24
|
+
class Falsifiable < StandardError
|
25
|
+
attr_reader :counterex, :shrunken, :passed, :skipped
|
26
|
+
|
27
|
+
def initialize(counterex, shrunken, passed, skipped)
|
28
|
+
@counterex, @shrunken, @passed, @skipped =
|
29
|
+
counterex, shrunken, passed, skipped
|
30
|
+
end
|
31
|
+
|
32
|
+
def counterex
|
33
|
+
Propr.wrap(@counterex)
|
34
|
+
end
|
35
|
+
|
36
|
+
def shrunken
|
37
|
+
Propr.wrap(@shrunken)
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
if @shrunken.nil?
|
42
|
+
["input: #{counterex.map(&:inspect).join(", ")}",
|
43
|
+
"after: #{@passed} passed, #{@skipped} skipped"].join("\n")
|
44
|
+
else
|
45
|
+
["input: #{counterex.map(&:inspect).join(", ")}",
|
46
|
+
"shrunken: #{shrunken.map(&:inspect).join(", ")}",
|
47
|
+
"after: #{@passed} passed, #{@skipped} skipped"].join("\n")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class Failure < StandardError
|
53
|
+
attr_reader :counterex, :shrunken, :passed, :skipped
|
54
|
+
|
55
|
+
def initialize(exception, counterex, shrunken, passed, skipped)
|
56
|
+
@exception, @counterex, @shrunken, @passed, @skipped =
|
57
|
+
exception, counterex, shrunken, passed, skipped
|
58
|
+
end
|
59
|
+
|
60
|
+
def counterex
|
61
|
+
Propr.wrap(@counterex)
|
62
|
+
end
|
63
|
+
|
64
|
+
def shrunken
|
65
|
+
Propr.wrap(@shrunken)
|
66
|
+
end
|
67
|
+
|
68
|
+
def class
|
69
|
+
@exception.class
|
70
|
+
end
|
71
|
+
|
72
|
+
def backtrace
|
73
|
+
@exception.backtrace
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_s
|
77
|
+
if @shrunken.nil?
|
78
|
+
[@exception.message,
|
79
|
+
"input: #{counterex.map(&:inspect).join(", ")}",
|
80
|
+
"after: #{@passed} passed, #{@skipped} skipped"].join("\n")
|
81
|
+
else
|
82
|
+
[@exception.message,
|
83
|
+
"input: #{counterex.map(&:inspect).join(", ")}",
|
84
|
+
"shrunken: #{shrunken.map(&:inspect).join(", ")}",
|
85
|
+
"after: #{@passed} passed, #{@skipped} skipped"].join("\n")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
alias_method :message, :to_s
|
90
|
+
end
|
91
|
+
|
92
|
+
class NoMoreTries < StandardError
|
93
|
+
# @return [Integer]
|
94
|
+
attr_reader :tries
|
95
|
+
|
96
|
+
def initialize(tries)
|
97
|
+
@tries = tries
|
98
|
+
end
|
99
|
+
|
100
|
+
# @return [String]
|
101
|
+
def to_s
|
102
|
+
"Exceeded #{@tries} failed guards"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.RSpec(checkdsl, propdsl)
|
107
|
+
Module.new.tap do |m|
|
108
|
+
m.send(:define_method, :property) { raise }
|
109
|
+
m.send(:define_singleton_method, :rand) { rand }
|
110
|
+
m.send(:define_singleton_method, :included) do |scope|
|
111
|
+
|
112
|
+
# @todo: raise an error if body isn't given
|
113
|
+
scope.send(:define_singleton_method, :property) do |name, options = {}, &body|
|
114
|
+
q = Dsl::Property.wrap(body)
|
115
|
+
p = Property.new(name, q)
|
116
|
+
RSpecAdapter.new(self, options, p)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
RSpec = RSpec(nil, nil)
|
123
|
+
end
|
data/lib/propr/dsl.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
module Propr
|
2
|
+
module Dsl
|
3
|
+
|
4
|
+
class Check
|
5
|
+
|
6
|
+
# Generates a monadic action, to be run with Random.eval
|
7
|
+
def self.wrap(block, m = Propr::Random)
|
8
|
+
new(block, m).instance_exec(&block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(block, m)
|
12
|
+
@context, @m =
|
13
|
+
Kernel.eval("self", block.binding), m
|
14
|
+
end
|
15
|
+
|
16
|
+
def bind(f, &g)
|
17
|
+
@m.bind(f, &g)
|
18
|
+
end
|
19
|
+
|
20
|
+
def unit(value)
|
21
|
+
@m.unit(value)
|
22
|
+
end
|
23
|
+
|
24
|
+
def guard(*conditions)
|
25
|
+
@m.guard(*conditions)
|
26
|
+
end
|
27
|
+
|
28
|
+
def join(value)
|
29
|
+
@m.join(value)
|
30
|
+
end
|
31
|
+
|
32
|
+
def sequence(actions)
|
33
|
+
@m.sequence(actions)
|
34
|
+
end
|
35
|
+
|
36
|
+
def scale(*args)
|
37
|
+
@m.scale(*args)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def method_missing(name, *args, &block)
|
43
|
+
@context.__send__(name, *args, &block)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Propr
|
2
|
+
module Dsl
|
3
|
+
|
4
|
+
class Property
|
5
|
+
|
6
|
+
# Properties shouldn't be monadic, as all random data is generated
|
7
|
+
# elsewhere and passed as arguments to the property. However, this
|
8
|
+
# provides a workaround: m.eval, m.unit, m.bind, etc where `m` is
|
9
|
+
# given as an argument to `wrap`.
|
10
|
+
attr_reader :m
|
11
|
+
|
12
|
+
# Generates a new function, which should return a Boolean
|
13
|
+
def self.wrap(block, m = Propr::Random)
|
14
|
+
case block.arity
|
15
|
+
when 0; lambda{|| new(block, m).instance_exec(&block) }
|
16
|
+
when 1; lambda{|a| new(block, m).instance_exec(a,&block) }
|
17
|
+
when 2; lambda{|a,b| new(block, m).instance_exec(a,b,&block) }
|
18
|
+
when 3; lambda{|a,b,c| new(block, m).instance_exec(a,b,c,&block) }
|
19
|
+
when 4; lambda{|a,b,c,d| new(block, m).instance_exec(a,b,c,d &block) }
|
20
|
+
when 5; lambda{|a,b,c,d,e| new(block, m).instance_exec(a,b,c,d,e,&block) }
|
21
|
+
when 6; lambda{|a,b,c,d,e,f| new(block, m).instance_exec(a,b,c,d,e,f,&block) }
|
22
|
+
when 7; lambda{|a,b,c,d,e,f,g| new(block, m).instance_exec(a,b,c,d,e,f,g,&block) }
|
23
|
+
when 8; lambda{|a,b,c,d,e,f,g,h| new(block, m).instance_exec(a,b,c,d,e,f,g,h,&block) }
|
24
|
+
else lambda{|*args| new(block, m).instance_exec(*args,&block) }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(block, m)
|
29
|
+
@context, @m =
|
30
|
+
Kernel.eval("self", block.binding), m
|
31
|
+
end
|
32
|
+
|
33
|
+
def error?(type = Exception)
|
34
|
+
begin
|
35
|
+
yield
|
36
|
+
false
|
37
|
+
rescue => e
|
38
|
+
e.is_a?(type)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def guard(*conditions)
|
43
|
+
if index = conditions.index{|x| not x }
|
44
|
+
raise GuardFailure,
|
45
|
+
"guard condition #{index} was false"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def label(value)
|
50
|
+
# @todo
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def method_missing(name, *args, &block)
|
56
|
+
@context.__send__(name, *args, &block)
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Propr
|
2
|
+
class Property
|
3
|
+
# @return [Proc]
|
4
|
+
def self.new(name, body)
|
5
|
+
body.instance_variable_set(:@name, name)
|
6
|
+
|
7
|
+
# @return [String]
|
8
|
+
body.define_singleton_method(:name) { @name }
|
9
|
+
|
10
|
+
# @return [Boolean]
|
11
|
+
body.define_singleton_method(:check) do |*args, &block|
|
12
|
+
if block.nil?
|
13
|
+
true == call(*args)
|
14
|
+
else
|
15
|
+
count = args.first || 100
|
16
|
+
count.times.all? { true == call(*block.call) }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
body
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/propr/random.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
module Propr
|
2
|
+
|
3
|
+
class Random
|
4
|
+
end
|
5
|
+
|
6
|
+
class << Random
|
7
|
+
include Fr::Monad
|
8
|
+
|
9
|
+
# Evaluators
|
10
|
+
#############################################
|
11
|
+
|
12
|
+
def run(computation, scale = BigDecimal(1))
|
13
|
+
computation.call(bound scale)
|
14
|
+
end
|
15
|
+
|
16
|
+
def eval(computation, scale = BigDecimal(1), retries = 0)
|
17
|
+
skipped = 0
|
18
|
+
scale = bound(scale)
|
19
|
+
|
20
|
+
while true
|
21
|
+
value, _, success = computation.call(scale)
|
22
|
+
|
23
|
+
if success
|
24
|
+
return value
|
25
|
+
elsif (skipped += 1) > retries
|
26
|
+
raise NoMoreTries, retries
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Combinators
|
32
|
+
#############################################
|
33
|
+
|
34
|
+
def unit(value)
|
35
|
+
lambda do |scale|
|
36
|
+
[value, scale, true]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def bind(f, &g)
|
41
|
+
lambda do |scale|
|
42
|
+
value, scale, success = f.call(scale)
|
43
|
+
|
44
|
+
success ?
|
45
|
+
g.call(value).call(scale) :
|
46
|
+
[value, scale, success]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Actions
|
51
|
+
#############################################
|
52
|
+
|
53
|
+
# TODO: Make Random an instance of Functor and use Functor.guard?
|
54
|
+
def guard(*conditions)
|
55
|
+
lambda do |scale|
|
56
|
+
[nil, scale, conditions.all?]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# When given two arguments, scales a numeric value around a
|
61
|
+
# given origin `zero`, using the current scale factor (0..1).
|
62
|
+
#
|
63
|
+
def scale(number, range, zero)
|
64
|
+
if range.zero?
|
65
|
+
# No scaling
|
66
|
+
lambda do |scale|
|
67
|
+
[number, scale, true]
|
68
|
+
end
|
69
|
+
else
|
70
|
+
# Shrink range exponentially, and -1 + scale reduces the
|
71
|
+
# rng_ to 0 when scale = 0, but rng_ = range when scale = 1.
|
72
|
+
lambda do |scale|
|
73
|
+
rng_ = (range ** scale) - 1 + scale
|
74
|
+
pct = (number - zero) / range
|
75
|
+
[zero + rng_ * pct, scale, true]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Generate psuedo-random number normally distributed between
|
81
|
+
# 0 <= x < 1. This distribution is not weighted using `scale`.
|
82
|
+
#
|
83
|
+
def rand(limit = nil)
|
84
|
+
if not limit.nil? and limit <= 0
|
85
|
+
raise InvalidArgument, "limit <= 0"
|
86
|
+
end
|
87
|
+
|
88
|
+
lambda do |scale|
|
89
|
+
[Kernel.rand(limit), scale, true]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def bound(scale)
|
96
|
+
if scale > 1
|
97
|
+
scale.coerce(1)[0]
|
98
|
+
elsif scale < 0
|
99
|
+
scale.coerce(0)[0]
|
100
|
+
else
|
101
|
+
scale
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Instances of Monad Random
|
109
|
+
require "propr/random/array"
|
110
|
+
require "propr/random/bigdecimal"
|
111
|
+
require "propr/random/boolean"
|
112
|
+
require "propr/random/complex"
|
113
|
+
require "propr/random/date"
|
114
|
+
require "propr/random/float"
|
115
|
+
require "propr/random/hash"
|
116
|
+
require "propr/random/integer"
|
117
|
+
require "propr/random/maybe"
|
118
|
+
require "propr/random/nil"
|
119
|
+
require "propr/random/range"
|
120
|
+
require "propr/random/rational"
|
121
|
+
require "propr/random/set"
|
122
|
+
require "propr/random/string"
|
123
|
+
require "propr/random/symbol"
|
124
|
+
require "propr/random/time"
|
125
|
+
|
126
|
+
# Instances of Shrink "typeclass"
|
127
|
+
require "propr/shrink/object"
|
128
|
+
require "propr/shrink/array"
|
129
|
+
require "propr/shrink/bigdecimal"
|
130
|
+
require "propr/shrink/boolean"
|
131
|
+
require "propr/shrink/complex"
|
132
|
+
require "propr/shrink/date"
|
133
|
+
require "propr/shrink/float"
|
134
|
+
require "propr/shrink/hash"
|
135
|
+
require "propr/shrink/integer"
|
136
|
+
require "propr/shrink/maybe"
|
137
|
+
require "propr/shrink/nil"
|
138
|
+
require "propr/shrink/range"
|
139
|
+
require "propr/shrink/rational"
|
140
|
+
require "propr/shrink/set"
|
141
|
+
require "propr/shrink/string"
|
142
|
+
require "propr/shrink/symbol"
|
143
|
+
require "propr/shrink/time"
|