junklet 1.0.1 → 1.0.2

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
- ZDBjY2JlZjYxYjAyMjI4NTc0NTBlZDkwZTRjNzdmMjMyZmQwNGMyMQ==
4
+ Zjc3YzlmNzVjYTc3YmZhNTIwZjZlNTE4Y2I1ZTQ2YWM0ZmNmODk3ZQ==
5
5
  data.tar.gz: !binary |-
6
- MmRjOTE4OGRiYTA4ODkyYzE3ZTAzNTQ3Y2VjOGYzZDkwZjc0NTY4MA==
6
+ NTI1YTRjZWJkMmFiMmI3ZWNhOGM2NTBhNDRkZmNjYWZiMzJjNGM4ZQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NGY2OTMwZGM3NDc5MWI5MzFjYmY2ZTFiZTdjM2VjOTFiMjJiYWVjMjk3N2Fk
10
- OWE1MDU2YTc5OWI1M2ZmMjM2YThiMzNiZGMyYTg1MGJkNzNlOTU1NDVjMWEw
11
- N2NjMjNjYzE5ZTQyYWNkMGZiNmFkODFhZjkxYzAyNzk4YTNjZmE=
9
+ OGM0ZDRjYTkxYjdhNTdlNzA5YTM3MjU5MGEyZDk3ZWQwYTQ0YjU5NjczMGE2
10
+ ZGU0NmIxNjk4ZDc1Yzg5NDAxOTU2NTM4NmJlNTE3NjIwMmM4NWZhMDM2MTNk
11
+ OTFmN2FiNjAwMThiYTBlNTFkYTM3M2I4NjE5NDQzMDZiZjIxZjk=
12
12
  data.tar.gz: !binary |-
13
- YjRkMGJhNjdhNzBmMzI4N2U3ZjgwZTVhNjdmODdjNmQxYzg0ZWM1NzBkMzYz
14
- NTljODE1NjM4Y2I2YTc0NjEzMjc3MmM3Zjg3M2I1N2NiZDZjODcyZDBjMWM5
15
- ODI3MGI1NjQ5ZmQ5MDYyNTM0ODFmY2M4ODdkMWI2MWM0Y2QwZTE=
13
+ OWIxNDM5ZGUyOWRhODg1YmM5NDFiMWZhMjNjNGY0MTRlZGQ1ZDNmY2JhNGJh
14
+ Nzk5MzVlZTk4NDJhYWIwM2E0ZDUyNmI1NzEyZGQ2NGIyYjM0M2VhOWM5YTAx
15
+ MmJiNTg0MGY3ZDY2NGNlNjJlYTU4NmViMmM2OWY5ZmNjZjlhYmU=
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- junklet (1.0.0)
4
+ junklet (1.0.2)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -119,6 +119,49 @@ This will have the same effect as calling `let` on the named fields
119
119
  and setting the fieldname and a 32-byte hex string (a uuid with
120
120
  hyphens removed) to be the memoized value.
121
121
 
122
+
123
+
124
+ # Finer Control / Custom Types
125
+
126
+ The `junk` method now has MUCH finer-grained control, though this
127
+ hasn't been pushed up to junklet yet.
128
+
129
+
130
+ `junk <type> [options]`
131
+
132
+ ```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
137
+
138
+ junk(:bool) # returns true or false
139
+
140
+ junk([:a, :b, :c]) # samples from the Array
141
+ junk(('A'..'Z')) # samples from the range.
142
+
143
+ # Memory warning: calls to_a on range first, so if you want a number
144
+ # from 100000 to 999999, favor junk(:int, min: 100000, max: 999999)
145
+ # instead.
146
+
147
+ junk ->{ your_own_random_thing_here }
148
+
149
+ # Also note that all types also take an `exclude` option, which can be
150
+ # a value, an array, or a proc. These all return even numbers:
151
+
152
+ junk :int, min: 2, max: 4, exclude: 3
153
+ junk :int, min: 2, max: 10, exclude: [3,5,7,9]
154
+ junk :int, min: 2, max: 100, exclude: ->(x) { x % 2 == 1 }
155
+
156
+ # And remember that junk is at the same level of precedence as let, so
157
+ # the following ALSO return even numbers:
158
+
159
+ let(:three) { 3 }
160
+ let(:evens) { junk :int, min: 2, max: 4, exclude: three }
161
+ let(:two_or_four) { junk :int, min: 2, max: 4, exclude: ->(x){ !evens.include?(x) }
162
+
163
+ ```
164
+
122
165
  # TODO
123
166
 
124
167
  * Formats - The original motivation for Junklet is to encapsulate the
@@ -1,3 +1,3 @@
1
1
  module Junklet
2
- VERSION = "1.0.1"
2
+ VERSION = "1.0.2"
3
3
  end
data/lib/junklet.rb CHANGED
@@ -5,12 +5,17 @@ module RSpec
5
5
  module MemoizedHelpers
6
6
  module ClassMethods
7
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.
8
13
  opts = args.size > 1 && !args.last.is_a?(Symbol) && args.pop || {}
9
14
 
10
15
  names = args.map(&:to_s)
11
16
 
12
17
  if opts.key?(:separator)
13
- names = names.map {|name| name.gsub(/_/, opts[:separator]) }
18
+ names = names.map {|name| name.gsub(/_/, opts[:separator])}
14
19
  end
15
20
 
16
21
  args.zip(names).each do |arg, name|
@@ -19,14 +24,84 @@ module RSpec
19
24
  end
20
25
  end
21
26
 
22
- def junk(size=32, options={})
23
- if :integer == options[:type]
24
- # TODO: We can actual know how many hex digits we need in
25
- # advance because of te ratio of the logs. We should totally
26
- # work that out sometime. The ratio is like 1.2 something. For
27
- # now, blah, just make it work.
28
- SecureRandom.hex.to_i(16).to_s[0..size]
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
+ # Fun fact: you can get back an arbitrarily large number
82
+ # by specifying a max value >= 2**62 so that Ruby promotes
83
+ # it to a BigNum.
84
+ min = opts[:min] || 0
85
+ max = (opts[:max] || 2**62-2) + 1
86
+ min,max = max,min if min>max
87
+
88
+ generator = -> { rand(max-min) + min }
89
+ when :bool
90
+ generator = -> { [true, false].sample }
91
+ when Array, Enumerable
92
+ generator = -> { type.to_a.sample }
93
+ when Proc
94
+ generator = type
95
+ else
96
+ raise "Unrecognized junk type: '#{type}'"
97
+ end
98
+
99
+ begin
100
+ val = generator.call
101
+ end while excluder.call(val)
102
+ val
29
103
  else
104
+ size = args.first.is_a?(Numeric) ? args.first : 32
30
105
  # hex returns size*2 digits, because it returns a 0..255 byte
31
106
  # as a hex pair. But when we want junt, we want *bytes* of
32
107
  # junk. Get (size+1)/2 chars, which will be correct for even
@@ -71,12 +71,80 @@ describe Junklet do
71
71
  end
72
72
  end
73
73
 
74
- context "with type: :decimal" do
75
- let(:junk_integer) { junk 15, type: :integer }
76
- it "returns the request number of decimal digits" do
77
- expect { (widget_id).to be_a Integer }
78
- expect { (widget_id.to_s.size).to eq(15) }
74
+ context "with type: array" do
75
+ let(:junk_ray) { junk [:a, :b, :c] }
76
+ it "returns a random element of the array" do
77
+ expect([:a, :b, :c]).to include(junk_ray)
79
78
  end
79
+
80
+ context "with excludes" do
81
+ let(:junk_ray) { junk [:a, :b, :c], exclude: [:a, :b] }
82
+ specify { expect(junk_ray).to eq(:c) }
83
+ end
84
+ end
85
+
86
+ context "with type: Proc" do
87
+ let(:junk_proc) { junk(->{ rand(3) }) }
88
+ specify { expect([0,1,2]).to include(junk_proc) }
89
+
90
+ context "with excludes" do
91
+ let(:junk_proc) { junk(->{ rand(3) }, exclude: [0,2]) }
92
+ specify { expect(junk_proc).to eq(1) }
93
+ end
94
+ end
95
+
96
+ context "with type: enumerable" do
97
+ let(:junk_list) { junk (0..3) }
98
+ it "returns a random element of the array" do
99
+ expect([0,1,2,3]).to include(junk_list)
100
+ end
101
+ end
102
+
103
+ context "with type: :int" do
104
+ let(:junk_integer) { junk :int }
105
+ it "returns a random positive Fixnum" do
106
+ expect { (junk_integer).to be_a Fixnum }
107
+ end
108
+
109
+ context "with min and max values" do
110
+ let(:coin) { junk :int, min: 0, max: 1 }
111
+ specify { expect([0,1]).to include(coin) }
112
+ end
113
+
114
+ context "with exclude proc" do
115
+ let(:junk_evens) { junk :int, min: 0, max: 10, exclude: ->(x) { x % 2 == 1 } }
116
+ specify { expect(junk_evens % 2).to eq(0) }
117
+ end
118
+ end
119
+
120
+ context "with type: :bool" do
121
+ let(:junk_bool) { junk :bool }
122
+ specify { expect([true, false]).to include(junk_bool) }
123
+
124
+ context "with excludes" do
125
+ let(:junk_bool) { junk :bool, exclude: true }
126
+ specify { expect(junk_bool).to eq(false) }
127
+ end
128
+ end
129
+
130
+ # begin
131
+ # $caught_bad_junklet_error = false
132
+ # junklet :cheesy_bad_junklet, cheese: true
133
+ # rescue INVALID_JUNKLET_ERROR => e
134
+ # raise "junklet got invalid option" unless e.message == "junklet options must be one of #{VALID_JUNKLET_ARGS.map(&:inspect) * ', '}"
135
+ # $caught_bad_junklet_error = true
136
+ # else
137
+ # raise "junklet got an invalid argument but didn't catch it" unless $caught_bad_junklet_error
138
+ # end
139
+
140
+ context "with exclude: val" do
141
+ let(:heads) { 0 }
142
+ let(:tails) { 1 }
143
+ let(:coin_heads) { junk :int, max: 1, exclude: tails }
144
+ let(:coin_tails) { junk :int, max: 1, exclude: heads }
145
+
146
+ specify { expect(coin_heads).to eq(heads) }
147
+ specify { expect(coin_tails).to eq(tails) }
80
148
  end
81
149
  end
82
150
  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.1
4
+ version: 1.0.2
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-10 00:00:00.000000000 Z
11
+ date: 2015-02-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler