Skip to content

Commit

Permalink
Implement ULID.valid_as_variants? with ULID.valid? deprecation
Browse files Browse the repository at this point in the history
  • Loading branch information
kachick committed Jul 3, 2022
1 parent 14fcca1 commit fa026b2
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 36 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
},
"ruby.format": "rubocop",
"cSpell.words": [
"kwargs",
"Undocumentable",
"unshareable"
] // use rubocop for formatting
Expand Down
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ The original `Crockford's base32` maps `I`, `L` to `1`, `O` to `0`.
And accepts freestyle inserting `Hyphens (-)`.
To consider this patterns or not is different in each implementations.

Current parser/validator/matcher aims to cover `subset of Crockford's base32`.
Current parser/validator/matcher basically aims to cover `subset of Crockford's base32`.
I have suggested it would be clarified in [ulid/spec#57](https://github.com/ulid/spec/pull/57).

>Case insensitive
Expand All @@ -356,12 +356,13 @@ But it is a controversial point, discussing in [ulid/spec#3](https://github.com/

Be that as it may, this gem provides API for handling the nasty possibilities.

`ULID.normalize` and `ULID.normalized?`
`ULID.normalize`, `ULID.normalized?`, `ULID.valid_as_variants?`

```ruby
ULID.normalize('-olarz3-noekisv4rrff-q6ig5fav--') #=> "01ARZ3N0EK1SV4RRFFQ61G5FAV"
ULID.normalized?('-olarz3-noekisv4rrff-q6ig5fav--') #=> false
ULID.normalized?('01ARZ3N0EK1SV4RRFFQ61G5FAV') #=> true
ULID.normalize('01g70y0y7g-z1xwdarexergsddd') #=> "01G70Y0Y7GZ1XWDAREXERGSDDD"
ULID.normalized?('01g70y0y7g-z1xwdarexergsddd') #=> false
ULID.normalized?('01G70Y0Y7GZ1XWDAREXERGSDDD') #=> true
ULID.valid_as_variants?('01g70y0y7g-z1xwdarexergsddd') #=> true
```

#### UUIDv4 converter (experimental)
Expand Down
28 changes: 23 additions & 5 deletions lib/ulid.rb
Original file line number Diff line number Diff line change
Expand Up @@ -280,18 +280,36 @@ def self.normalize(string)
parse(normalized_in_crockford).to_s
end

# @param [String, #to_str] string
# @return [Boolean]
def self.normalized?(object)
normalized = normalize(object)
def self.normalized?(string)
normalized = normalize(string)
rescue Exception
false
else
normalized == object
normalized == string
end

# @param [String, #to_str] string
# @return [Boolean]
def self.valid?(object)
string = String.try_convert(object)
def self.valid_as_variants?(string)
normalize(string)
rescue Exception
false
else
true
end

# @deprecated Use [.valid_as_variants?] or [.normalized?] instead
#
# Returns `true` if it is normalized string.
# Basically the difference of normalized? is to accept downcase or not. This returns true for downcased ULIDs.
#
# @return [Boolean]
def self.valid?(string)
warn_kwargs = (RUBY_VERSION >= '3.0') ? { category: :deprecated } : {}
Warning.warn('ULID.valid? is deprecated. Use ULID.valid_as_variants? or ULID.normalized? instead.', **warn_kwargs)
string = String.try_convert(string)
string ? STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET.match?(string) : false
end

Expand Down
40 changes: 33 additions & 7 deletions sig/ulid.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -309,14 +309,13 @@ class ULID < Object
# ```
def self.sample: (?period: period) -> ULID
| (Integer number, ?period: period?) -> Array[ULID]
def self.valid?: (untyped) -> bool

# Returns normalized string
#
# ```ruby
# ULID.normalize('01G70Y0Y7G-Z1XWDAREXERGSDDD') #=> "01G70Y0Y7GZ1XWDAREXERGSDDD"
# ULID.normalize('-olarz3-noekisv4rrff-q6ig5fav--') #=> "01ARZ3N0EK1SV4RRFFQ61G5FAV"
# ULID.normalized?('-olarz3-noekisv4rrff-q6ig5fav--') #=> false
# ULID.normalized?('01ARZ3N0EK1SV4RRFFQ61G5FAV') #=> true
# ULID.normalize('01G70Y0Y7G_Z1XWDAREXERGSDDD') #=> ULID::ParserError
# ```
#
# See also [ulid/spec#57](https://github.com/ulid/spec/pull/57) and [ulid/spec#3](https://github.com/ulid/spec/issues/3)
Expand All @@ -325,13 +324,40 @@ class ULID < Object
# Returns `true` if it is normalized string
#
# ```ruby
# ULID.normalize('-olarz3-noekisv4rrff-q6ig5fav--') #=> "01ARZ3N0EK1SV4RRFFQ61G5FAV"
# ULID.normalized?('-olarz3-noekisv4rrff-q6ig5fav--') #=> false
# ULID.normalized?('01ARZ3N0EK1SV4RRFFQ61G5FAV') #=> true
# ULID.normalized?('01G70Y0Y7GZ1XWDAREXERGSDDD') #=> true
# ULID.normalized?('01G70Y0Y7G-Z1XWDAREXERGSDDD') #=> false
# ULID.normalized?(ULID.generate.to_s.downcase) #=> false
# ULID.normalized?('01G70Y0Y7G_Z1XWDAREXERGSDDD') #=> false (Not raising ULID::ParserError)
# ```
#
# See also [ulid/spec#57](https://github.com/ulid/spec/pull/57) and [ulid/spec#3](https://github.com/ulid/spec/issues/3)
def self.normalized?: (_ToStr string) -> bool
| (untyped) -> false

# Returns `true` if it is valid in ULID format variants
#
# ```ruby
# ULID.valid_as_variants?(ULID.generate.to_s.downcase) #=> true
# ULID.valid_as_variants?('01G70Y0Y7G-Z1XWDAREXERGSDDD') #=> true
# ULID.valid_as_variants?('01G70Y0Y7G_Z1XWDAREXERGSDDD') #=> false
# ```
#
# See also [ulid/spec#57](https://github.com/ulid/spec/pull/57) and [ulid/spec#3](https://github.com/ulid/spec/issues/3)
def self.valid_as_variants?: (_ToStr string) -> bool
| (untyped) -> false

# DEPRECATED Use valid_as_variants? instead
#
# Returns `true` if it is normalized string.
# Basically the difference of normalized? is to accept downcase or not. This returns true for downcased ULIDs.
#
# ```ruby
# ULID.valid?(ULID.generate.to_s.downcase) #=> true
# ```
#
# See also [ulid/spec#57](https://github.com/ulid/spec/pull/57) and [ulid/spec#3](https://github.com/ulid/spec/issues/3)
def self.normalized?: (untyped) -> bool
def self.valid?: (_ToStr string) -> bool
| (untyped) -> false

# Returns parsed ULIDs from given String for rough operations.
#
Expand Down
14 changes: 13 additions & 1 deletion steep_expectations.yml
Original file line number Diff line number Diff line change
@@ -1 +1,13 @@
--- []
---
- file: lib/ulid.rb
diagnostics:
- range:
start:
line: 311
character: 12
end:
line: 311
character: 16
severity: ERROR
message: Type `singleton(::Warning)` does not have method `warn`
code: Ruby::NoMethod
70 changes: 56 additions & 14 deletions test/core/test_ulid_class.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ def test_exposed_methods
:range,
:at,
:normalized?,
:parse
:parse,
:valid_as_variants?
].sort,
exposed_methods.sort
)
Expand Down Expand Up @@ -136,19 +137,23 @@ def test_from_milliseconds_and_entropy
end

def test_valid?
assert_equal(false, ULID.valid?(nil))
assert_equal(false, ULID.valid?(''))
assert_equal(false, ULID.valid?(BasicObject.new))
assert_equal(false, ULID.valid?(Object.new))
assert_equal(false, ULID.valid?(42))
assert_equal(false, ULID.valid?(:'01ARZ3NDEKTSV4RRFFQ69G5FAV'))
assert_equal(false, ULID.valid?(ULID.sample))
assert_equal(false, ULID.valid?("01ARZ3NDEKTSV4RRFFQ69G5FAV\n"))
assert_equal(false, ULID.valid?('01ARZ3NDEKTSV4RRFFQ69G5FAU'))
assert_equal(true, ULID.valid?('01ARZ3NDEKTSV4RRFFQ69G5FAV'))
assert_equal(true, ULID.valid?('01ARZ3NDEKTSV4RRFFQ69G5FAV'.downcase))
assert_equal(true, ULID.valid?('7ZZZZZZZZZZZZZZZZZZZZZZZZZ'))
assert_equal(false, ULID.valid?('80000000000000000000000000'))
assert_warning('ULID.valid? is deprecated. Use ULID.valid_as_variants? or ULID.normalized? instead.') do
assert_equal(false, ULID.valid?(nil))
assert_equal(false, ULID.valid?(''))
assert_equal(false, ULID.valid?(BasicObject.new))
assert_equal(false, ULID.valid?(Object.new))
assert_equal(false, ULID.valid?(42))
assert_equal(false, ULID.valid?(:'01ARZ3NDEKTSV4RRFFQ69G5FAV'))
assert_equal(false, ULID.valid?(ULID.sample))
assert_equal(false, ULID.valid?("01ARZ3NDEKTSV4RRFFQ69G5FAV\n"))
assert_equal(false, ULID.valid?('01ARZ3NDEKTSV4RRFFQ69G5FAU'))
assert_equal(true, ULID.valid?('01ARZ3NDEKTSV4RRFFQ69G5FAV'))
assert_equal(true, ULID.valid?('01ARZ3NDEKTSV4RRFFQ69G5FAV'.downcase))
assert_equal(true, ULID.valid?('7ZZZZZZZZZZZZZZZZZZZZZZZZZ'))
assert_equal(false, ULID.valid?('80000000000000000000000000'))

assert_false(ULID.valid?('01G70Y0Y7G-Z1XWDAREXERGSDDD'))
end

assert_raises(ArgumentError) do
ULID.valid?
Expand Down Expand Up @@ -215,6 +220,8 @@ def test_normalize
end

def test_normalized?
assert_false(ULID.normalized?('01G70Y0Y7G-Z1XWDAREXERGSDDD'))

nasty = '-olarz3-noekisv4rrff-q6ig5fav--'
assert_equal(false, ULID.normalized?(nasty))
assert_equal(true, ULID.normalized?(ULID.normalize(nasty)))
Expand Down Expand Up @@ -247,6 +254,41 @@ def test_normalized?
end
end

def test_valid_as_variants?
assert_true(ULID.valid_as_variants?('01G70Y0Y7G-Z1XWDAREXERGSDDD'))

nasty = '-olarz3-noekisv4rrff-q6ig5fav--'
assert_true(ULID.valid_as_variants?(nasty))
assert_true(ULID.valid_as_variants?(ULID.normalize(nasty)))

normalized = '01ARZ3NDEKTSV4RRFFQ69G5FAV'
assert_true(ULID.valid_as_variants?(normalized))
assert_true(ULID.valid_as_variants?(normalized.downcase))

[
'',
"01ARZ3NDEKTSV4RRFFQ69G5FAV\n",
'01ARZ3NDEKTSV4RRFFQ69G5FAU',
'01ARZ3NDEKTSV4RRFFQ69G5FA',
'80000000000000000000000000'
].each do |invalid|
assert_false(ULID.valid_as_variants?(invalid))
end

ULID.sample(1000).each do |sample|
assert_true(ULID.valid_as_variants?(sample.to_s))
assert_true(ULID.valid_as_variants?(sample.to_s.downcase))
end

assert_raises(ArgumentError) do
ULID.valid_as_variants?
end

[nil, 42, normalized.to_sym, BasicObject.new, Object.new, ULID.parse(normalized)].each do |evil|
assert_false(ULID.valid_as_variants?(evil))
end
end

def test_range
time_has_more_value_than_milliseconds1 = Time.at(946684800, Rational('123456.789')) # 2000-01-01 00:00:00.123456789 UTC
time_has_more_value_than_milliseconds2 = Time.at(1620045632, Rational('123456.789')) # 2021-05-03 12:40:32.123456789 UTC
Expand Down
26 changes: 23 additions & 3 deletions test/helper.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
# coding: us-ascii
# frozen_string_literal: true

# How to use: https://github.com/jeremyevans/ruby-warning
require('warning')

# How to use => https://test-unit.github.io/test-unit/en/
# How to use: https://test-unit.github.io/test-unit/en/
require('test/unit')

require('irb')
require('power_assert/colorize')
require('irb/power_assert')

require 'stringio'

Warning[:deprecated] = true
Warning[:experimental] = true

Warning.process do |_warning|
:raise
Warning.process do |warning_message|
if /ULID.valid\? is deprecated/.match?(warning_message)
:default
else
:raise
end
end

require_relative('../lib/ulid')
Expand All @@ -37,4 +44,17 @@ def assert_acceptable_randomized_string(ulids)
assert_in_epsilon(awesome_randomized_ulids.size, ulids.size, (5/100r).to_f)
end
end

def assert_warning(pattern, &block)
org_stderr = $stderr
$stderr = fake_io = StringIO.new(+'', 'r+')

begin
block.call
fake_io.rewind
assert_match(pattern, fake_io.read)
ensure
$stderr = org_stderr
end
end
end
2 changes: 1 addition & 1 deletion test/many_data/test_fixed_many_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def assert_example(ulid, example)
assert_equal(example.octets, ulid.octets)

assert do
ULID.valid?(example.string)
ULID.normalized?(example.string)
end
end

Expand Down

0 comments on commit fa026b2

Please sign in to comment.