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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MTJlN2QwZTEyOGU1OThhMzBmYzMwY2RhYTAzM2E1NzQzNWJhNGM4MQ==
4
+ MzEzOTFiOWJiOTlhZjEyMGY0MjEzYTk4MzU5ZWVhOWEzZTlhNjg4NA==
5
5
  data.tar.gz: !binary |-
6
- MjA3MDU1MDhlNzE4YmI2NGJlN2RmYmZjNGU2ZDIxMjIzMmQzMDIxNw==
6
+ YjM1OTJmZGJhNjhlZmRjM2I3OTU1MzVjOTY3Mjg4OTMxNjFiM2E5OQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- MTg2NTc3MzJmNTBiYmM4OGI4NTZjZGU0MDA3MDgzNjg1NWY4ZDY5MDlmMGQ5
10
- Mjc0MGRjYjY0ZWE2N2E1M2ZiM2E1MmVhOTBlYjk3NjdhZWQ4NGQ0YTk5YzQw
11
- MDBjZDQxYmI0YzE2NGVkN2RiMmNlY2YwMTFkY2JkMzJmZmU1MGY=
9
+ ZDBjMGMxYWE3MjI4YzljZWQ2NzQ0ZmNjMWE5ODFjZmQxZDQzOWMzNWI4NWRj
10
+ NDI5MzA1YzE2MDYzMzI5MGM0YzdmMjZjODlmODY2YzQ5YTE2OWNlNjZmNzMy
11
+ NTdiZDlmNjU0NDJiNmY5ZmMzMmU0OTZjMGNjMjFkNzEwNzIyZjA=
12
12
  data.tar.gz: !binary |-
13
- OWNhZDNlNjQ0NmZlNDdmNDg1N2U3YWZlOWMzMjIyNmUzZGY5ZGM0NDJhOWIx
14
- OWZiYzk4ZDRjYmRkNTNlZTg0YjM5YmQxZGFlMDgwMGI5N2ZmODZkNGFmODI3
15
- YmE5MTZhY2E5ZTAyMjc3NmZlNzYzYWExZDcwOTcwYWEzOTI5NGU=
13
+ NjYwMjVhOGU4OTUxZjYyMTRjMmI2ZWU0M2ZiZGQ0OTU3YTAyM2M0ZDNlYmQx
14
+ MmI2NjdhZDA5YzVkMTY1M2EzMWExODdjMDk0MGZlY2VlZDM0OTRlMGJkNjIy
15
+ MTIyMjUzZWVjNGJlZWY1ZmEyNmIzY2U1MjJlZjBiMWQ5NmUwNDg=
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- junklet (1.0.3)
4
+ junklet (1.0.4)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
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
- # If You Work At CMM
21
+ ## Installation
22
+
23
+ Add this line to your application's Gemfile:
22
24
 
23
- 1. junklet means never having to type SecureRandom again.
24
- 1. junklet prepends the field name to make errors easier to read.
25
- 1. junk() returns a 32-byte random hex number, junk(n) returns the
26
- same thing, only n bytes long (can be longer than 32)
25
+ ```ruby
26
+ gem 'junklet'
27
+ ```
27
28
 
28
- Instead of writing this -> write this:
29
+ And then execute:
29
30
 
30
- * `let(:pants) { SecureRandom.uuid }` -> `junklet :pants`
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 case, not just
45
- each test run)
65
+ uuid without hyphens and will change with each test _case_, not just
66
+ each test run).
46
67
 
47
- junklet :host_name, separator: '-'
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
- junklet :a_a, :b_b, :c_c, separator: '.'
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`, `b.b`, and `c.c` respectively.
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
- junk [length=32]
90
+ `junk` returns random junk, which can be finely tuned and fiddled with.
61
91
 
62
- Can be called from inside a spec or let block, and returns a random
63
- hex string 32 bytes long (or whatever length you specify)
92
+ ```ruby
93
+ junk
94
+ junk (integer|symbol|enumerable|proc) [options]
95
+ ```
64
96
 
65
- # Background
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
- At CoverMyMeds we have a legacy impingement that prevents us sometimes
68
- from clearing out data from previous test runs. As a result we often
69
- have required fields in tests that must be unique but are tested
70
- elsewhere, so we don't really care about them in the current test
71
- run. For the current test we just want to stub out that field with
72
- something unique but we also want to communicate to the developer that
73
- the contents of the field are not what we currently care about.
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
- let(:first_name) { SecureRandom.uuid }
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
- ...etc. Later in the spec we'll often test against those stubs,
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
- Junklet seeks to improve the readability and conciseness of this
93
- intention. One thing that bugs me about the above approach is that if
94
- a weird regression bug appears and an unimportant field is the source
95
- of a crash. So with Junklet I also wanted to add the ability to call
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
- Enter Junklet:
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
- junklet :first_name
105
- junklet :last_name
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
- Or, if you don't want the junklets to sprawl vertically,
139
+ `size`, `min` and `max` control the size and bounds of the random
140
+ number.
113
141
 
114
142
  ```ruby
115
- junklet :first_name, :last_name, :address, :city, :state, :phone
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
- This will have the same effect as calling `let` on the named fields
119
- and setting the fieldname and a 32-byte hex string (a uuid with
120
- hyphens removed) to be the memoized value.
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
- # Finer Control / Custom Types
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
- The `junk` method now has MUCH finer-grained control, though this
127
- hasn't been pushed up to junklet yet.
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
- `junk <type> [options]`
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(:int) # returns a random, positive ruby Fixnum between 0 and
134
- # 2**62-1.
135
- junk(:int, min: 5, max: 9) # returns a number from 5 to 9
136
- junk(:int, max: 1) # returns 0 or 1
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
- # If you just know how many digits you need, use size:
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
- junk(:int, size: 3) # generates number between 100 and 999
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
- # min and max can further constrain size (but not expand it):
143
- junk(:int, size: 3, min: 425) # number from 425 to 999
144
- junk(:int, size: 3, max: 425) # number from 100 to 425
195
+ ```ruby
196
+ junk({a: 42, b: 13}) # either [:a, 42] or [:b, 13]
197
+ ```
145
198
 
146
- junk(:bool) # returns true or false
199
+ ### Proc
147
200
 
148
- junk([:a, :b, :c]) # samples from the Array
149
- junk(('A'..'Z')) # samples from the range.
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
- # Memory warning: calls to_a on range first, so if you want a number
152
- # from 100000 to 999999, favor junk(:int, min: 100000, max: 999999)
153
- # instead.
205
+ ```ruby
206
+ junk ->{ rand(5)*2 + 1 } # 1-digit odd number
207
+ ```
154
208
 
155
- junk ->{ your_own_random_thing_here }
209
+ ### Other Options
156
210
 
157
- # Also note that all types also take an `exclude` option, which can be
158
- # a value, an array, or a proc. These all return even numbers:
211
+ ### Exclude
159
212
 
160
- junk :int, min: 2, max: 4, exclude: 3
161
- junk :int, min: 2, max: 10, exclude: [3,5,7,9]
162
- junk :int, min: 2, max: 100, exclude: ->(x) { x % 2 == 1 }
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
- # And remember that junk is at the same level of precedence as let, so
165
- # the following ALSO return even numbers:
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
- let(:three) { 3 }
168
- let(:evens) { junk :int, min: 2, max: 4, exclude: three }
169
- let(:two_or_four) { junk :int, min: 2, max: 4, exclude: ->(x){ !evens.include?(x) }
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
- ## Installation
285
+ # Background
208
286
 
209
- Add this line to your application's Gemfile:
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
- gem 'junklet'
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
- And then execute:
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
- $ bundle
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
- Or install it yourself as:
321
+ Enter Junklet:
220
322
 
221
- $ gem install junklet
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
- module RSpec
4
- module Core
5
- module MemoizedHelpers
6
- module ClassMethods
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Junklet
2
- VERSION = "1.0.3"
2
+ VERSION = "1.0.4"
3
3
  end
@@ -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 /^trash-/ }
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 /^trash-/ }
24
+ specify { expect(trash).to match /^trash_/ }
25
25
  specify { expect(trash).to match hex_regex }
26
- specify { expect(toss).to match /^toss-/ }
26
+ specify { expect(toss).to match /^toss_/ }
27
27
  specify { expect(toss).to match hex_regex }
28
- specify { expect(crud).to match /^crud-/ }
28
+ specify { expect(crud).to match /^crud_/ }
29
29
  specify { expect(crud).to match hex_regex }
30
- specify { expect(crap).to match /^crap-/ }
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, :last_name, :first_name, separator: '-'
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-name-/ }
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-name-/ }
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.3
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 00:00:00.000000000 Z
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