junklet 1.0.3 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/Gemfile.lock +1 -1
- data/README.md +214 -95
- data/lib/junklet.rb +6 -124
- data/lib/junklet/junk.rb +102 -0
- data/lib/junklet/junklet.rb +21 -0
- data/lib/junklet/version.rb +1 -1
- data/spec/lib/junklet_spec.rb +16 -8
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MzEzOTFiOWJiOTlhZjEyMGY0MjEzYTk4MzU5ZWVhOWEzZTlhNjg4NA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YjM1OTJmZGJhNjhlZmRjM2I3OTU1MzVjOTY3Mjg4OTMxNjFiM2E5OQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZDBjMGMxYWE3MjI4YzljZWQ2NzQ0ZmNjMWE5ODFjZmQxZDQzOWMzNWI4NWRj
|
10
|
+
NDI5MzA1YzE2MDYzMzI5MGM0YzdmMjZjODlmODY2YzQ5YTE2OWNlNjZmNzMy
|
11
|
+
NTdiZDlmNjU0NDJiNmY5ZmMzMmU0OTZjMGNjMjFkNzEwNzIyZjA=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NjYwMjVhOGU4OTUxZjYyMTRjMmI2ZWU0M2ZiZGQ0OTU3YTAyM2M0ZDNlYmQx
|
14
|
+
MmI2NjdhZDA5YzVkMTY1M2EzMWExODdjMDk0MGZlY2VlZDM0OTRlMGJkNjIy
|
15
|
+
MTIyMjUzZWVjNGJlZWY1ZmEyNmIzY2U1MjJlZjBiMWQ5NmUwNDg=
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -18,158 +18,236 @@ So,
|
|
18
18
|
* If equality fails we want to be led to the offending field by the
|
19
19
|
error message and not just the line number in the stack trace.
|
20
20
|
|
21
|
-
|
21
|
+
## Installation
|
22
|
+
|
23
|
+
Add this line to your application's Gemfile:
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
same thing, only n bytes long (can be longer than 32)
|
25
|
+
```ruby
|
26
|
+
gem 'junklet'
|
27
|
+
```
|
27
28
|
|
28
|
-
|
29
|
+
And then execute:
|
29
30
|
|
30
|
-
|
31
|
-
* `let(:host_name) { "host-name-#{SecureRandom.uuid}" }` -> `junklet :host_name, separator: '-'` (Remember that underscores aren't legal in host names)
|
32
|
-
* `let(:bad_number) { SecureRandom.hex[0..7] }` -> `let(:bad_number) { junk 8 }`
|
33
|
-
* `let(:website) { "www.#{SecureRandom.hex}.com" }` -> `let(:website) { "www.#{junk}.com }`
|
31
|
+
$ bundle
|
34
32
|
|
33
|
+
Or install it yourself as:
|
34
|
+
|
35
|
+
$ gem install junklet
|
35
36
|
|
36
37
|
# Usage
|
37
38
|
|
39
|
+
junklet adds two keywords to RSpec's DSL: `junklet`, which defines a
|
40
|
+
`let` statement containing junk data, and `junk`, which is a method
|
41
|
+
that returns many and varied types of junk data. The former is meant
|
42
|
+
to be used as part of the DSL to declare pieces of data to be junk,
|
43
|
+
while the latter is intended to be used anywhere inside RSpec to
|
44
|
+
supply the junk data itself.
|
45
|
+
|
46
|
+
To illustrate, these statements are functionally identically:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
junklet :pigtruck
|
50
|
+
let(:pigtruck) { junk }
|
51
|
+
```
|
52
|
+
|
53
|
+
## Junklet
|
54
|
+
|
55
|
+
`junklet` declares a memoized variable containing random junk.
|
56
|
+
|
38
57
|
junklet :var [, :var_2 [...]] [, options_hash]
|
39
58
|
|
59
|
+
So, for example,
|
60
|
+
|
40
61
|
junklet :first_name
|
41
62
|
|
42
63
|
Creates a `let :first_name` with the value of
|
43
64
|
`first_name-774030d0f58d4f588c5edddbdc7f9580` (the hex number is a
|
44
|
-
uuid without hyphens and will change with each test
|
45
|
-
each test run)
|
65
|
+
uuid without hyphens and will change with each test _case_, not just
|
66
|
+
each test run).
|
46
67
|
|
47
|
-
|
68
|
+
Currently the options hash only gives you control over the variable
|
69
|
+
names appear in the junk data, not the junk data itself. For example,
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
junklet :host_name, separator: '-'
|
73
|
+
```
|
48
74
|
|
49
75
|
Creates a `let :host_name`, but changes underscores to hyphens in the
|
50
76
|
string value,
|
51
77
|
e.g. `host-name-774030d0f58d4f588c5edddbdc7f9580`. Useful
|
52
78
|
specifically for host names, which cannot have underscores in them.
|
53
79
|
|
54
|
-
|
80
|
+
```ruby
|
81
|
+
junklet :a_a, :b_b, :c_c, separator: '.'
|
82
|
+
```
|
55
83
|
|
56
84
|
Does what it says on the tin: creates 3 items with string values of
|
57
|
-
`a.a
|
85
|
+
`a.a.774...`, `b.b.1234...`, and `c.c.234abc...` respectively. I don't
|
86
|
+
know why you'd need this, but hey, if you do there it is.
|
58
87
|
|
88
|
+
## Junk
|
59
89
|
|
60
|
-
|
90
|
+
`junk` returns random junk, which can be finely tuned and fiddled with.
|
61
91
|
|
62
|
-
|
63
|
-
|
92
|
+
```ruby
|
93
|
+
junk
|
94
|
+
junk (integer|symbol|enumerable|proc) [options]
|
95
|
+
```
|
64
96
|
|
65
|
-
|
97
|
+
By default, just calling `junk` from inside a spec or let block
|
98
|
+
returns a random hex string 32 bytes long.
|
66
99
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
100
|
+
### integer
|
101
|
+
|
102
|
+
Give junk an integer argument and it will return that many hexadecimal
|
103
|
+
digits of junk data. Note that this is HALF the number of digits
|
104
|
+
returned if you were to call `SecureRandom.hex(n)`, because `hex(n)`
|
105
|
+
returns n _bytes_, each of which requires two hex digits to
|
106
|
+
represent. Since we're more concerned about specific byte lengths,
|
107
|
+
`junk(n)` gives you n digits, not n*2 digits representing n bytes.
|
74
108
|
|
75
|
-
Currently we do this with `SecureRandom.uuid`, so we'll see code like
|
76
|
-
this frequently in RSpec:
|
77
109
|
|
78
110
|
```ruby
|
79
|
-
|
80
|
-
let(:last_name) { SecureRandom.uuid }
|
81
|
-
let(:address) { SecureRandom.uuid }
|
82
|
-
let(:city) { SecureRandom.uuid }
|
83
|
-
let(:state) { SecureRandom.uuid }
|
84
|
-
let(:phone) { SecureRandom.uuid }
|
111
|
+
junk 17 - return 17 bytes of junk data
|
85
112
|
```
|
86
113
|
|
87
|
-
|
88
|
-
e.g. with `expect(user.first_name).to eq(first_name)` but this idiom
|
89
|
-
expresses that we only care about the equality, not the actual
|
90
|
-
contents.
|
114
|
+
### symbol
|
91
115
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
out the offending field by name. In theory we could just write
|
97
|
-
`let(:first_name) { 'first_name-' + SecureRandom.uuid }` but in
|
98
|
-
practice that creates duplication in the code and muddies the original
|
99
|
-
idiom of "uncared-about" data.
|
116
|
+
junk may be given a symbol denoting the type of data to be
|
117
|
+
returned. Currently `:int` and `:bool` are the only supported
|
118
|
+
types. `junk :bool` merely returns true or false; `junk :int` is
|
119
|
+
much more complicated and interesting.
|
100
120
|
|
101
|
-
|
121
|
+
```ruby
|
122
|
+
junk :bool # Boring. Well, 50% chance of boring.
|
123
|
+
```
|
124
|
+
|
125
|
+
|
126
|
+
`junk :int` is the most complicated and/or interesting type of
|
127
|
+
junk. It returns a random decimal number, and it has the most options
|
128
|
+
such as `size` (number of digits), and `min` and `max` (which sort of
|
129
|
+
do what you'd expect).
|
130
|
+
|
131
|
+
By default, `junk :int` returns a random number from 0 to the largest
|
132
|
+
possible `Fixnum`, which is 2**62-1.
|
102
133
|
|
103
134
|
```ruby
|
104
|
-
|
105
|
-
|
106
|
-
junklet :address
|
107
|
-
junklet :city
|
108
|
-
junklet :state
|
109
|
-
junklet :phone
|
135
|
+
junk :int # return a random integer from 0 to 2**62-1 (maximum size of
|
136
|
+
a Fixnum)
|
110
137
|
```
|
111
138
|
|
112
|
-
|
139
|
+
`size`, `min` and `max` control the size and bounds of the random
|
140
|
+
number.
|
113
141
|
|
114
142
|
```ruby
|
115
|
-
|
143
|
+
junk :int, size: 3 # returns a 3-digit decimal from 100 to 999.
|
144
|
+
junk :int, max: 10 # returns a random number from 0 to 10.
|
145
|
+
junk :int, min: 100 # returns a random number from 100 to 2**62-1.
|
116
146
|
```
|
117
147
|
|
118
|
-
|
119
|
-
|
120
|
-
|
148
|
+
Note: You can mix size with min or max, but they can only be used to
|
149
|
+
further restrict the range, not expand it, because that would change
|
150
|
+
the size constraint. So these examples work the way you'd expect:
|
121
151
|
|
152
|
+
```ruby
|
153
|
+
junk :int, size: 4, min: 2000 # random number 2000-9999
|
154
|
+
junk :int, size: 4, max: 2000 # random number 1000-2000
|
155
|
+
```
|
122
156
|
|
157
|
+
But in these examples, `min` and `max` have no effect:
|
123
158
|
|
124
|
-
|
159
|
+
```ruby
|
160
|
+
junk :int, size: 2, min: 0 # nope, still gonna get 10-99
|
161
|
+
junk :int, size: 2, max: 200 # nope, still gonna get 10-99
|
162
|
+
```
|
125
163
|
|
126
|
-
|
127
|
-
|
164
|
+
Technically, you can use BOTH `min` and `max` with `size` to constrain
|
165
|
+
both bounds of the number, but this effectively makes the `size`
|
166
|
+
option redundant. It will work correctly, but if you remove the size
|
167
|
+
constraint you'll still get the same exact range:
|
128
168
|
|
169
|
+
```ruby
|
170
|
+
# Don't do this
|
171
|
+
junk :int, size: 3, min: 125, max: 440 # 125-440. size is now redundant.
|
172
|
+
```
|
129
173
|
|
130
|
-
|
174
|
+
### Array / Enumerable
|
175
|
+
|
176
|
+
If you give junk an `Array`, `Range`, or any other object that
|
177
|
+
implements `Enumerable`, it will select an element at random from the
|
178
|
+
collection.
|
131
179
|
|
132
180
|
```ruby
|
133
|
-
junk
|
134
|
-
|
135
|
-
junk(
|
136
|
-
|
181
|
+
junk [1,3,5,7,9] # pick a 1-digit odd number
|
182
|
+
junk (1..5).map {|x| x*2-1 } # pick a 1-digit odd number while trying way too hard
|
183
|
+
junk (1..9).step(2) # pick a 1-digit odd number, but now I'm just showing off
|
184
|
+
```
|
137
185
|
|
138
|
-
|
186
|
+
*IMPORTANT CAVEAT*: the Enumerable forms all use `.to_a.sample` to get
|
187
|
+
the random value, and `.to_a` will cheerfully exhaust all the memory
|
188
|
+
Ruby has if you use it on a sufficiently large array.
|
139
189
|
|
140
|
-
|
190
|
+
*LESS-IMPORTANT CAVEAT*: Technically anything that can be converted to
|
191
|
+
an array and then sampled can go through here, so `words.split` would
|
192
|
+
do what you want, but remember that hashes get turned into an array
|
193
|
+
of _pairs_, so expect this weirdness if you ask for it:
|
141
194
|
|
142
|
-
|
143
|
-
junk(:
|
144
|
-
|
195
|
+
```ruby
|
196
|
+
junk({a: 42, b: 13}) # either [:a, 42] or [:b, 13]
|
197
|
+
```
|
145
198
|
|
146
|
-
|
199
|
+
### Proc
|
147
200
|
|
148
|
-
|
149
|
-
junk
|
201
|
+
When all else fails, it's time to haul out the lambdas, amirite? The
|
202
|
+
first argument ot `junk` can be a proc that yields the desired random
|
203
|
+
value. Let's get those odd numbers again:
|
150
204
|
|
151
|
-
|
152
|
-
|
153
|
-
|
205
|
+
```ruby
|
206
|
+
junk ->{ rand(5)*2 + 1 } # 1-digit odd number
|
207
|
+
```
|
154
208
|
|
155
|
-
|
209
|
+
### Other Options
|
156
210
|
|
157
|
-
|
158
|
-
# a value, an array, or a proc. These all return even numbers:
|
211
|
+
### Exclude
|
159
212
|
|
160
|
-
|
161
|
-
|
162
|
-
|
213
|
+
Besides the options that `:int` will take, all of the types will
|
214
|
+
accept an `exclude` argument. This is useful if you want to generate
|
215
|
+
more than one piece of junk data and ensure that they are
|
216
|
+
different. The exclude option can be given a single element, an array,
|
217
|
+
or an enumerable, and if all that fails you can give it a proc that
|
218
|
+
accepts the generated value and returns true if the value should be
|
219
|
+
excluded. Let's use all these excludes to generate odd numbers again:
|
163
220
|
|
164
|
-
|
165
|
-
|
221
|
+
```ruby
|
222
|
+
junk :int, min: 1, max: 3, exclude: 2 # stupid, but it works
|
223
|
+
junk (1..9), exclude: [2,4,6,8]
|
224
|
+
junk (1..9), exclude: (2..8) # okay, only returns 1 or 9 but hey,
|
225
|
+
# they're both odd
|
226
|
+
junk :int, exclude: ->(x) { x % 2 == 0 } # reject even numbers
|
227
|
+
```
|
228
|
+
But again, the real advantage is being able to avoid collisions:
|
166
229
|
|
167
|
-
|
168
|
-
let(:
|
169
|
-
let(:
|
230
|
+
```ruby
|
231
|
+
let(:id1) { junk (0..9) }
|
232
|
+
let(:id2) { junk (0..9), exclude: id1 }
|
233
|
+
let(:id3) { junk (0..9), exclude: [id1, id2] }
|
170
234
|
|
235
|
+
let(:coinflip) { junk :bool }
|
236
|
+
let(:otherside) { junk :bool, exclude: coinflip } # Look I never said
|
237
|
+
# all of these were great ideas, I'm just saying you can DO them.
|
171
238
|
```
|
172
239
|
|
240
|
+
*VERY IMPORTANT CAVEAT* If you exclude all of the possibilities from
|
241
|
+
the random key space, junk will cheerfully go into an infinite
|
242
|
+
loop.
|
243
|
+
|
244
|
+
### Format
|
245
|
+
|
246
|
+
Coming soon! I currently have need of the ability to generate decimal
|
247
|
+
numbers but then left-pad them with zeros and represent them as
|
248
|
+
strings. Can do it with lambdas, but some standard formatting would be
|
249
|
+
really nice.
|
250
|
+
|
173
251
|
# TODO
|
174
252
|
|
175
253
|
* Formats - The original motivation for Junklet is to encapsulate the
|
@@ -204,21 +282,62 @@ let(:two_or_four) { junk :int, min: 2, max: 4, exclude: ->(x){ !evens.include?
|
|
204
282
|
RSpec 2. Remember kids, "Enterprise" means "most of our money comes
|
205
283
|
from the legacy platform".
|
206
284
|
|
207
|
-
|
285
|
+
# Background
|
208
286
|
|
209
|
-
|
287
|
+
At CoverMyMeds we have a legacy impingement that prevents us sometimes
|
288
|
+
from clearing out data from previous test runs. As a result we often
|
289
|
+
have required fields in tests that must be unique but are tested
|
290
|
+
elsewhere, so we don't really care about them in the current test
|
291
|
+
run. For the current test we just want to stub out that field with
|
292
|
+
something unique but we also want to communicate to the developer that
|
293
|
+
the contents of the field are not what we currently care about.
|
294
|
+
|
295
|
+
Currently we do this with `SecureRandom.uuid`, so we'll see code like
|
296
|
+
this frequently in RSpec:
|
210
297
|
|
211
298
|
```ruby
|
212
|
-
|
299
|
+
let(:first_name) { SecureRandom.uuid }
|
300
|
+
let(:last_name) { SecureRandom.uuid }
|
301
|
+
let(:address) { SecureRandom.uuid }
|
302
|
+
let(:city) { SecureRandom.uuid }
|
303
|
+
let(:state) { SecureRandom.uuid }
|
304
|
+
let(:phone) { SecureRandom.uuid }
|
213
305
|
```
|
214
306
|
|
215
|
-
|
307
|
+
...etc. Later in the spec we'll often test against those stubs,
|
308
|
+
e.g. with `expect(user.first_name).to eq(first_name)` but this idiom
|
309
|
+
expresses that we only care about the equality, not the actual
|
310
|
+
contents.
|
216
311
|
|
217
|
-
|
312
|
+
Junklet seeks to improve the readability and conciseness of this
|
313
|
+
intention. One thing that bugs me about the above approach is that if
|
314
|
+
a weird regression bug appears and an unimportant field is the source
|
315
|
+
of a crash. So with Junklet I also wanted to add the ability to call
|
316
|
+
out the offending field by name. In theory we could just write
|
317
|
+
`let(:first_name) { 'first_name-' + SecureRandom.uuid }` but in
|
318
|
+
practice that creates duplication in the code and muddies the original
|
319
|
+
idiom of "uncared-about" data.
|
218
320
|
|
219
|
-
|
321
|
+
Enter Junklet:
|
220
322
|
|
221
|
-
|
323
|
+
```ruby
|
324
|
+
junklet :first_name
|
325
|
+
junklet :last_name
|
326
|
+
junklet :address
|
327
|
+
junklet :city
|
328
|
+
junklet :state
|
329
|
+
junklet :phone
|
330
|
+
```
|
331
|
+
|
332
|
+
Or, if you don't want the junklets to sprawl vertically,
|
333
|
+
|
334
|
+
```ruby
|
335
|
+
junklet :first_name, :last_name, :address, :city, :state, :phone
|
336
|
+
```
|
337
|
+
|
338
|
+
This will have the same effect as calling `let` on the named fields
|
339
|
+
and setting the fieldname and a 32-byte hex string (a uuid with
|
340
|
+
hyphens removed) to be the memoized value.
|
222
341
|
|
223
342
|
## Contributing
|
224
343
|
|
data/lib/junklet.rb
CHANGED
@@ -1,127 +1,9 @@
|
|
1
1
|
require "junklet/version"
|
2
|
+
require "junklet/junk"
|
3
|
+
require "junklet/junklet"
|
2
4
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
def junklet(*args)
|
8
|
-
# TODO: figure out how to use this to wrap junk in junklet,
|
9
|
-
# so that junklet can basically have all the same options as
|
10
|
-
# junk does. E.g. junklet :ddid, :pcn, :group, type: :int,
|
11
|
-
# min: 100000, max: 999999, etc, and you'd get back 3
|
12
|
-
# junklets of 6-digit numbers.
|
13
|
-
opts = args.size > 1 && !args.last.is_a?(Symbol) && args.pop || {}
|
14
|
-
|
15
|
-
names = args.map(&:to_s)
|
16
|
-
|
17
|
-
if opts.key?(:separator)
|
18
|
-
names = names.map {|name| name.gsub(/_/, opts[:separator])}
|
19
|
-
end
|
20
|
-
|
21
|
-
args.zip(names).each do |arg, name|
|
22
|
-
let(arg) { "#{name}-#{junk}" }
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def junk(*args)
|
28
|
-
# TODO: It's long past time to extract this....
|
29
|
-
|
30
|
-
# args.first can be
|
31
|
-
# - an integer indicating the size of the hex string to return
|
32
|
-
# - a symbol denoting the base type, e.g. :int
|
33
|
-
# - an array to sample from
|
34
|
-
# - a range or Enumerable to sample from. WARNING: will call
|
35
|
-
# .to_a on it first, which might be expensive
|
36
|
-
# - a generator Proc which, when called, will generate the
|
37
|
-
# value to use.
|
38
|
-
#
|
39
|
-
# args.rest is a hash of options:
|
40
|
-
# - sequence: Proc or Array of values to choose from.
|
41
|
-
# - exclude: value, array of values, or proc to exclude. If a
|
42
|
-
# Proc is given, it takes the value generated and returns
|
43
|
-
# true if the value should be excluded.
|
44
|
-
#
|
45
|
-
# - for int:
|
46
|
-
# - min: minimum number to return. Default: 0
|
47
|
-
# - max: upper limit of range
|
48
|
-
# - exclude: number, array of numbers, or proc to
|
49
|
-
# exclude. If Proc is provided, tests the number against
|
50
|
-
# the Proc an excludes it if the Proc returns true. This
|
51
|
-
# is implemented except for the proc.
|
52
|
-
|
53
|
-
# FIXME: Raise Argument error unless *args.size is 0-2
|
54
|
-
# FIXME: If arg 1 is a hash, it's the options hash, raise
|
55
|
-
# ArgumentError unless args.size == 1
|
56
|
-
# FIXME: If arg 2 present, Raise Argument error unless it's a
|
57
|
-
# hash.
|
58
|
-
# FIXME: Figure out what our valid options are and parse them;
|
59
|
-
# raise errors if present.
|
60
|
-
|
61
|
-
classes = [Symbol, Array, Enumerable, Proc]
|
62
|
-
if args.size > 0 && classes.any? {|klass| args.first.is_a?(klass) }
|
63
|
-
type = args.shift
|
64
|
-
opts = args.last || {}
|
65
|
-
excluder = if opts[:exclude]
|
66
|
-
if opts[:exclude].is_a?(Proc)
|
67
|
-
opts[:exclude]
|
68
|
-
else
|
69
|
-
->(x) { Array(opts[:exclude]).include?(x) }
|
70
|
-
end
|
71
|
-
else
|
72
|
-
->(x) { false }
|
73
|
-
end
|
74
|
-
|
75
|
-
# TODO: Refactor me. Seriously, this is a functional
|
76
|
-
# programming version of the strategy pattern. Wouldn't it
|
77
|
-
# be neat if we had some kind of object-oriented language
|
78
|
-
# available here?
|
79
|
-
case type
|
80
|
-
when :int
|
81
|
-
# min,max cooperate with size to further constrain it. So
|
82
|
-
# size: 2, min: 30 would be min 30, max 99.
|
83
|
-
if opts[:size]
|
84
|
-
sized_min = 10**(opts[:size]-1)
|
85
|
-
sized_max = 10**opts[:size]-1
|
86
|
-
end
|
87
|
-
explicit_min = opts[:min] || 0
|
88
|
-
explicit_max = (opts[:max] || 2**62-2) + 1
|
89
|
-
|
90
|
-
if sized_min
|
91
|
-
min = [sized_min, explicit_min].max
|
92
|
-
max = [sized_max, explicit_max].min
|
93
|
-
else
|
94
|
-
min = sized_min || explicit_min
|
95
|
-
max = sized_max || explicit_max
|
96
|
-
end
|
97
|
-
|
98
|
-
min,max = max,min if min>max
|
99
|
-
|
100
|
-
generator = -> { rand(max-min) + min }
|
101
|
-
when :bool
|
102
|
-
generator = -> { [true, false].sample }
|
103
|
-
when Array, Enumerable
|
104
|
-
generator = -> { type.to_a.sample }
|
105
|
-
when Proc
|
106
|
-
generator = type
|
107
|
-
else
|
108
|
-
raise "Unrecognized junk type: '#{type}'"
|
109
|
-
end
|
110
|
-
|
111
|
-
begin
|
112
|
-
val = generator.call
|
113
|
-
end while excluder.call(val)
|
114
|
-
val
|
115
|
-
else
|
116
|
-
size = args.first.is_a?(Numeric) ? args.first : 32
|
117
|
-
# hex returns size*2 digits, because it returns a 0..255 byte
|
118
|
-
# as a hex pair. But when we want junt, we want *bytes* of
|
119
|
-
# junk. Get (size+1)/2 chars, which will be correct for even
|
120
|
-
# sizes and 1 char too many for odds, so trim off with
|
121
|
-
# [0...size] (note three .'s to trim off final char)
|
122
|
-
SecureRandom.hex((size+1)/2)[0...size]
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
5
|
+
RSpec.configure do |config|
|
6
|
+
config.extend(Junklet::Junklet)
|
7
|
+
config.extend(Junklet::Junk) # when metaprogramming cases, you may need junk in ExampleGroups
|
8
|
+
config.include(Junklet::Junk)
|
127
9
|
end
|
data/lib/junklet/junk.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
module Junklet
|
2
|
+
module Junk
|
3
|
+
def junk(*args)
|
4
|
+
# TODO: It's long past time to extract this....
|
5
|
+
|
6
|
+
# args.first can be
|
7
|
+
# - an integer indicating the size of the hex string to return
|
8
|
+
# - a symbol denoting the base type, e.g. :int
|
9
|
+
# - an array to sample from
|
10
|
+
# - a range or Enumerable to sample from. WARNING: will call
|
11
|
+
# .to_a on it first, which might be expensive
|
12
|
+
# - a generator Proc which, when called, will generate the
|
13
|
+
# value to use.
|
14
|
+
#
|
15
|
+
# args.rest is a hash of options:
|
16
|
+
# - sequence: Proc or Array of values to choose from.
|
17
|
+
# - exclude: value, array of values, or proc to exclude. If a
|
18
|
+
# Proc is given, it takes the value generated and returns
|
19
|
+
# true if the value should be excluded.
|
20
|
+
#
|
21
|
+
# - for int:
|
22
|
+
# - min: minimum number to return. Default: 0
|
23
|
+
# - max: upper limit of range
|
24
|
+
# - exclude: number, array of numbers, or proc to
|
25
|
+
# exclude. If Proc is provided, tests the number against
|
26
|
+
# the Proc an excludes it if the Proc returns true. This
|
27
|
+
# is implemented except for the proc.
|
28
|
+
|
29
|
+
# FIXME: Raise Argument error unless *args.size is 0-2
|
30
|
+
# FIXME: If arg 1 is a hash, it's the options hash, raise
|
31
|
+
# ArgumentError unless args.size == 1
|
32
|
+
# FIXME: If arg 2 present, Raise Argument error unless it's a
|
33
|
+
# hash.
|
34
|
+
# FIXME: Figure out what our valid options are and parse them;
|
35
|
+
# raise errors if present.
|
36
|
+
|
37
|
+
classes = [Symbol, Array, Enumerable, Proc]
|
38
|
+
if args.size > 0 && classes.any? {|klass| args.first.is_a?(klass) }
|
39
|
+
type = args.shift
|
40
|
+
opts = args.last || {}
|
41
|
+
excluder = if opts[:exclude]
|
42
|
+
if opts[:exclude].is_a?(Proc)
|
43
|
+
opts[:exclude]
|
44
|
+
else
|
45
|
+
->(x) { Array(opts[:exclude]).include?(x) }
|
46
|
+
end
|
47
|
+
else
|
48
|
+
->(x) { false }
|
49
|
+
end
|
50
|
+
|
51
|
+
# TODO: Refactor me. Seriously, this is a functional
|
52
|
+
# programming version of the strategy pattern. Wouldn't it
|
53
|
+
# be neat if we had some kind of object-oriented language
|
54
|
+
# available here?
|
55
|
+
case type
|
56
|
+
when :int
|
57
|
+
# min,max cooperate with size to further constrain it. So
|
58
|
+
# size: 2, min: 30 would be min 30, max 99.
|
59
|
+
if opts[:size]
|
60
|
+
sized_min = 10**(opts[:size]-1)
|
61
|
+
sized_max = 10**opts[:size]-1
|
62
|
+
end
|
63
|
+
explicit_min = opts[:min] || 0
|
64
|
+
explicit_max = (opts[:max] || 2**62-2) + 1
|
65
|
+
|
66
|
+
if sized_min
|
67
|
+
min = [sized_min, explicit_min].max
|
68
|
+
max = [sized_max, explicit_max].min
|
69
|
+
else
|
70
|
+
min = sized_min || explicit_min
|
71
|
+
max = sized_max || explicit_max
|
72
|
+
end
|
73
|
+
|
74
|
+
min,max = max,min if min>max
|
75
|
+
|
76
|
+
generator = -> { rand(max-min) + min }
|
77
|
+
when :bool
|
78
|
+
generator = -> { [true, false].sample }
|
79
|
+
when Array, Enumerable
|
80
|
+
generator = -> { type.to_a.sample }
|
81
|
+
when Proc
|
82
|
+
generator = type
|
83
|
+
else
|
84
|
+
raise "Unrecognized junk type: '#{type}'"
|
85
|
+
end
|
86
|
+
|
87
|
+
begin
|
88
|
+
val = generator.call
|
89
|
+
end while excluder.call(val)
|
90
|
+
val
|
91
|
+
else
|
92
|
+
size = args.first.is_a?(Numeric) ? args.first : 32
|
93
|
+
# hex returns size*2 digits, because it returns a 0..255 byte
|
94
|
+
# as a hex pair. But when we want junt, we want *bytes* of
|
95
|
+
# junk. Get (size+1)/2 chars, which will be correct for even
|
96
|
+
# sizes and 1 char too many for odds, so trim off with
|
97
|
+
# [0...size] (note three .'s to trim off final char)
|
98
|
+
SecureRandom.hex((size+1)/2)[0...size]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Junklet
|
2
|
+
module Junklet
|
3
|
+
def junklet(*args)
|
4
|
+
# TODO: figure out how to use this to wrap junk in junklet,
|
5
|
+
# so that junklet can basically have all the same options as
|
6
|
+
# junk does. E.g. junklet :ddid, :pcn, :group, type: :int,
|
7
|
+
# min: 100000, max: 999999, etc, and you'd get back 3
|
8
|
+
# junklets of 6-digit numbers.
|
9
|
+
opts = args.size > 1 && !args.last.is_a?(Symbol) && args.pop || {}
|
10
|
+
|
11
|
+
names = args.map(&:to_s)
|
12
|
+
|
13
|
+
separator = opts[:separator] || '_'
|
14
|
+
names = names.map {|name| name.gsub(/_/, separator)}
|
15
|
+
|
16
|
+
args.zip(names).each do |arg, name|
|
17
|
+
let(arg) { "#{name}#{separator}#{junk}" }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/junklet/version.rb
CHANGED
data/spec/lib/junklet_spec.rb
CHANGED
@@ -10,7 +10,7 @@ describe Junklet do
|
|
10
10
|
junklet :trash
|
11
11
|
|
12
12
|
specify { expect(trash).to be }
|
13
|
-
specify { expect(trash).to match /^
|
13
|
+
specify { expect(trash).to match /^trash_/ }
|
14
14
|
specify { expect(trash).to match hex_regex }
|
15
15
|
|
16
16
|
describe "memoization" do
|
@@ -21,23 +21,24 @@ describe Junklet do
|
|
21
21
|
context "with multiple args" do
|
22
22
|
junklet :trash, :toss, :crud, :crap
|
23
23
|
|
24
|
-
specify { expect(trash).to match /^
|
24
|
+
specify { expect(trash).to match /^trash_/ }
|
25
25
|
specify { expect(trash).to match hex_regex }
|
26
|
-
specify { expect(toss).to match /^
|
26
|
+
specify { expect(toss).to match /^toss_/ }
|
27
27
|
specify { expect(toss).to match hex_regex }
|
28
|
-
specify { expect(crud).to match /^
|
28
|
+
specify { expect(crud).to match /^crud_/ }
|
29
29
|
specify { expect(crud).to match hex_regex }
|
30
|
-
specify { expect(crap).to match /^
|
30
|
+
specify { expect(crap).to match /^crap_/ }
|
31
31
|
specify { expect(crap).to match hex_regex }
|
32
32
|
end
|
33
33
|
|
34
34
|
context 'with separator option' do
|
35
|
-
junklet :host_name,
|
35
|
+
junklet :host_name, separator: '-'
|
36
|
+
junklet :last_name, :first_name, separator: '.'
|
36
37
|
specify { expect(host_name).to match /^host-name-/ }
|
37
38
|
specify { expect(host_name).to match hex_regex }
|
38
|
-
specify { expect(last_name).to match /^last
|
39
|
+
specify { expect(last_name).to match /^last\.name\./ }
|
39
40
|
specify { expect(last_name).to match hex_regex }
|
40
|
-
specify { expect(first_name).to match /^first
|
41
|
+
specify { expect(first_name).to match /^first\.name\./ }
|
41
42
|
specify { expect(first_name).to match hex_regex }
|
42
43
|
end
|
43
44
|
end
|
@@ -152,4 +153,11 @@ describe Junklet do
|
|
152
153
|
specify { expect(coin_tails).to eq(tails) }
|
153
154
|
end
|
154
155
|
end
|
156
|
+
|
157
|
+
context "metaprogramming use cases" do
|
158
|
+
metaname = junk
|
159
|
+
describe "works by allowing junk to be set from an ExampleGroup outside of an ExampleCase" do
|
160
|
+
specify { expect(metaname).to be }
|
161
|
+
end
|
162
|
+
end
|
155
163
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: junklet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dave Brady
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-02-
|
11
|
+
date: 2015-02-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -96,6 +96,8 @@ files:
|
|
96
96
|
- README.md
|
97
97
|
- junklet.gemspec
|
98
98
|
- lib/junklet.rb
|
99
|
+
- lib/junklet/junk.rb
|
100
|
+
- lib/junklet/junklet.rb
|
99
101
|
- lib/junklet/version.rb
|
100
102
|
- lib/line.rb
|
101
103
|
- spec/fixtures/block1_after.txt
|