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 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