rubyqc 0.0.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.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.gitmodules +3 -0
- data/.travis.yml +9 -0
- data/Gemfile +11 -0
- data/LICENSE +201 -0
- data/README.md +226 -0
- data/Rakefile +13 -0
- data/lib/rubyqc.rb +16 -0
- data/lib/rubyqc/modifier.rb +21 -0
- data/lib/rubyqc/prelude.rb +263 -0
- data/lib/rubyqc/test.rb +67 -0
- data/lib/rubyqc/version.rb +4 -0
- data/rubyqc.gemspec +41 -0
- data/task/README.md +54 -0
- data/task/gemgem.rb +262 -0
- data/test/test_api.rb +12 -0
- data/test/test_kind_of.rb +22 -0
- data/test/test_readme.rb +12 -0
- metadata +72 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 9ff877141825f4a8516b956d216de5dcae3a9ae9
|
|
4
|
+
data.tar.gz: 0284ea73671c159c38a5a8c6249e477159666c1f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a0e49da081b1af4fc05e43be982395ba6fe0521013589c0e244b148873ab2ba7eb7ca3fecf9515ff6454f75c27b5ef9c14998df181c40659830bf8f0b2979273
|
|
7
|
+
data.tar.gz: c4427a5669c4c7d9c7085e562c6d4f1d1aa4ef833513ac893f9fbd9d3efdb7beee07bc5da82de126252c39bdd75b593ce2875c30d8dcad1ab9f2ad181cd37290
|
data/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/pkg/
|
data/.gitmodules
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
data/README.md
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# RubyQC [](http://travis-ci.org/godfat/rubyqc)
|
|
2
|
+
|
|
3
|
+
by Lin Jen-Shin ([godfat](http://godfat.org))
|
|
4
|
+
|
|
5
|
+
## LINKS:
|
|
6
|
+
|
|
7
|
+
* [github](https://github.com/godfat/rubyqc)
|
|
8
|
+
* [rubygems](https://rubygems.org/gems/rubyqc)
|
|
9
|
+
* [rdoc](http://rdoc.info/github/godfat/rubyqc)
|
|
10
|
+
|
|
11
|
+
## DESCRIPTION:
|
|
12
|
+
|
|
13
|
+
RubyQC -- A conceptual [QuickCheck][] library for Ruby.
|
|
14
|
+
|
|
15
|
+
It's not a faithful port since Hsakell is totally different than Ruby.
|
|
16
|
+
However it's still benefit to use some of the ideas behind QuickCheck,
|
|
17
|
+
and we could also use RubyQC for generating arbitrary objects.
|
|
18
|
+
|
|
19
|
+
[QuickCheck]: http://en.wikipedia.org/wiki/QuickCheck
|
|
20
|
+
|
|
21
|
+
## WHY?
|
|
22
|
+
|
|
23
|
+
How do we make sure our programs work as expected?
|
|
24
|
+
Taking it to the extreme, of course we prove it formally.
|
|
25
|
+
We have [Agda][] for Haskell people, or [Coq][] for OCaml people.
|
|
26
|
+
|
|
27
|
+
However, in most cases we don't really care if they are
|
|
28
|
+
100% correct. Do we care PRNGs are really random
|
|
29
|
+
in games? Some people might care, we don't. Can we
|
|
30
|
+
prove halting problem? Of course not, but we do need
|
|
31
|
+
termination check at times.
|
|
32
|
+
|
|
33
|
+
For most cases, an army of tests is far good enough.
|
|
34
|
+
Usually we write scenario based tests. We first assume
|
|
35
|
+
things, and then do things, finally verify results. This is
|
|
36
|
+
quite simple, but cannot really cover most of the inputs
|
|
37
|
+
without great effort.
|
|
38
|
+
|
|
39
|
+
QuickCheck took another approach. Instead of writing
|
|
40
|
+
scenario, we think about what properties do our programs,
|
|
41
|
+
or functions have, giving a range of inputs. Suppose
|
|
42
|
+
we want to test the reverse function, instead of testing
|
|
43
|
+
against a fixed set of lists and verify a fixed set of results,
|
|
44
|
+
we think about what properties does reverse have.
|
|
45
|
+
|
|
46
|
+
For example, if we reverse and reverse a list, the result
|
|
47
|
+
should be equal to the original list. With QuickCheck, it
|
|
48
|
+
could then generate arbitrary random lists to the property
|
|
49
|
+
function you just wrote, and verify if the property holds.
|
|
50
|
+
By default, it would generate 100 test cases.
|
|
51
|
+
|
|
52
|
+
This approach would force you think more about the
|
|
53
|
+
precondition and postcondition, eliminating unusual
|
|
54
|
+
corner cases you might never think of, and force you
|
|
55
|
+
think what are the functions we're really writing. We
|
|
56
|
+
could also raise the number of test cases by configuring
|
|
57
|
+
it and raise our level of confidence about correctness.
|
|
58
|
+
|
|
59
|
+
[Agda]: http://en.wikipedia.org/wiki/Agda_%28programming_language%29
|
|
60
|
+
[Coq]: http://en.wikipedia.org/wiki/Coq
|
|
61
|
+
|
|
62
|
+
## DESIGN:
|
|
63
|
+
|
|
64
|
+
* Testing framework agnostic
|
|
65
|
+
* Therefore RubyQC could be treated as an arbitrary object generator library
|
|
66
|
+
* Think about [combinator][]
|
|
67
|
+
* Self hosted (Test RubyQC with RubyQC!)
|
|
68
|
+
|
|
69
|
+
[combinator]: http://en.wikipedia.org/wiki/Combinator_library
|
|
70
|
+
|
|
71
|
+
## REQUIREMENTS:
|
|
72
|
+
|
|
73
|
+
* Tested with MRI (official CRuby) 2.0.0, 2.1.0, Rubinius and JRuby.
|
|
74
|
+
|
|
75
|
+
## INSTALLATION:
|
|
76
|
+
|
|
77
|
+
gem install rubyqc
|
|
78
|
+
|
|
79
|
+
## SYNOPSIS:
|
|
80
|
+
|
|
81
|
+
Here's a quick example using [Bacon][]. We check if `Array#sort` has the
|
|
82
|
+
property that the front elements of the result array would be `<=` than
|
|
83
|
+
the rear elements of the result array for all arrays.
|
|
84
|
+
|
|
85
|
+
``` ruby
|
|
86
|
+
require 'bacon'
|
|
87
|
+
require 'rubyqc'
|
|
88
|
+
|
|
89
|
+
Bacon.summary_on_exit
|
|
90
|
+
include RubyQC::API
|
|
91
|
+
|
|
92
|
+
describe Array do
|
|
93
|
+
describe 'sort' do
|
|
94
|
+
should 'Any front elements should be <= any rear elements' do
|
|
95
|
+
check([Fixnum]*100).times(10) do |array|
|
|
96
|
+
array.sort.each_cons(2).each{ |x, y| x.should <= y }
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
[Bacon]: https://github.com/chneukirchen/bacon
|
|
104
|
+
|
|
105
|
+
Basically, `RubyQC::API.check` would merely take the arguments and
|
|
106
|
+
generate the instances via `rubyqc` method. Here the generated array
|
|
107
|
+
could be viewed as `([Fixnum]*100).rubyqc`, meaning that we want an
|
|
108
|
+
array which contains 100 random instances of Fixnum.
|
|
109
|
+
|
|
110
|
+
As you can see, here actually `rubyqc` is an instance method of Array,
|
|
111
|
+
and it would recursively call `rubyqc` for all elements of the array,
|
|
112
|
+
and collect the results. Here's the definition of `Array#rubyqc`:
|
|
113
|
+
|
|
114
|
+
``` ruby
|
|
115
|
+
class Array
|
|
116
|
+
def rubyqc
|
|
117
|
+
map(&:rubyqc)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
And `Fixnum.rubyqc` is a Fixnum's singleton method which is defined as
|
|
123
|
+
follows:
|
|
124
|
+
|
|
125
|
+
``` ruby
|
|
126
|
+
class Fixnum
|
|
127
|
+
def self.rubyqc
|
|
128
|
+
rand(RubyQC::FixnumMin..RubyQC::FixnumMax)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
You get the idea. See next section for the list of built-in generators.
|
|
134
|
+
|
|
135
|
+
### Kernel
|
|
136
|
+
|
|
137
|
+
The very default generator would simply return the instance itself.
|
|
138
|
+
So if there's no generator defined for a given class or instance, it
|
|
139
|
+
would merely take `self`.
|
|
140
|
+
|
|
141
|
+
``` ruby
|
|
142
|
+
true.rubyqc # true
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Class
|
|
146
|
+
|
|
147
|
+
This default generator for classes would simply return a new instance via
|
|
148
|
+
`new` method. This could fail if the `initialize` method for the particular
|
|
149
|
+
class does not take zero argument.
|
|
150
|
+
|
|
151
|
+
``` ruby
|
|
152
|
+
Object.rubyqc # kind_of?(Object)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Fixnum, Bignum, and Integer
|
|
156
|
+
|
|
157
|
+
This would give you a random integer. Fixnum and Bignum would guarantee to
|
|
158
|
+
give you the particular class, whereas Integer would give you either a Fixnum
|
|
159
|
+
or Bignum.
|
|
160
|
+
|
|
161
|
+
``` ruby
|
|
162
|
+
Fixnum.rubyqc # kind_of?(Fixnum)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### array
|
|
166
|
+
|
|
167
|
+
We also have instance level generator, which was used in the first example.
|
|
168
|
+
The array instance generator would recursively call `rubyqc` for all elements
|
|
169
|
+
of the array, and collect the results.
|
|
170
|
+
|
|
171
|
+
``` ruby
|
|
172
|
+
[Fixnum, Fixnum].rubyqc # [kind_of?(Fixnum), kind_of?(Fixnum)]
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### hash
|
|
176
|
+
|
|
177
|
+
This also applies to hashes which would do the same thing as arrays for the
|
|
178
|
+
values, keeping the key.
|
|
179
|
+
|
|
180
|
+
``` ruby
|
|
181
|
+
{:fixnum => Fixnum}.rubyqc # {:fixnum => kind_of?(Fixnum)}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### range
|
|
185
|
+
|
|
186
|
+
Fixnum would actually give a very large or very small (negative) number in
|
|
187
|
+
most cases. If you want to have a number with specific range, use a range
|
|
188
|
+
object to specific the range.
|
|
189
|
+
|
|
190
|
+
``` ruby
|
|
191
|
+
(1..6).rubyqc # within?(1..6)
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Granted that this is actually the same as using `rand(1..6)`, but for
|
|
195
|
+
combinators we need to have a unified interface.
|
|
196
|
+
|
|
197
|
+
### Define your own generator
|
|
198
|
+
|
|
199
|
+
Just define `rubyqc` method for your classes or instances. This weird name
|
|
200
|
+
was simply chosen to avoid name conflicting since we don't have [typeclass][]
|
|
201
|
+
in Ruby, and it's quite natural to open and insert new methods into classes
|
|
202
|
+
in Ruby.
|
|
203
|
+
|
|
204
|
+
[typeclass]: http://learnyouahaskell.com/types-and-typeclasses
|
|
205
|
+
|
|
206
|
+
## CONTRIBUTORS:
|
|
207
|
+
|
|
208
|
+
* Lin Jen-Shin (@godfat)
|
|
209
|
+
|
|
210
|
+
## LICENSE:
|
|
211
|
+
|
|
212
|
+
Apache License 2.0
|
|
213
|
+
|
|
214
|
+
Copyright (c) 2014, Lin Jen-Shin (godfat)
|
|
215
|
+
|
|
216
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
217
|
+
you may not use this file except in compliance with the License.
|
|
218
|
+
You may obtain a copy of the License at
|
|
219
|
+
|
|
220
|
+
<http://www.apache.org/licenses/LICENSE-2.0>
|
|
221
|
+
|
|
222
|
+
Unless required by applicable law or agreed to in writing, software
|
|
223
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
224
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
225
|
+
See the License for the specific language governing permissions and
|
|
226
|
+
limitations under the License.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
|
|
2
|
+
begin
|
|
3
|
+
require "#{dir = File.dirname(__FILE__)}/task/gemgem"
|
|
4
|
+
rescue LoadError
|
|
5
|
+
sh 'git submodule update --init'
|
|
6
|
+
exec Gem.ruby, '-S', $PROGRAM_NAME, *ARGV
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
Gemgem.init(dir) do |s|
|
|
10
|
+
require 'rubyqc/version'
|
|
11
|
+
s.name = 'rubyqc'
|
|
12
|
+
s.version = RubyQC::VERSION
|
|
13
|
+
end
|
data/lib/rubyqc.rb
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
module RubyQC
|
|
3
|
+
class Modifier
|
|
4
|
+
def initialize args, &block
|
|
5
|
+
@args = args
|
|
6
|
+
@times = 10
|
|
7
|
+
run(&block)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def times t, &block
|
|
11
|
+
@times = t
|
|
12
|
+
run(&block)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def run
|
|
16
|
+
@times.times{
|
|
17
|
+
yield(*@args.map(&:rubyqc))
|
|
18
|
+
} if block_given?
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
|
|
2
|
+
module RubyQC
|
|
3
|
+
FixnumMax = 2 ** (0.size * 8 - 2) - 1
|
|
4
|
+
FixnumMin = - FixnumMax - 1
|
|
5
|
+
FixnumSuc = FixnumMax + 1
|
|
6
|
+
BignumMax = FixnumMax * 10
|
|
7
|
+
BignumMin = FixnumMin * 10
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
###
|
|
11
|
+
### default implementation
|
|
12
|
+
###
|
|
13
|
+
|
|
14
|
+
module Kernel
|
|
15
|
+
def rubyqc
|
|
16
|
+
self
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class Class
|
|
21
|
+
def rubyqc
|
|
22
|
+
new
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
###
|
|
27
|
+
### class level implementation
|
|
28
|
+
###
|
|
29
|
+
|
|
30
|
+
class NilClass
|
|
31
|
+
def self.rubyqc
|
|
32
|
+
nil
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class TrueClass
|
|
37
|
+
def self.rubyqc
|
|
38
|
+
true
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class FalseClass
|
|
43
|
+
def self.rubyqc
|
|
44
|
+
false
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class Proc
|
|
49
|
+
def self.rubyqc
|
|
50
|
+
lambda{ Class.rubyqc.rubyqc }
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class Method
|
|
55
|
+
def self.rubyqc
|
|
56
|
+
obj = Class.rubyqc.rubyqc
|
|
57
|
+
obj.method(obj.methods.sample)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
class UnboundMethod
|
|
62
|
+
def self.rubyqc
|
|
63
|
+
klass = Class.rubyqc
|
|
64
|
+
klass.instance_method(klass.instance_methods.sample)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# TODO
|
|
69
|
+
class Binding
|
|
70
|
+
def self.rubyqc
|
|
71
|
+
binding
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# TODO
|
|
76
|
+
class Symbol
|
|
77
|
+
def self.rubyqc
|
|
78
|
+
String.rubyqc.to_sym
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
class Encoding
|
|
83
|
+
def self.rubyqc
|
|
84
|
+
list.sample
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# TODO
|
|
89
|
+
class Dir
|
|
90
|
+
def self.rubyqc
|
|
91
|
+
Dir.open(__dir__)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
class File
|
|
96
|
+
def self.rubyqc
|
|
97
|
+
File.open(__FILE__)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
class IO
|
|
102
|
+
def self.rubyqc
|
|
103
|
+
pipe.sample
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# TODO
|
|
108
|
+
class String
|
|
109
|
+
def self.rubyqc
|
|
110
|
+
/.*/.rubyqc
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
class Regexp
|
|
115
|
+
def self.rubyqc
|
|
116
|
+
new(String.rubyqc)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
class MatchData
|
|
121
|
+
def self.rubyqc
|
|
122
|
+
String.rubyqc.match(Regexp.rubyqc)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
class Class
|
|
127
|
+
def self.rubyqc
|
|
128
|
+
Object.constants.map{ |name|
|
|
129
|
+
unless [:Class, :BasicObject, :Data,
|
|
130
|
+
:Config, :RubyVM, :TracePoint].include?(name)
|
|
131
|
+
const_get(name)
|
|
132
|
+
end
|
|
133
|
+
}.select{ |const| const.kind_of?(Class) }.sample
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# TODO: why?
|
|
138
|
+
# class Object
|
|
139
|
+
# def self.rubyqc
|
|
140
|
+
# Class.rubyqc.rubyqc
|
|
141
|
+
# end
|
|
142
|
+
# end
|
|
143
|
+
|
|
144
|
+
class Fixnum
|
|
145
|
+
def self.rubyqc
|
|
146
|
+
(RubyQC::FixnumMin..RubyQC::FixnumMax).rubyqc
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
class Bignum
|
|
151
|
+
def self.rubyqc
|
|
152
|
+
case (0..1).rubyqc
|
|
153
|
+
when 0
|
|
154
|
+
(RubyQC::BignumMin...RubyQC::FixnumMin).rubyqc
|
|
155
|
+
when 1
|
|
156
|
+
(RubyQC::FixnumSuc...RubyQC::BignumMax).rubyqc
|
|
157
|
+
else
|
|
158
|
+
raise "huh?"
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
class Integer
|
|
164
|
+
def self.rubyqc
|
|
165
|
+
[Bignum, Fixnum].sample.rubyqc
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
class Complex
|
|
170
|
+
def self.rubyqc
|
|
171
|
+
Fixnum.rubyqc + Fixnum.rubyqc.i
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
class Rational
|
|
176
|
+
def self.rubyqc
|
|
177
|
+
Rational(Fixnum.rubyqc, Fixnum.rubyqc)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
class Range
|
|
182
|
+
def self.rubyqc
|
|
183
|
+
new(*[Fixnum.rubyqc, Fixnum.rubyqc].sort)
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
class Enumerator
|
|
188
|
+
def self.rubyqc
|
|
189
|
+
Array.rubyqc.to_enum
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# TODO
|
|
194
|
+
class Float
|
|
195
|
+
def self.rubyqc
|
|
196
|
+
rand
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
class Thread::SizedQueue
|
|
201
|
+
def self.rubyqc
|
|
202
|
+
new(rand(0..100))
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
class Fiber
|
|
207
|
+
def self.rubyqc
|
|
208
|
+
new(&Proc.rubyqc)
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
class Thread
|
|
213
|
+
def self.rubyqc
|
|
214
|
+
new(&Proc.rubyqc)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
class SignalException
|
|
219
|
+
def self.rubyqc
|
|
220
|
+
new(rand(0..32))
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
class SystemCallError
|
|
225
|
+
def self.rubyqc
|
|
226
|
+
new(rand(1..106))
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
class Struct
|
|
231
|
+
def self.rubyqc
|
|
232
|
+
new(Symbol.rubyqc)
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
###
|
|
237
|
+
### instance level implementation
|
|
238
|
+
###
|
|
239
|
+
|
|
240
|
+
class Array
|
|
241
|
+
def rubyqc
|
|
242
|
+
map(&:rubyqc)
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
class Hash
|
|
247
|
+
def rubyqc
|
|
248
|
+
inject({}){ |r, (k, v)| r[k] = v.rubyqc; r }
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
class Range
|
|
253
|
+
def rubyqc
|
|
254
|
+
rand(self.begin..self.end)
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# TODO
|
|
259
|
+
class Regexp
|
|
260
|
+
def rubyqc
|
|
261
|
+
'rubyqc...'
|
|
262
|
+
end
|
|
263
|
+
end
|
data/lib/rubyqc/test.rb
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
|
|
2
|
+
require 'bacon'
|
|
3
|
+
require 'rubyqc'
|
|
4
|
+
|
|
5
|
+
Bacon.summary_on_exit
|
|
6
|
+
include RubyQC::API
|
|
7
|
+
|
|
8
|
+
module Kernel
|
|
9
|
+
def eq? rhs
|
|
10
|
+
self == rhs
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class Should
|
|
15
|
+
def self.rubyqc
|
|
16
|
+
new(Class.rubyqc.rubyqc)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def verify_generated generated, spec
|
|
21
|
+
if spec.empty?
|
|
22
|
+
generated.should.eq spec
|
|
23
|
+
else
|
|
24
|
+
case spec
|
|
25
|
+
when Array
|
|
26
|
+
verify_array(generated, spec)
|
|
27
|
+
when Hash
|
|
28
|
+
verify_hash(generated, spec)
|
|
29
|
+
else
|
|
30
|
+
verify_other(generated, spec)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def verify_array generated, spec
|
|
36
|
+
generated.zip(spec).each do |(instance, expected)|
|
|
37
|
+
case expected
|
|
38
|
+
when Array
|
|
39
|
+
verify_array(instance, expected)
|
|
40
|
+
when Hash
|
|
41
|
+
verify_hash(instance, expected)
|
|
42
|
+
else
|
|
43
|
+
verify_other(instance, expected)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def verify_hash generated, spec
|
|
49
|
+
generated.each do |key, instance|
|
|
50
|
+
case expected = spec[key]
|
|
51
|
+
when Array
|
|
52
|
+
verify_array(instance, expected)
|
|
53
|
+
when Hash
|
|
54
|
+
verify_hash(instance, expected)
|
|
55
|
+
else
|
|
56
|
+
verify_other(instance, expected)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def verify_other generated, spec
|
|
62
|
+
if spec.kind_of?(Class)
|
|
63
|
+
generated.should.kind_of spec
|
|
64
|
+
else
|
|
65
|
+
generated.should.eq spec
|
|
66
|
+
end
|
|
67
|
+
end
|
data/rubyqc.gemspec
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
# stub: rubyqc 0.0.1 ruby lib
|
|
3
|
+
|
|
4
|
+
Gem::Specification.new do |s|
|
|
5
|
+
s.name = "rubyqc"
|
|
6
|
+
s.version = "0.0.1"
|
|
7
|
+
|
|
8
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
9
|
+
s.require_paths = ["lib"]
|
|
10
|
+
s.authors = ["Lin Jen-Shin (godfat)"]
|
|
11
|
+
s.date = "2014-01-30"
|
|
12
|
+
s.description = "RubyQC -- A conceptual [QuickCheck][] library for Ruby.\n\nIt's not a faithful port since Hsakell is totally different than Ruby.\nHowever it's still benefit to use some of the ideas behind QuickCheck,\nand we could also use RubyQC for generating arbitrary objects.\n\n[QuickCheck]: http://en.wikipedia.org/wiki/QuickCheck"
|
|
13
|
+
s.email = ["godfat (XD) godfat.org"]
|
|
14
|
+
s.files = [
|
|
15
|
+
".gitignore",
|
|
16
|
+
".gitmodules",
|
|
17
|
+
".travis.yml",
|
|
18
|
+
"Gemfile",
|
|
19
|
+
"LICENSE",
|
|
20
|
+
"README.md",
|
|
21
|
+
"Rakefile",
|
|
22
|
+
"lib/rubyqc.rb",
|
|
23
|
+
"lib/rubyqc/modifier.rb",
|
|
24
|
+
"lib/rubyqc/prelude.rb",
|
|
25
|
+
"lib/rubyqc/test.rb",
|
|
26
|
+
"lib/rubyqc/version.rb",
|
|
27
|
+
"rubyqc.gemspec",
|
|
28
|
+
"task/README.md",
|
|
29
|
+
"task/gemgem.rb",
|
|
30
|
+
"test/test_api.rb",
|
|
31
|
+
"test/test_kind_of.rb",
|
|
32
|
+
"test/test_readme.rb"]
|
|
33
|
+
s.homepage = "https://github.com/godfat/rubyqc"
|
|
34
|
+
s.licenses = ["Apache License 2.0"]
|
|
35
|
+
s.rubygems_version = "2.2.1"
|
|
36
|
+
s.summary = "RubyQC -- A conceptual [QuickCheck][] library for Ruby."
|
|
37
|
+
s.test_files = [
|
|
38
|
+
"test/test_api.rb",
|
|
39
|
+
"test/test_kind_of.rb",
|
|
40
|
+
"test/test_readme.rb"]
|
|
41
|
+
end
|
data/task/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Gemgem
|
|
2
|
+
|
|
3
|
+
## DESCRIPTION:
|
|
4
|
+
|
|
5
|
+
Provided tasks:
|
|
6
|
+
|
|
7
|
+
rake clean # Remove ignored files
|
|
8
|
+
rake gem:build # Build gem
|
|
9
|
+
rake gem:install # Install gem
|
|
10
|
+
rake gem:release # Release gem
|
|
11
|
+
rake gem:spec # Generate gemspec
|
|
12
|
+
rake test # Run tests in memory
|
|
13
|
+
|
|
14
|
+
## REQUIREMENTS:
|
|
15
|
+
|
|
16
|
+
* Tested with MRI (official CRuby) 1.9.3, 2.0.0, Rubinius and JRuby.
|
|
17
|
+
|
|
18
|
+
## INSTALLATION:
|
|
19
|
+
|
|
20
|
+
git submodule add git://github.com/godfat/gemgem.git task
|
|
21
|
+
|
|
22
|
+
And in Rakefile:
|
|
23
|
+
|
|
24
|
+
``` ruby
|
|
25
|
+
begin
|
|
26
|
+
require "#{dir = File.dirname(__FILE__)}/task/gemgem"
|
|
27
|
+
rescue LoadError
|
|
28
|
+
sh 'git submodule update --init'
|
|
29
|
+
exec Gem.ruby, '-S', $PROGRAM_NAME, *ARGV
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
Gemgem.init(dir) do |s|
|
|
33
|
+
s.name = 'your-gem'
|
|
34
|
+
s.version = '0.1.0'
|
|
35
|
+
end
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## LICENSE:
|
|
39
|
+
|
|
40
|
+
Apache License 2.0
|
|
41
|
+
|
|
42
|
+
Copyright (c) 2011-2013, Lin Jen-Shin (godfat)
|
|
43
|
+
|
|
44
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
45
|
+
you may not use this file except in compliance with the License.
|
|
46
|
+
You may obtain a copy of the License at
|
|
47
|
+
|
|
48
|
+
<http://www.apache.org/licenses/LICENSE-2.0>
|
|
49
|
+
|
|
50
|
+
Unless required by applicable law or agreed to in writing, software
|
|
51
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
52
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
53
|
+
See the License for the specific language governing permissions and
|
|
54
|
+
limitations under the License.
|
data/task/gemgem.rb
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
|
|
2
|
+
module Gemgem
|
|
3
|
+
class << self
|
|
4
|
+
attr_accessor :dir, :spec, :spec_create
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
module_function
|
|
8
|
+
def gem_tag ; "#{spec.name}-#{spec.version}" ; end
|
|
9
|
+
def gem_path ; "#{pkg_dir}/#{gem_tag}.gem" ; end
|
|
10
|
+
def spec_path ; "#{dir}/#{spec.name}.gemspec" ; end
|
|
11
|
+
def pkg_dir ; "#{dir}/pkg" ; end
|
|
12
|
+
def escaped_dir; @escaped_dir ||= Regexp.escape(dir); end
|
|
13
|
+
|
|
14
|
+
def init dir, &block
|
|
15
|
+
self.dir = dir
|
|
16
|
+
$LOAD_PATH.unshift("#{dir}/lib")
|
|
17
|
+
ENV['RUBYLIB'] = "#{dir}/lib:#{ENV['RUBYLIB']}"
|
|
18
|
+
ENV['PATH'] = "#{dir}/bin:#{ENV['PATH']}"
|
|
19
|
+
self.spec_create = block
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def create
|
|
23
|
+
spec = Gem::Specification.new do |s|
|
|
24
|
+
s.authors = ['Lin Jen-Shin (godfat)']
|
|
25
|
+
s.email = ['godfat (XD) godfat.org']
|
|
26
|
+
|
|
27
|
+
s.description = description.join
|
|
28
|
+
s.summary = description.first
|
|
29
|
+
s.license = readme['LICENSE'].sub(/.+\n\n/, '').lines.first.strip
|
|
30
|
+
|
|
31
|
+
s.date = Time.now.strftime('%Y-%m-%d')
|
|
32
|
+
s.files = gem_files
|
|
33
|
+
s.test_files = test_files
|
|
34
|
+
s.executables = bin_files
|
|
35
|
+
end
|
|
36
|
+
spec_create.call(spec)
|
|
37
|
+
spec.homepage = "https://github.com/godfat/#{spec.name}"
|
|
38
|
+
self.spec = spec
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def write
|
|
42
|
+
File.open(spec_path, 'w'){ |f| f << split_lines(spec.to_ruby) }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def split_lines ruby
|
|
46
|
+
ruby.gsub(/(.+?)\s*=\s*\[(.+?)\]/){ |s|
|
|
47
|
+
if $2.index(',')
|
|
48
|
+
"#{$1} = [\n #{$2.split(',').map(&:strip).join(",\n ")}]"
|
|
49
|
+
else
|
|
50
|
+
s
|
|
51
|
+
end
|
|
52
|
+
}
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def strip_path path
|
|
56
|
+
strip_home_path(strip_cwd_path(path))
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def strip_home_path path
|
|
60
|
+
path.sub(ENV['HOME'], '~')
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def strip_cwd_path path
|
|
64
|
+
path.sub(Dir.pwd, '.')
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def git *args
|
|
68
|
+
`git --git-dir=#{dir}/.git #{args.join(' ')}`
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def sh_git *args
|
|
72
|
+
Rake.sh('git', "--git-dir=#{dir}/.git", *args)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def sh_gem *args
|
|
76
|
+
Rake.sh(Gem.ruby, '-S', 'gem', *args)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def glob path=dir
|
|
80
|
+
Dir.glob("#{path}/**/*", File::FNM_DOTMATCH)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def readme
|
|
84
|
+
@readme ||=
|
|
85
|
+
if (path = "#{Gemgem.dir}/README.md") && File.exist?(path)
|
|
86
|
+
ps = "##{File.read(path)}".
|
|
87
|
+
scan(/((#+)[^\n]+\n\n.+?(?=(\n\n\2[^#\n]+\n)|\Z))/m).map(&:first)
|
|
88
|
+
ps.inject('HEADER' => ps.first){ |r, s, i|
|
|
89
|
+
r[s[/\w+/]] = s
|
|
90
|
+
r
|
|
91
|
+
}
|
|
92
|
+
else
|
|
93
|
+
{}
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def description
|
|
98
|
+
# JRuby String#lines is returning an enumerator
|
|
99
|
+
@description ||= (readme['DESCRIPTION']||'').sub(/.+\n\n/, '').lines.to_a
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def all_files
|
|
103
|
+
@all_files ||= fold_files(glob).sort
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def fold_files files
|
|
107
|
+
files.inject([]){ |r, path|
|
|
108
|
+
if File.file?(path) && path !~ %r{/\.git(/|$)} &&
|
|
109
|
+
(rpath = path[%r{^#{escaped_dir}/(.*$)}, 1])
|
|
110
|
+
r << rpath
|
|
111
|
+
elsif File.symlink?(path) # walk into symlinks...
|
|
112
|
+
r.concat(fold_files(glob(File.expand_path(path,
|
|
113
|
+
File.readlink(path)))))
|
|
114
|
+
else
|
|
115
|
+
r
|
|
116
|
+
end
|
|
117
|
+
}
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def gem_files
|
|
121
|
+
@gem_files ||= all_files.reject{ |f|
|
|
122
|
+
f =~ ignored_pattern && !git_files.include?(f)
|
|
123
|
+
}
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def test_files
|
|
127
|
+
@test_files ||= gem_files.grep(%r{^test/(.+?/)*test_.+?\.rb$})
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def bin_files
|
|
131
|
+
@bin_files ||= gem_files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def git_files
|
|
135
|
+
@git_files ||= if File.exist?("#{dir}/.git")
|
|
136
|
+
git('ls-files').split("\n")
|
|
137
|
+
else
|
|
138
|
+
[]
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def ignored_files
|
|
143
|
+
@ignored_files ||= all_files.grep(ignored_pattern)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def ignored_pattern
|
|
147
|
+
@ignored_pattern ||= if gitignore.empty?
|
|
148
|
+
/^$/
|
|
149
|
+
else
|
|
150
|
+
Regexp.new(expand_patterns(gitignore).join('|'))
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def expand_patterns pathes
|
|
155
|
+
# http://git-scm.com/docs/gitignore
|
|
156
|
+
pathes.flat_map{ |path|
|
|
157
|
+
# we didn't implement negative pattern for now
|
|
158
|
+
Regexp.escape(path).sub(%r{^/}, '^').gsub(/\\\*/, '[^/]*')
|
|
159
|
+
}
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def gitignore
|
|
163
|
+
@gitignore ||= if File.exist?(path = "#{dir}/.gitignore")
|
|
164
|
+
File.read(path).lines.
|
|
165
|
+
reject{ |l| l == /^\s*(#|\s+$)/ }.map(&:strip)
|
|
166
|
+
else
|
|
167
|
+
[]
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
namespace :gem do
|
|
173
|
+
|
|
174
|
+
desc 'Install gem'
|
|
175
|
+
task :install => [:build] do
|
|
176
|
+
Gemgem.sh_gem('install', Gemgem.gem_path)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
desc 'Build gem'
|
|
180
|
+
task :build => [:spec] do
|
|
181
|
+
require 'fileutils'
|
|
182
|
+
require 'rubygems/package'
|
|
183
|
+
gem = nil
|
|
184
|
+
Dir.chdir(Gemgem.dir) do
|
|
185
|
+
gem = Gem::Package.build(Gem::Specification.load(Gemgem.spec_path))
|
|
186
|
+
FileUtils.mkdir_p(Gemgem.pkg_dir)
|
|
187
|
+
FileUtils.mv(gem, Gemgem.pkg_dir) # gem is relative path, but might be ok
|
|
188
|
+
end
|
|
189
|
+
puts "\e[35mGem built: \e[33m" \
|
|
190
|
+
"#{Gemgem.strip_path("#{Gemgem.pkg_dir}/#{gem}")}\e[0m"
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
desc 'Generate gemspec'
|
|
194
|
+
task :spec do
|
|
195
|
+
Gemgem.create
|
|
196
|
+
Gemgem.write
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
desc 'Release gem'
|
|
200
|
+
task :release => [:spec, :check, :build] do
|
|
201
|
+
Gemgem.module_eval do
|
|
202
|
+
sh_git('tag', Gemgem.gem_tag)
|
|
203
|
+
sh_git('push')
|
|
204
|
+
sh_git('push', '--tags')
|
|
205
|
+
sh_gem('push', Gemgem.gem_path)
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
task :check do
|
|
210
|
+
ver = Gemgem.spec.version.to_s
|
|
211
|
+
|
|
212
|
+
if ENV['VERSION'].nil?
|
|
213
|
+
puts("\e[35mExpected " \
|
|
214
|
+
"\e[33mVERSION\e[35m=\e[33m#{ver}\e[0m")
|
|
215
|
+
exit(1)
|
|
216
|
+
|
|
217
|
+
elsif ENV['VERSION'] != ver
|
|
218
|
+
puts("\e[35mExpected \e[33mVERSION\e[35m=\e[33m#{ver} " \
|
|
219
|
+
"\e[35mbut got\n " \
|
|
220
|
+
"\e[33mVERSION\e[35m=\e[33m#{ENV['VERSION']}\e[0m")
|
|
221
|
+
exit(2)
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
end # of gem namespace
|
|
226
|
+
|
|
227
|
+
desc 'Run tests'
|
|
228
|
+
task :test do
|
|
229
|
+
next if Gemgem.test_files.empty?
|
|
230
|
+
|
|
231
|
+
require 'bacon'
|
|
232
|
+
Bacon.extend(Bacon::TestUnitOutput)
|
|
233
|
+
Bacon.summary_on_exit
|
|
234
|
+
Gemgem.test_files.each{ |file| require "#{Gemgem.dir}/#{file[0..-4]}" }
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
desc 'Trash ignored files'
|
|
238
|
+
task :clean => ['gem:spec'] do
|
|
239
|
+
next if Gemgem.ignored_files.empty?
|
|
240
|
+
|
|
241
|
+
require 'fileutils'
|
|
242
|
+
trash = File.expand_path("~/.Trash/#{Gemgem.spec.name}")
|
|
243
|
+
puts "Move the following files into:" \
|
|
244
|
+
" \e[35m#{Gemgem.strip_path(trash)}\e[33m"
|
|
245
|
+
|
|
246
|
+
Gemgem.ignored_files.each do |file|
|
|
247
|
+
from = "#{Gemgem.dir}/#{file}"
|
|
248
|
+
to = "#{trash}/#{File.dirname(file)}"
|
|
249
|
+
puts Gemgem.strip_path(from)
|
|
250
|
+
|
|
251
|
+
FileUtils.mkdir_p(to)
|
|
252
|
+
FileUtils.mv(from, to)
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
print "\e[0m"
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
task :default do
|
|
259
|
+
# Is there a reliable way to do this in the current process?
|
|
260
|
+
# It failed miserably before between Rake versions...
|
|
261
|
+
exec "#{Gem.ruby} -S #{$PROGRAM_NAME} -f #{Rake.application.rakefile} -T"
|
|
262
|
+
end
|
data/test/test_api.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
require 'rubyqc/test'
|
|
3
|
+
|
|
4
|
+
describe 'kind_of' do
|
|
5
|
+
[Fixnum, Bignum, Array, Integer, Class].each do |klass|
|
|
6
|
+
should klass.name do
|
|
7
|
+
check(klass){ |obj| obj.should.kind_of(klass) }
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
[[], [Fixnum], [Fixnum, Array],
|
|
12
|
+
{}, {:a => 0}, {'b' => 1}, {2 => Integer},
|
|
13
|
+
{nil => [Fixnum]}, {true => {false => [Bignum]}}
|
|
14
|
+
].each do |spec|
|
|
15
|
+
|
|
16
|
+
should spec.inspect do
|
|
17
|
+
check(spec) do |generated|
|
|
18
|
+
verify_generated(generated, spec)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
data/test/test_readme.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rubyqc
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Lin Jen-Shin (godfat)
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2014-01-30 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: |-
|
|
14
|
+
RubyQC -- A conceptual [QuickCheck][] library for Ruby.
|
|
15
|
+
|
|
16
|
+
It's not a faithful port since Hsakell is totally different than Ruby.
|
|
17
|
+
However it's still benefit to use some of the ideas behind QuickCheck,
|
|
18
|
+
and we could also use RubyQC for generating arbitrary objects.
|
|
19
|
+
|
|
20
|
+
[QuickCheck]: http://en.wikipedia.org/wiki/QuickCheck
|
|
21
|
+
email:
|
|
22
|
+
- godfat (XD) godfat.org
|
|
23
|
+
executables: []
|
|
24
|
+
extensions: []
|
|
25
|
+
extra_rdoc_files: []
|
|
26
|
+
files:
|
|
27
|
+
- ".gitignore"
|
|
28
|
+
- ".gitmodules"
|
|
29
|
+
- ".travis.yml"
|
|
30
|
+
- Gemfile
|
|
31
|
+
- LICENSE
|
|
32
|
+
- README.md
|
|
33
|
+
- Rakefile
|
|
34
|
+
- lib/rubyqc.rb
|
|
35
|
+
- lib/rubyqc/modifier.rb
|
|
36
|
+
- lib/rubyqc/prelude.rb
|
|
37
|
+
- lib/rubyqc/test.rb
|
|
38
|
+
- lib/rubyqc/version.rb
|
|
39
|
+
- rubyqc.gemspec
|
|
40
|
+
- task/README.md
|
|
41
|
+
- task/gemgem.rb
|
|
42
|
+
- test/test_api.rb
|
|
43
|
+
- test/test_kind_of.rb
|
|
44
|
+
- test/test_readme.rb
|
|
45
|
+
homepage: https://github.com/godfat/rubyqc
|
|
46
|
+
licenses:
|
|
47
|
+
- Apache License 2.0
|
|
48
|
+
metadata: {}
|
|
49
|
+
post_install_message:
|
|
50
|
+
rdoc_options: []
|
|
51
|
+
require_paths:
|
|
52
|
+
- lib
|
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
54
|
+
requirements:
|
|
55
|
+
- - ">="
|
|
56
|
+
- !ruby/object:Gem::Version
|
|
57
|
+
version: '0'
|
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
59
|
+
requirements:
|
|
60
|
+
- - ">="
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: '0'
|
|
63
|
+
requirements: []
|
|
64
|
+
rubyforge_project:
|
|
65
|
+
rubygems_version: 2.2.1
|
|
66
|
+
signing_key:
|
|
67
|
+
specification_version: 4
|
|
68
|
+
summary: RubyQC -- A conceptual [QuickCheck][] library for Ruby.
|
|
69
|
+
test_files:
|
|
70
|
+
- test/test_api.rb
|
|
71
|
+
- test/test_kind_of.rb
|
|
72
|
+
- test/test_readme.rb
|