rspec-junklet 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/.gitignore +34 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +47 -0
- data/LICENSE +22 -0
- data/README.md +314 -0
- data/lib/junklet/junk.rb +118 -0
- data/lib/junklet/junklet.rb +25 -0
- data/lib/junklet/version.rb +3 -0
- data/lib/junklet.rb +9 -0
- data/lib/line.rb +44 -0
- data/rspec-junklet.gemspec +26 -0
- data/spec/fixtures/block1_after.txt +6 -0
- data/spec/fixtures/block1_before.txt +7 -0
- data/spec/fixtures/combined_after.txt +6 -0
- data/spec/fixtures/combined_before.txt +9 -0
- data/spec/fixtures/embedded_after.txt +8 -0
- data/spec/fixtures/embedded_before.txt +9 -0
- data/spec/fixtures/mixed_code_after.txt +47 -0
- data/spec/fixtures/mixed_code_before.txt +46 -0
- data/spec/fixtures/old_skool_after.txt +6 -0
- data/spec/fixtures/old_skool_before.txt +7 -0
- data/spec/lib/junklet/junk_spec.rb +152 -0
- data/spec/lib/junklet/junklet_spec.rb +41 -0
- data/spec/lib/junklet_spec.rb +173 -0
- data/spec/lib/line_spec.rb +76 -0
- data/spec/spec_helper.rb +45 -0
- metadata +157 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZWQwYjZkZmNlNjlkNmM0MTE2YTMyNzBmYTEwMjU0MThjMjNlYzUzMA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MjQwNmUwNzQxYTljYWQ1NjhiMTgxNzU0NTk0NzNjNTk4MDhkYjdkNg==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZTFlNmUzYTRhNzgzNjQyNjViZjA4YWNmYzI5Mjk4NWQ3N2VmNDhkYjZmNzIx
|
10
|
+
NmM5MWE5MDkzNzk1Y2E2YmM2MTEyM2U2YWI0YTU4NjVkOTA1MTdiYjNhZGFj
|
11
|
+
MThhZjU2MWY2ZDRhYTI2ODQ4MTI2YzJkZmM1NTM0ODJiM2Q1YmQ=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
OWY2NjNiNmY2ZDM1NTVlNWNmODNmZDIxMGNjODI1N2Y2M2MzMmJkOWY4Nzc4
|
14
|
+
NDY1NTFjZmFjZjU1NjA2YmJiZWI3NzUyYzQ0ZjZmZjYxNjIxYmJlMThkM2Y5
|
15
|
+
OTYyMjA0ZjEwMTk5MGU1ZmQ2N2UxYWZjMmVlYjQyZWEyZWM4NDQ=
|
data/.gitignore
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/test/tmp/
|
9
|
+
/test/version_tmp/
|
10
|
+
/tmp/
|
11
|
+
|
12
|
+
## Specific to RubyMotion:
|
13
|
+
.dat*
|
14
|
+
.repl_history
|
15
|
+
build/
|
16
|
+
|
17
|
+
## Documentation cache and generated files:
|
18
|
+
/.yardoc/
|
19
|
+
/_yardoc/
|
20
|
+
/doc/
|
21
|
+
/rdoc/
|
22
|
+
|
23
|
+
## Environment normalisation:
|
24
|
+
/.bundle/
|
25
|
+
/lib/bundler/man/
|
26
|
+
|
27
|
+
# for a library or gem, you might want to ignore these files since the code is
|
28
|
+
# intended to run in multiple environments; otherwise, check them in:
|
29
|
+
# Gemfile.lock
|
30
|
+
# .ruby-version
|
31
|
+
# .ruby-gemset
|
32
|
+
|
33
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
34
|
+
.rvmrc
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-1.9.3-p551
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rspec-junklet (1.0.4)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
builder (3.2.2)
|
10
|
+
coderay (1.1.0)
|
11
|
+
cucumber (1.3.18)
|
12
|
+
builder (>= 2.1.2)
|
13
|
+
diff-lcs (>= 1.1.3)
|
14
|
+
gherkin (~> 2.12)
|
15
|
+
multi_json (>= 1.7.5, < 2.0)
|
16
|
+
multi_test (>= 0.1.1)
|
17
|
+
diff-lcs (1.2.5)
|
18
|
+
gherkin (2.12.2)
|
19
|
+
multi_json (~> 1.3)
|
20
|
+
method_source (0.8.2)
|
21
|
+
multi_json (1.10.1)
|
22
|
+
multi_test (0.1.1)
|
23
|
+
pry (0.10.1)
|
24
|
+
coderay (~> 1.1.0)
|
25
|
+
method_source (~> 0.8.1)
|
26
|
+
slop (~> 3.4)
|
27
|
+
rake (10.4.2)
|
28
|
+
rspec (2.99.0)
|
29
|
+
rspec-core (~> 2.99.0)
|
30
|
+
rspec-expectations (~> 2.99.0)
|
31
|
+
rspec-mocks (~> 2.99.0)
|
32
|
+
rspec-core (2.99.2)
|
33
|
+
rspec-expectations (2.99.2)
|
34
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
35
|
+
rspec-mocks (2.99.2)
|
36
|
+
slop (3.6.0)
|
37
|
+
|
38
|
+
PLATFORMS
|
39
|
+
ruby
|
40
|
+
|
41
|
+
DEPENDENCIES
|
42
|
+
bundler (~> 1.7)
|
43
|
+
cucumber
|
44
|
+
pry
|
45
|
+
rake (~> 10.0)
|
46
|
+
rspec (~> 2.0)
|
47
|
+
rspec-junklet!
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 CoverMyMeds
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,314 @@
|
|
1
|
+
# Junklet
|
2
|
+
|
3
|
+
Cache tiny chunks of unique junk data in RSpec with `junklet :name`; get handy
|
4
|
+
clumps of junk data at any time with `junk`. Size your junk with e.g. `junk 100`
|
5
|
+
or `junk 4`.
|
6
|
+
|
7
|
+
Junklet data is fixture data that:
|
8
|
+
|
9
|
+
* We essentially don't care about,
|
10
|
+
* But we might want to test for equality somewhere later,
|
11
|
+
* And we might need to be unique between runs in case a spec crashes and
|
12
|
+
SQLServer fails to clean up the test database
|
13
|
+
|
14
|
+
So,
|
15
|
+
|
16
|
+
* We want it to be easy to create junk data fields quickly and easily, and
|
17
|
+
* If equality fails we want to be led to the offending field by the error
|
18
|
+
message and not just the line number in the stack trace.
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
Add this line to your application's Gemfile:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
gem 'rspec-junklet'
|
26
|
+
```
|
27
|
+
|
28
|
+
And then execute:
|
29
|
+
|
30
|
+
$ bundle
|
31
|
+
|
32
|
+
Or install it yourself as:
|
33
|
+
|
34
|
+
$ gem install rspec-junklet
|
35
|
+
|
36
|
+
# Usage
|
37
|
+
|
38
|
+
junklet adds two keywords to RSpec's DSL: `junklet`, which defines a `let`
|
39
|
+
statement containing junk data, and `junk`, which is a method that returns many
|
40
|
+
and varied types of junk data. The former is meant to be used as part of the DSL
|
41
|
+
to declare pieces of data to be junk, while the latter is intended to be used
|
42
|
+
anywhere inside RSpec to supply the junk data itself.
|
43
|
+
|
44
|
+
To illustrate, these statements are functionally identical:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
junklet :pigtruck
|
48
|
+
let(:pigtruck) { junk }
|
49
|
+
```
|
50
|
+
|
51
|
+
## Junklet
|
52
|
+
|
53
|
+
`junklet` declares a memoized variable containing random junk.
|
54
|
+
|
55
|
+
junklet :var [, :var_2 [...]] [, options_hash]
|
56
|
+
|
57
|
+
So, for example,
|
58
|
+
|
59
|
+
junklet :first_name
|
60
|
+
|
61
|
+
Creates a `let :first_name` with the value of
|
62
|
+
`first_name-774030d0f58d4f588c5edddbdc7f9580` (the hex number is a uuid without
|
63
|
+
hyphens and will change with each test _case_, not just each test run).
|
64
|
+
|
65
|
+
Currently the options hash only gives you control over the variable names appear
|
66
|
+
in the junk data, not the junk data itself. For example,
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
junklet :host_name, separator: '-'
|
70
|
+
```
|
71
|
+
|
72
|
+
Creates a `let :host_name`, but changes underscores to hyphens in the string
|
73
|
+
value, e.g. `host-name-774030d0f58d4f588c5edddbdc7f9580`. Useful specifically
|
74
|
+
for host names, which cannot have underscores in them.
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
junklet :a_a, :b_b, :c_c, separator: '.'
|
78
|
+
```
|
79
|
+
|
80
|
+
Does what it says on the tin: creates 3 items with string values of
|
81
|
+
`a.a.774...`, `b.b.1234...`, and `c.c.234abc...` respectively. I don't know why
|
82
|
+
you'd need this, but hey, if you do there it is.
|
83
|
+
|
84
|
+
## Junk
|
85
|
+
|
86
|
+
`junk` returns random junk, which can be finely tuned and fiddled with.
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
junk
|
90
|
+
junk (integer|symbol|enumerable|proc) [options]
|
91
|
+
```
|
92
|
+
|
93
|
+
By default, just calling `junk` from inside a spec or let block returns a random
|
94
|
+
hex string 32 bytes long.
|
95
|
+
|
96
|
+
### integer
|
97
|
+
|
98
|
+
Give junk an integer argument and it will return that many hexadecimal digits of
|
99
|
+
junk data. Note that this is HALF the number of digits returned if you were to
|
100
|
+
call `SecureRandom.hex(n)`, because `hex(n)` returns n _bytes_, each of which
|
101
|
+
requires two hex digits to represent. Since we're more concerned about specific
|
102
|
+
byte lengths, `junk(n)` gives you n digits, not n*2 digits representing n bytes.
|
103
|
+
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
junk 17 - return 17 bytes of junk data
|
107
|
+
```
|
108
|
+
|
109
|
+
### symbol
|
110
|
+
|
111
|
+
junk may be given a symbol denoting the type of data to be returned. Currently
|
112
|
+
`:int` and `:bool` are the only supported types. `junk :bool` merely returns
|
113
|
+
true or false; `junk :int` is much more complicated and interesting.
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
junk :bool # Boring. Well, 50% chance of boring.
|
117
|
+
```
|
118
|
+
|
119
|
+
`junk :int` is the most complicated and/or interesting type of junk. It returns
|
120
|
+
a random decimal number, and it has the most options such as `size` (number of
|
121
|
+
digits), and `min` and `max` (which sort of do what you'd expect).
|
122
|
+
|
123
|
+
By default, `junk :int` returns a random number from 0 to the largest possible
|
124
|
+
`Fixnum`, which is 2**62-1.
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
junk :int # return a random integer from 0 to 2**62-1 (maximum size of
|
128
|
+
a Fixnum)
|
129
|
+
```
|
130
|
+
|
131
|
+
`size`, `min` and `max` control the size and bounds of the random number.
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
junk :int, size: 3 # returns a 3-digit decimal from 100 to 999.
|
135
|
+
junk :int, max: 10 # returns a random number from 0 to 10.
|
136
|
+
junk :int, min: 100 # returns a random number from 100 to 2**62-1.
|
137
|
+
```
|
138
|
+
|
139
|
+
Note: You can mix size with min or max, but they can only be used to further
|
140
|
+
restrict the range, not expand it, because that would change the size
|
141
|
+
constraint. So these examples work the way you'd expect:
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
junk :int, size: 4, min: 2000 # random number 2000-9999
|
145
|
+
junk :int, size: 4, max: 2000 # random number 1000-2000
|
146
|
+
```
|
147
|
+
|
148
|
+
But in these examples, `min` and `max` have no effect:
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
junk :int, size: 2, min: 0 # nope, still gonna get 10-99
|
152
|
+
junk :int, size: 2, max: 200 # nope, still gonna get 10-99
|
153
|
+
```
|
154
|
+
|
155
|
+
Technically, you CAN use BOTH `min` and `max` with `size` to constrain both
|
156
|
+
bounds of the number, but this effectively makes the `size` option redundant. It
|
157
|
+
will work correctly, but if you remove the size constraint you'll still get the
|
158
|
+
same exact range:
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
# Don't do this - size argument is redundant
|
162
|
+
junk :int, size: 3, min: 125, max: 440 # 125-440. size is now redundant.
|
163
|
+
```
|
164
|
+
|
165
|
+
### Array / Enumerable
|
166
|
+
|
167
|
+
If you give junk an `Array`, `Range`, or any other object that implements
|
168
|
+
`Enumerable`, it will select an element at random from the collection.
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
junk [1,3,5,7,9] # pick a 1-digit odd number
|
172
|
+
junk (1..5).map {|x| x*2-1 } # pick a 1-digit odd number while trying way too hard
|
173
|
+
junk (1..9).step(2) # pick a 1-digit odd number, but now I'm just showing off
|
174
|
+
```
|
175
|
+
|
176
|
+
*IMPORTANT CAVEAT*: the Enumerable forms all use `.to_a.sample` to get the
|
177
|
+
random value, and `.to_a` will cheerfully exhaust all the memory Ruby has if you
|
178
|
+
use it on a sufficiently large array.
|
179
|
+
|
180
|
+
*LESS-IMPORTANT CAVEAT*: Technically anything that can be converted to an array
|
181
|
+
and then sampled can go through here, so `words.split` would do what you want,
|
182
|
+
but remember that hashes get turned into an array of _pairs_, so expect this
|
183
|
+
weirdness if you ask for it:
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
junk({a: 42, b: 13}) # either [:a, 42] or [:b, 13]
|
187
|
+
```
|
188
|
+
|
189
|
+
### Proc
|
190
|
+
|
191
|
+
When all else fails, it's time to haul out the lambdas, amirite? The first
|
192
|
+
argument ot `junk` can be a proc that yields the desired random value. Let's get
|
193
|
+
those odd numbers again:
|
194
|
+
|
195
|
+
```ruby
|
196
|
+
junk ->{ rand(5)*2 + 1 } # 1-digit odd number
|
197
|
+
```
|
198
|
+
|
199
|
+
### Other Options
|
200
|
+
|
201
|
+
### Exclude
|
202
|
+
|
203
|
+
Besides the options that `:int` will take, all of the types will accept an
|
204
|
+
`exclude` argument. This is useful if you want to generate more than one piece
|
205
|
+
of junk data and ensure that they are different. The exclude option can be given
|
206
|
+
a single element, an array, or an enumerable, and if all that fails you can give
|
207
|
+
it a proc that accepts the generated value and returns true if the value should
|
208
|
+
be excluded. Let's use all these excludes to generate odd numbers again:
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
junk :int, min: 1, max: 3, exclude: 2 # stupid, but it works
|
212
|
+
junk (1..9), exclude: [2,4,6,8]
|
213
|
+
junk (1..9), exclude: (2..8) # okay, only returns 1 or 9 but hey,
|
214
|
+
# they're both odd
|
215
|
+
junk :int, exclude: ->(x) { x % 2 == 0 } # reject even numbers
|
216
|
+
```
|
217
|
+
|
218
|
+
But again, the real advantage is being able to avoid collisions:
|
219
|
+
|
220
|
+
```ruby
|
221
|
+
let(:id1) { junk (0..9) }
|
222
|
+
let(:id2) { junk (0..9), exclude: id1 }
|
223
|
+
let(:id3) { junk (0..9), exclude: [id1, id2] }
|
224
|
+
|
225
|
+
let(:coinflip) { junk :bool }
|
226
|
+
let(:otherside) { junk :bool, exclude: coinflip } # Look I never said
|
227
|
+
# all of these were great ideas, I'm just saying you can DO them.
|
228
|
+
```
|
229
|
+
|
230
|
+
*VERY IMPORTANT CAVEAT* If you exclude all of the possibilities from the random
|
231
|
+
key space, junk will cheerfully go into an infinite loop.
|
232
|
+
|
233
|
+
### Format
|
234
|
+
|
235
|
+
Formats are here, but need documentating! For now: `format: :int` will cast your
|
236
|
+
junk to an integer by calling `.to_i` on it. Other formats include:
|
237
|
+
|
238
|
+
* `format: :int` calls `.to_i` on the junk before returning it
|
239
|
+
* `format: :string` calls `.to_s` on the junk
|
240
|
+
* `format: "%s"` (or any other string) calls sprintf on the junk with the
|
241
|
+
string as the format string
|
242
|
+
* `format: SomeClass` passes the junk to `SomeClass.new`, returning an instance
|
243
|
+
of `SomeClass`. *Note:* If you plan on combining this with `exclude`, make
|
244
|
+
sure your class implements the `==` operator.
|
245
|
+
* `format: ->(x) { ... }` passes the junk to your Proc
|
246
|
+
|
247
|
+
# TODO
|
248
|
+
|
249
|
+
* Allow all args to junk to be passed to junklet. Use explicit `type` option to
|
250
|
+
specify the type. E.g. `junklet :foo, :bar, :baz, type: :int, max: 14,
|
251
|
+
exclude: [:qaz, :qux]`. _(Do we want to allow a flag for mutually exclusive?)_
|
252
|
+
|
253
|
+
# Background
|
254
|
+
|
255
|
+
At CoverMyMeds we have a legacy impingement that prevents us sometimes from
|
256
|
+
clearing out data from previous test runs. As a result we often have required
|
257
|
+
fields in tests that must be unique but are tested elsewhere, so we don't really
|
258
|
+
care about them in the current test run. For the current test we just want to
|
259
|
+
stub out that field with something unique but we also want to communicate to the
|
260
|
+
developer that the contents of the field are not what we currently care about.
|
261
|
+
|
262
|
+
Currently we do this with `SecureRandom.uuid`, so we'll see code like
|
263
|
+
this frequently in RSpec:
|
264
|
+
|
265
|
+
```ruby
|
266
|
+
let(:first_name) { SecureRandom.uuid }
|
267
|
+
let(:last_name) { SecureRandom.uuid }
|
268
|
+
let(:address) { SecureRandom.uuid }
|
269
|
+
let(:city) { SecureRandom.uuid }
|
270
|
+
let(:state) { SecureRandom.uuid }
|
271
|
+
let(:phone) { SecureRandom.uuid }
|
272
|
+
```
|
273
|
+
|
274
|
+
...etc. Later in the spec we'll often test against those stubs, e.g. with
|
275
|
+
`expect(user.first_name).to eq(first_name)` but this idiom expresses that we
|
276
|
+
only care about the equality, not the actual contents.
|
277
|
+
|
278
|
+
Junklet seeks to improve the readability and conciseness of this intention. One
|
279
|
+
thing that bugs me about the above approach is that if a weird regression bug
|
280
|
+
appears and an unimportant field is the source of a crash. So with Junklet I
|
281
|
+
also wanted to add the ability to call out the offending field by name. In
|
282
|
+
theory we could just write `let(:first_name) { 'first_name-' + SecureRandom.uuid
|
283
|
+
}` but in practice that creates duplication in the code and muddies the original
|
284
|
+
idiom of "uncared-about" data.
|
285
|
+
|
286
|
+
Enter Junklet:
|
287
|
+
|
288
|
+
```ruby
|
289
|
+
junklet :first_name
|
290
|
+
junklet :last_name
|
291
|
+
junklet :address
|
292
|
+
junklet :city
|
293
|
+
junklet :state
|
294
|
+
junklet :phone
|
295
|
+
```
|
296
|
+
|
297
|
+
Or, if you don't want the junklets to sprawl vertically,
|
298
|
+
|
299
|
+
```ruby
|
300
|
+
junklet :first_name, :last_name, :address, :city, :state, :phone
|
301
|
+
```
|
302
|
+
|
303
|
+
This will have the same effect as calling `let` on the named fields and setting
|
304
|
+
the fieldname and a 32-byte hex string (a uuid with hyphens removed) to be the
|
305
|
+
memoized value.
|
306
|
+
|
307
|
+
## Contributing
|
308
|
+
|
309
|
+
1. Fork it ( https://github.com/[my-github-username]/junklet/fork )
|
310
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
311
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
312
|
+
4. Write specs to document how your change is to be used
|
313
|
+
5. Push to the branch (`git push origin my-new-feature`)
|
314
|
+
6. Create a new Pull Request
|
data/lib/junklet/junk.rb
ADDED
@@ -0,0 +1,118 @@
|
|
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
|
+
junk_types = [Symbol, Array, Enumerable, Proc]
|
38
|
+
if args.size > 0 && junk_types.any? {|klass| args.first.is_a?(klass) }
|
39
|
+
type = args.shift
|
40
|
+
opts = args.last || {}
|
41
|
+
|
42
|
+
excluder = if opts[:exclude]
|
43
|
+
if opts[:exclude].is_a?(Proc)
|
44
|
+
opts[:exclude]
|
45
|
+
else
|
46
|
+
->(x) { Array(opts[:exclude]).include?(x) }
|
47
|
+
end
|
48
|
+
else
|
49
|
+
->(x) { false }
|
50
|
+
end
|
51
|
+
|
52
|
+
formatter = case opts[:format]
|
53
|
+
when :string
|
54
|
+
->(x) { x.to_s }
|
55
|
+
when :int
|
56
|
+
->(x) { x.to_i }
|
57
|
+
when String
|
58
|
+
->(x) { sprintf(opts[:format], x) }
|
59
|
+
when Class
|
60
|
+
->(x) { opts[:format].new(x) }
|
61
|
+
when Proc
|
62
|
+
opts[:format]
|
63
|
+
else
|
64
|
+
->(x) { x }
|
65
|
+
end
|
66
|
+
|
67
|
+
# TODO: Refactor me. Seriously, this is a functional
|
68
|
+
# programming version of the strategy pattern. Wouldn't it
|
69
|
+
# be neat if we had some kind of object-oriented language
|
70
|
+
# available here?
|
71
|
+
case type
|
72
|
+
when :int
|
73
|
+
# min,max cooperate with size to further constrain it. So
|
74
|
+
# size: 2, min: 30 would be min 30, max 99.
|
75
|
+
if opts[:size]
|
76
|
+
sized_min = 10**(opts[:size]-1)
|
77
|
+
sized_max = 10**opts[:size]-1
|
78
|
+
end
|
79
|
+
explicit_min = opts[:min] || 0
|
80
|
+
explicit_max = (opts[:max] || 2**62-2) + 1
|
81
|
+
|
82
|
+
if sized_min
|
83
|
+
min = [sized_min, explicit_min].max
|
84
|
+
max = [sized_max, explicit_max].min
|
85
|
+
else
|
86
|
+
min = sized_min || explicit_min
|
87
|
+
max = sized_max || explicit_max
|
88
|
+
end
|
89
|
+
|
90
|
+
min,max = max,min if min>max
|
91
|
+
|
92
|
+
generator = -> { rand(max-min) + min }
|
93
|
+
when :bool
|
94
|
+
generator = -> { [true, false].sample }
|
95
|
+
when Array, Enumerable
|
96
|
+
generator = -> { type.to_a.sample }
|
97
|
+
when Proc
|
98
|
+
generator = type
|
99
|
+
else
|
100
|
+
raise "Unrecognized junk type: '#{type}'"
|
101
|
+
end
|
102
|
+
|
103
|
+
begin
|
104
|
+
val = formatter.call(generator.call)
|
105
|
+
end while excluder.call(val)
|
106
|
+
val
|
107
|
+
else
|
108
|
+
size = args.first.is_a?(Numeric) ? args.first : 32
|
109
|
+
# hex returns size*2 digits, because it returns a 0..255 byte
|
110
|
+
# as a hex pair. But when we want junt, we want *bytes* of
|
111
|
+
# junk. Get (size+1)/2 chars, which will be correct for even
|
112
|
+
# sizes and 1 char too many for odds, so trim off with
|
113
|
+
# [0...size] (note three .'s to trim off final char)
|
114
|
+
SecureRandom.hex((size+1)/2)[0...size]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,25 @@
|
|
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
|
+
make_junklet arg, name, separator, junk
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def make_junklet(let_name, name, separator, junk_data)
|
22
|
+
let(let_name) { "#{name}#{separator}#{junk_data}" }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/junklet.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require "junklet/version"
|
2
|
+
require "junklet/junk"
|
3
|
+
require "junklet/junklet"
|
4
|
+
|
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)
|
9
|
+
end
|
data/lib/line.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
class Line < String
|
2
|
+
def initialize(line="")
|
3
|
+
super
|
4
|
+
end
|
5
|
+
|
6
|
+
def indent
|
7
|
+
' ' * (size - lstrip.size)
|
8
|
+
end
|
9
|
+
|
10
|
+
def let?
|
11
|
+
match(/^\s*let\s*\(/) && !junklet?
|
12
|
+
end
|
13
|
+
|
14
|
+
def junklet?
|
15
|
+
already_junklet? || secure_random?
|
16
|
+
end
|
17
|
+
|
18
|
+
def already_junklet?
|
19
|
+
match(/^\s*junklet\b/)
|
20
|
+
end
|
21
|
+
|
22
|
+
def secure_random?
|
23
|
+
match(/^\s*(let)\s*\(?([^)]*)\)\s*{\s*SecureRandom.(uuid|hex)\s*}/)
|
24
|
+
end
|
25
|
+
|
26
|
+
def code?
|
27
|
+
empty? || (!let? && !junklet?)
|
28
|
+
end
|
29
|
+
|
30
|
+
def names
|
31
|
+
return nil unless let? || junklet?
|
32
|
+
match(/^\s*(let|junklet)\s*\(?([^)]*)\)?/) \
|
33
|
+
.captures[1..-1] \
|
34
|
+
.join('') \
|
35
|
+
.split(/,/) \
|
36
|
+
.map(&:strip)
|
37
|
+
end
|
38
|
+
|
39
|
+
def convert
|
40
|
+
return nil unless junklet?
|
41
|
+
return self if already_junklet?
|
42
|
+
Line.new("#{indent}junklet #{names.first}")
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'junklet/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "rspec-junklet"
|
8
|
+
spec.version = Junklet::VERSION
|
9
|
+
spec.authors = ["David Brady"]
|
10
|
+
spec.email = ["dbrady@shinybit.com"]
|
11
|
+
spec.summary = "Easily create junk data for specs"
|
12
|
+
spec.description = "Works like let for rspec, but creates unique random junk data"
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "rspec", "~> 2.0"
|
24
|
+
spec.add_development_dependency "cucumber"
|
25
|
+
spec.add_development_dependency "pry"
|
26
|
+
end
|