Class: Base32::URL

Inherits:
Object
  • Object
show all
Defined in:
lib/base32/url.rb

Constant Summary collapse

ENCODE_CHARS =
%w[0 1 2 3 4 5 6 7 8 9 a b c d e f g h j k m n p q r s t v w x y z ?].freeze
DECODE_MAP =
ENCODE_CHARS.to_enum(:each_with_index).each_with_object({}) do |(c, i), hsh|
  hsh[c] = i
end.merge({ 'i' => 1, 'l' => 1, 'o' => 0 })

Class Method Summary collapse

Class Method Details

.clean(string) ⇒ Object



196
197
198
# File 'lib/base32/url.rb', line 196

def clean(string)
  string.gsub(/-/, '').downcase
end

.decode(string, opts = {}) ⇒ Object

decode a string to an integer using Douglas Crockfords Base32 Encoding

the string is converted to uppercase and hyphens are stripped before decoding

I,i,l,L decodes to 1
O,o decodes to 0

Base32::URL.decode("16J") # => 1234
Base32::URL.decode("OI") # => 1
Base32::URL.decode("3G923-0VQVS") # => 123456789012345

returns nil if the string contains invalid characters and can’t be decoded, or if checksum option is used and checksum is incorrect



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/base32/url.rb', line 125

def self.decode(string, opts = {})
  if opts[:checksum]
    checksum = string[-2..].to_i
    string = string[0..-3]
  end

  number = clean(string).split(//).map do |char|
    DECODE_MAP[char] or return nil
  end.inject(0) { |result, val| (result << 5) + val }

  if opts[:checksum]
    remainder = 98 - ((number * 100) % 97)
    return nil if remainder != checksum
  end

  number
end

.decode!(string, _opts = {}) ⇒ Object

same as decode, but raises ArgumentError when the string can’t be decoded



164
165
166
# File 'lib/base32/url.rb', line 164

def self.decode!(string, _opts = {})
  decode(string) or raise ArgumentError
end

.decode_uuid(string, opts = {}) ⇒ Object

decode a string to a uuid using Douglas Crockfords Base32 Encoding

the string is converted to uppercase and hyphens are stripped before decoding

I,i,l,L decodes to 1
O,o decodes to 0

Base32::URL.decode_uuid("4awyymjt99wr3b8k84b9fxca") # => '0022b9ef-525a-4a79-81ad-13411697f58a'

returns nil if the string contains invalid characters and can’t be decoded, or if checksum option is used and checksum is incorrect



156
157
158
159
160
161
# File 'lib/base32/url.rb', line 156

def self.decode_uuid(string, opts = {})
  number = decode(string, opts)
  return nil unless number

  ::UUIDTools::UUID.parse_int(number).to_s
end

.encode(number, opts = {}) ⇒ Object

encodes an integer into a string

when checksum is given, a checksum is added at the end of the the string, calculated as modulo 97-10 (ISO 7064)

when split is given a hyphen is inserted every <n> characters to improve readability

when length is given, the resulting string is zero-padded to be exactly this number of characters long (hyphens are ignored)

Base32::URL.encode(1234) # => "16j"
Base32::URL.encode(123456789012345, :split=>5) # => "3g923-0vqvs"

Raises:

  • (ArgumentError)


62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/base32/url.rb', line 62

def self.encode(number, opts = {})
  # verify options
  raise ArgumentError unless opts.keys - %i[length split checksum] == []

  str = number.to_s(2).reverse.scan(/.{1,5}/).map do |bits|
    ENCODE_CHARS[bits.reverse.to_i(2)]
  end.reverse.join

  if opts[:checksum]
    remainder = 98 - ((number * 100) % 97)
    str += format('%02d', remainder)
  end

  str = str.rjust(opts[:length], '0') if opts[:length]

  if opts[:split]
    str = str.reverse
    str = str.scan(/.{1,#{opts[:split]}}/).map(&:reverse)
    str = str.reverse.join('-')
  end

  str
end

.encode_uuid(uuid, opts = {}) ⇒ Object

encodes a uuid into a string

when checksum is given, a checksum is added at the end of the the string, calculated as modulo 97-10 (ISO 7064)

when split is given a hyphen is inserted every <n> characters to improve readability

when length is given, the resulting string is zero-padded to be exactly this number of characters long (hyphens are ignored)

Base32::URL.encode_uuid('0022b9ef-525a-4a79-81ad-13411697f58a') # => "16j"
Base32::URL.encode_uuid('6179ad80-cc7f-4904-9260-0ecb3c3a90ba', :split=>5) # => "3g923-0vqvs"

Raises:

  • (ArgumentError)


101
102
103
104
105
106
107
108
# File 'lib/base32/url.rb', line 101

def self.encode_uuid(uuid, opts = {})
  # verify options
  raise ArgumentError unless opts.keys - %i[length split checksum] == []

  number = ::UUIDTools::UUID.parse(uuid).to_i

  encode(number, opts)
end

.normalize(string, opts = {}) ⇒ Object

return the canonical encoding of a string. converts it to uppercase and removes hyphens

replaces invalid characters with a question mark (‘?’)



173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/base32/url.rb', line 173

def self.normalize(string, opts = {})
  if opts[:checksum]
    checksum = string.split(//).last(2).join
    string = string[0..-2]
  end

  string = clean(string).split(//).map do |char|
    ENCODE_CHARS[DECODE_MAP[char] || 32]
  end.join

  string += checksum if opts[:checksum]

  string
end

.valid?(string, opts = {}) ⇒ Boolean

returns false if the string contains invalid characters and can’t be decoded

Returns:

  • (Boolean)


191
192
193
# File 'lib/base32/url.rb', line 191

def self.valid?(string, opts = {})
  (normalize(string, opts) !~ /\?/)
end