Class: Rex::RandomIdentifier::Generator
- Inherits:
-
Object
- Object
- Rex::RandomIdentifier::Generator
- Defined in:
- lib/rex/random_identifier/generator.rb
Overview
A quick way to produce unique random strings that follow the rules of identifiers, i.e., begin with a letter and contain only alphanumeric characters and underscore.
The advantage of using this class over, say, Text.rand_text_alpha each time you need a new identifier is that it ensures you don’t have collisions.
Defined Under Namespace
Classes: ExhaustedSpaceError
Constant Summary collapse
- DefaultOpts =
Default options
{ # Arbitrary :max_length => 12, :min_length => 3, # This should be pretty universal for identifier rules :char_set => Rex::Text::AlphaNumeric+"_", :first_char_set => Rex::Text::LowerAlpha, :forbidden => [].freeze }
- JavaOpts =
DefaultOpts.merge( forbidden: ( DefaultOpts[:forbidden] + %w[ abstract assert boolean break byte case catch char class const continue default do double else enum extends false final finally float for goto if implements import instanceof int interface long native new null package private protected public return short static strictfp super switch synchronized this throw throws transient true try void volatile while _ ] ).uniq.freeze )
- JSPOpts =
JavaOpts.merge( forbidden: ( JavaOpts[:forbidden] + # Reserved Words for Implicit Objects # https://docs.oracle.com/cd/E13222_01/wls/docs90/webapp/reference.html#66991 %w[ application config out page pageContext request response session var ] ).uniq.freeze )
- Opts =
{ default: DefaultOpts, java: JavaOpts, jsp: JSPOpts }
Instance Method Summary collapse
-
#forbid_id?(ident = nil) ⇒ Boolean
Check if an identifier is forbidden.
-
#generate(len = nil) {|String| ... } ⇒ String
Create a random string that satisfies most languages’ requirements for identifiers.
-
#get(name, len = nil) ⇒ String
(also: #[], #init_var)
Return a unique random identifier for
name
, generating a new one if necessary. -
#initialize(opts = {}) ⇒ Generator
constructor
A new instance of Generator.
-
#store(name, value) ⇒ void
Add a new identifier.
-
#to_h ⇒ Hash
Returns the @value_by_name hash.
Constructor Details
#initialize(opts = {}) ⇒ Generator
Returns a new instance of Generator.
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/rex/random_identifier/generator.rb', line 77 def initialize(opts={}) # Holds all identifiers. @value_by_name = {} # Inverse of value_by_name so we can ensure uniqueness without # having to search through the whole list of values @name_by_value = {} language = opts[:language] || :default unless Opts.has_key?(language) raise ArgumentError, "Language option #{language} is not supported. Expected one of #{Opts.keys}" end @opts = Opts[language] @opts = @opts.merge(opts) if @opts[:min_length] < 1 || @opts[:max_length] < 1 || @opts[:max_length] < @opts[:min_length] raise ArgumentError, "Invalid length options" end # This is really just the maximum number of shortest names. This # will still be a pretty big number most of the time, so don't # bother calculating the real one, which will potentially be # expensive, since we're talking about a 36-digit decimal number to # represent the total possibilities for the range of 10- to # 20-character identifiers. # # 26 because the first char is lowercase alpha, (min_length - 1) and # not just min_length because it includes that first alpha char. @max_permutations = 26 * (@opts[:char_set].length ** (@opts[:min_length]-1)) # The real number of permutations could be calculated thusly: #((@opts[:min_length]-1) .. (@opts[:max_length]-1)).reduce(0) { |a, e| # a + (26 * @opts[:char_set].length ** e) #} end |
Instance Method Details
#forbid_id?(ident = nil) ⇒ Boolean
Check if an identifier is forbidden
223 224 225 |
# File 'lib/rex/random_identifier/generator.rb', line 223 def forbid_id?(ident = nil) ident.nil? or @opts[:forbidden].any? {|f| f.match(/^#{ident}$/i) } end |
#generate(len = nil) {|String| ... } ⇒ String
Calling this method with a block that returns only values that this generator already contains will result in an infinite loop.
Create a random string that satisfies most languages’ requirements for identifiers. In particular, with a default configuration, the first character will always be lowercase alpha (unless modified by a block), and the whole thing will contain only a-zA-Z0-9_ characters.
If called with a block, the block will be given the identifier before uniqueness checks. The block’s return value will be the new identifier. Note that the block may be called multiple times if it returns a non-unique value.
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/rex/random_identifier/generator.rb', line 192 def generate(len = nil) raise ArgumentError, "len must be positive integer" if len && len < 1 raise ExhaustedSpaceError if @value_by_name.length >= @max_permutations # pick a random length within the limits len ||= rand(@opts[:min_length] .. (@opts[:max_length])) ident = "" # XXX: Infinite loop if block returns only values we've already # generated. loop do ident = Rex::Text.rand_base(1, "", @opts[:first_char_set]) ident << Rex::Text.rand_base(len-1, "", @opts[:char_set]) if block_given? ident = yield ident end # Try to make another one if it collides with a previously # generated one. break unless @name_by_value.key?(ident) or forbid_id?(ident) end ident end |
#get(name, len = nil) ⇒ String Also known as: [], init_var
Return a unique random identifier for name
, generating a new one if necessary.
124 125 126 127 128 129 130 131 |
# File 'lib/rex/random_identifier/generator.rb', line 124 def get(name, len = nil) return @value_by_name[name] if @value_by_name[name] @value_by_name[name] = generate(len) @name_by_value[@value_by_name[name]] = name @value_by_name[name] end |
#store(name, value) ⇒ void
This should be called before any calls to #get to avoid potential collisions. If you do hit a collision, this method will raise.
This method returns an undefined value.
Add a new identifier. Its name will be checked for uniqueness among previously-generated names.
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/rex/random_identifier/generator.rb', line 147 def store(name, value) case @name_by_value[value] when name # we already have this value and it is associated with this name # nothing to do here when nil # don't have this value yet, so go ahead and just insert @value_by_name[name] = value @name_by_value[value] = name else # then the caller is trying to insert a duplicate raise RuntimeError, "Value is not unique!" end self end |
#to_h ⇒ Hash
Returns the @value_by_name hash
113 114 115 |
# File 'lib/rex/random_identifier/generator.rb', line 113 def to_h return @value_by_name end |