Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Random::Formatter#phrase #3

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions lib/random/formatter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@
# prng.alphanumeric(10) #=> "aOxAg8BAJe"
# Random.alphanumeric #=> "TmP9OsJHJLtaZYhP"
#
# Generate random phrase strings:
#
# prng.phrase(10) #=> "XaNgz+qGgG"
# prng.phrase(10) #=> "tKn0l^hBxf"
# prng.phrase(20) #=> "p1TSO+eBCmD.5XVRP+hT"
#
# Generate UUIDs:
#
# prng.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"
Expand Down Expand Up @@ -370,4 +376,80 @@ def alphanumeric(n = nil, chars: ALPHANUMERIC)
n = 16 if n.nil?
choose(chars, n)
end

# Default chunk size for #phrase
CHUNK_SIZE = 5

# Default punctuation joining chunks for #phrase
PUNCT = %w[! - + / . ^ ~].freeze

# Default excluded characters for #phrase
EXCLUDE = %w"0 O o 1 l".freeze
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: Is there a specific style reason to have another %w list delimiter for EXCLUDE than what PUNCT is using?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course nothing ;)


# Random::Formatter#phrase generates a random phrase string.
#
# The argument _n_ specifies the length, in characters, of the
# random phrase string to be generated.
#
# If _n_ is not specified or is nil, 40 is assumed. If _n_ is a
# Range, the length will be chosen in that range randomly. The
# default length may be larger in the future.
#
# The result will consist of chunks upto _chunk_ bytes separated by
# the elements of _separators_. The chunks consist of alpha-numeric
# characters, except for the elements of _exclude_ unless it is
# falsy. The last chunk may be longer than _chunk_. _separators_
# should be an Array of one-byte Strings, or a String. The defaults
# of _chunk_, _separators_ and _exclude_ are CHUNK_SIZE, PUNCT and
# EXCLUDE respectively.
#
# require 'random/formatter'
#
# prng.phrase #=> "kDc9y^Xyii4.rm3WB~MAgl0^pSOBn^jsQKc^WakTU!OjfSs"
# prng.phrase(20) #=> "rKz4p-QihCf.zHff4^ukRGn"
#
# If _separators_ is +nil+ or empty, the result will consist of only
# one chunk without separators.
#
# require 'random/formatter'
#
# prng.phrase(10, separators: nil) #=> "YAzMKLQT8G"
def phrase(n = 40, chunk: CHUNK_SIZE, separators: PUNCT, exclude: EXCLUDE)
raise ArgumentError, "invalid chunk size" unless chunk > 0
case n
when Range
unless n = random_number(Range.new([n.begin || 1, 1].max, n.end, n.exclude_end?))
raise ArgumentError, "invalid phrase length range"
end
else
raise ArgumentError, "empty phrase length" unless n > 0
end
case separators
when String
separators = separators.chars
end
if separators
case
when separators.size > 1
sep = proc {choose(separators, 1)}
when separators.size > 0
sep = proc {separators[0]}
end
end
if sep
w, d = n.divmod(chunk + 1)
if d.zero? and w > 1
w -= 1
d = chunk + 1
end
else
w, d = 0, n
end
source = ALPHANUMERIC
source -= exclude if exclude
ph = ''.dup
w.times {ph << choose(source, chunk) << sep[]}
ph << choose(source, d) unless d.zero?
ph
end
end
18 changes: 18 additions & 0 deletions test/ruby/test_random_formatter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,24 @@ def test_alphanumeric_chars
end
end

def test_phrase
formatter = Random::Formatter
s = @it.phrase
assert_equal(40, s.size)
formatter::EXCLUDE.each do |c|
assert_not_include(s, c)
end
s.scan(/\W/) do |c|
assert_include(formatter::PUNCT, c)
end
s.scan(/\w+/) do |c|
assert_operator(c.size, :<=, formatter::CHUNK_SIZE)
end

s = @it.phrase(10, separators: nil)
assert_match(/\A\w{10}\z/, s)
end

def assert_in_range(range, result, mesg = nil)
assert(range.cover?(result), build_message(mesg, "Expected #{result} to be in #{range}"))
end
Expand Down