Module: Junklet::Junk
- Defined in:
- lib/junklet/junk.rb
Instance Method Summary collapse
Instance Method Details
#junk(*args) ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/junklet/junk.rb', line 3 def junk(*args) # TODO: It's long past time to extract this.... # args.first can be # - an integer indicating the size of the hex string to return # - a symbol denoting the base type, e.g. :int # - an array to sample from # - a range or Enumerable to sample from. WARNING: will call # .to_a on it first, which might be expensive # - a generator Proc which, when called, will generate the # value to use. # # args.rest is a hash of options: # - sequence: Proc or Array of values to choose from. # - exclude: value, array of values, or proc to exclude. If a # Proc is given, it takes the value generated and returns # true if the value should be excluded. # # - for int: # - min: minimum number to return. Default: 0 # - max: upper limit of range # - exclude: number, array of numbers, or proc to # exclude. If Proc is provided, tests the number against # the Proc an excludes it if the Proc returns true. This # is implemented except for the proc. # FIXME: Raise Argument error unless *args.size is 0-2 # FIXME: If arg 1 is a hash, it's the options hash, raise # ArgumentError unless args.size == 1 # FIXME: If arg 2 present, Raise Argument error unless it's a # hash. # FIXME: Figure out what our valid options are and parse them; # raise errors if present. classes = [Symbol, Array, Enumerable, Proc] if args.size > 0 && classes.any? {|klass| args.first.is_a?(klass) } type = args.shift opts = args.last || {} excluder = if opts[:exclude] if opts[:exclude].is_a?(Proc) opts[:exclude] else ->(x) { Array(opts[:exclude]).include?(x) } end else ->(x) { false } end # TODO: Refactor me. Seriously, this is a functional # programming version of the strategy pattern. Wouldn't it # be neat if we had some kind of object-oriented language # available here? case type when :int # min,max cooperate with size to further constrain it. So # size: 2, min: 30 would be min 30, max 99. if opts[:size] sized_min = 10**(opts[:size]-1) sized_max = 10**opts[:size]-1 end explicit_min = opts[:min] || 0 explicit_max = (opts[:max] || 2**62-2) + 1 if sized_min min = [sized_min, explicit_min].max max = [sized_max, explicit_max].min else min = sized_min || explicit_min max = sized_max || explicit_max end min,max = max,min if min>max generator = -> { rand(max-min) + min } when :bool generator = -> { [true, false].sample } when Array, Enumerable generator = -> { type.to_a.sample } when Proc generator = type else raise "Unrecognized junk type: '#{type}'" end begin val = generator.call end while excluder.call(val) val else size = args.first.is_a?(Numeric) ? args.first : 32 # hex returns size*2 digits, because it returns a 0..255 byte # as a hex pair. But when we want junt, we want *bytes* of # junk. Get (size+1)/2 chars, which will be correct for even # sizes and 1 char too many for odds, so trim off with # [0...size] (note three .'s to trim off final char) SecureRandom.hex((size+1)/2)[0...size] end end |