module Junklet::Junk
Public Instance Methods
junk(*args)
click to toggle source
# 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