junklet 1.0.3 → 1.0.4
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 +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
|