Skip to content

Commit

Permalink
I got serious with RuboCop configuration 😅
Browse files Browse the repository at this point in the history
  • Loading branch information
kachick committed May 24, 2021
1 parent f23bb13 commit 73df9f7
Show file tree
Hide file tree
Showing 20 changed files with 835 additions and 62 deletions.
771 changes: 761 additions & 10 deletions .rubocop.yml

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

source 'https://rubygems.org'

gemspec
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ ULID.floor(time) #=> 2000-01-01 00:00:00.123 UTC
For rough operations, `ULID.scan` might be useful.

```ruby
json =<<'EOD'
json = <<'JSON'
{
"id": "01F4GNAV5ZR6FJQ5SFQC7WDSY3",
"author": {
Expand All @@ -217,7 +217,7 @@ json =<<'EOD'
}
]
}
EOD
JSON

ULID.scan(json).to_a
#=>
Expand Down
2 changes: 2 additions & 0 deletions Steepfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

target :lib do
signature 'sig'

Expand Down
2 changes: 2 additions & 0 deletions benchmark/compare_with_othergems/abachman/Gemfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

source 'https://rubygems.org'

gem 'ulid-ruby', '1.0.0'
Expand Down
2 changes: 1 addition & 1 deletion benchmark/compare_with_othergems/abachman/generate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
# Below sections ensuring basic behaviors

# This Regexp taken from my code. But case sensitive
STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET = /\A(?<timestamp>[0-7][0123456789ABCDEFGHJKMNPQRSTVWXYZ]{9})(?<randomness>[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{16})\z/
STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET = /\A(?<timestamp>[0-7][0123456789ABCDEFGHJKMNPQRSTVWXYZ]{9})(?<randomness>[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{16})\z/.freeze

unless (products.size > 42) && (products.uniq === products) && products.all?(STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET) && products.all?(String)
raise 'Some bugs in the gem or this benchmark exists!'
Expand Down
2 changes: 2 additions & 0 deletions benchmark/compare_with_othergems/kachick/Gemfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

source 'https://rubygems.org'

gem 'ruby-ulid', '0.1.4'
Expand Down
2 changes: 1 addition & 1 deletion benchmark/compare_with_othergems/kachick/generate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
# Below sections ensuring basic behaviors

# This Regexp taken from my code. But case sensitive
STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET = /\A(?<timestamp>[0-7][0123456789ABCDEFGHJKMNPQRSTVWXYZ]{9})(?<randomness>[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{16})\z/
STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET = /\A(?<timestamp>[0-7][0123456789ABCDEFGHJKMNPQRSTVWXYZ]{9})(?<randomness>[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{16})\z/.freeze

unless (products.size > 42) && (products.uniq === products) && products.all?(STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET) && products.all?(String)
raise 'Some bugs in the gem or this benchmark exists!'
Expand Down
2 changes: 2 additions & 0 deletions benchmark/compare_with_othergems/rafaelsales/Gemfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

source 'https://rubygems.org'

gem 'ulid', '1.3.0'
Expand Down
2 changes: 1 addition & 1 deletion benchmark/compare_with_othergems/rafaelsales/generate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
# Below sections ensuring basic behaviors

# This Regexp taken from my code. But case sensitive
STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET = /\A(?<timestamp>[0-7][0123456789ABCDEFGHJKMNPQRSTVWXYZ]{9})(?<randomness>[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{16})\z/
STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET = /\A(?<timestamp>[0-7][0123456789ABCDEFGHJKMNPQRSTVWXYZ]{9})(?<randomness>[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{16})\z/.freeze

unless (products.size > 42) && (products.uniq === products) && products.all?(STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET) && products.all?(String)
raise 'Some bugs in the gem or this benchmark exists!'
Expand Down
1 change: 1 addition & 0 deletions bin/console
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

require 'bundler/setup'
require 'irb'
Expand Down
40 changes: 21 additions & 19 deletions lib/ulid.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# coding: us-ascii
# frozen_string_literal: true

# Copyright (C) 2021 Kenichi Kamiya

require 'securerandom'
Expand Down Expand Up @@ -60,6 +61,7 @@ def self.generate(moment: current_milliseconds, entropy: reasonable_entropy)
# @return [ULID]
def self.at(time)
raise ArgumentError, 'ULID.at takes only `Time` instance' unless Time === time

from_milliseconds_and_entropy(milliseconds: milliseconds_from_time(time), entropy: reasonable_entropy)
end

Expand Down Expand Up @@ -91,19 +93,21 @@ def self.max(moment=MAX_MILLISECONDS)
# * Do not take random generator for the arguments
# * Raising error instead of truncating elements for the given number
def self.sample(*args, period: nil)
int_generator = if period
ulid_range = range(period)
min, max, exclude_end = ulid_range.begin.to_i, ulid_range.end.to_i, ulid_range.exclude_end?
int_generator = (
if period
ulid_range = range(period)
min, max, exclude_end = ulid_range.begin.to_i, ulid_range.end.to_i, ulid_range.exclude_end?

possibilities = (max - min) + (exclude_end ? 0 : 1)
raise ArgumentError, "given range `#{ulid_range.inspect}` does not have possibilities" unless possibilities.positive?
possibilities = (max - min) + (exclude_end ? 0 : 1)
raise ArgumentError, "given range `#{ulid_range.inspect}` does not have possibilities" unless possibilities.positive?

-> {
SecureRandom.random_number(possibilities) + min
}
else
RANDOM_INTEGER_GENERATOR
end
-> {
SecureRandom.random_number(possibilities) + min
}
else
RANDOM_INTEGER_GENERATOR
end
)

case args.size
when 0
Expand Down Expand Up @@ -134,6 +138,7 @@ def self.scan(string)
string = String.try_convert(string)
raise ArgumentError, 'ULID.scan takes only strings' unless string
return to_enum(__callee__, string) unless block_given?

string.scan(SCANNING_PATTERN) do |matched|
yield parse(matched)
end
Expand All @@ -156,14 +161,15 @@ def self.from_integer(integer)
milliseconds = n32encoded_timestamp.to_i(32)
entropy = n32encoded_randomness.to_i(32)

new milliseconds: milliseconds, entropy: entropy, integer: integer
new(milliseconds: milliseconds, entropy: entropy, integer: integer)
end

# @param [Range<Time>, Range<nil>, Range[ULID]] period
# @return [Range<ULID>]
# @raise [ArgumentError] if the given period is not a `Range[Time]`, `Range[nil]` or `Range[ULID]`
def self.range(period)
raise ArgumentError, 'ULID.range takes only `Range[Time]`, `Range[nil]` or `Range[ULID]`' unless Range === period

begin_element, end_element, exclude_end = period.begin, period.end, period.exclude_end?
return period if self === begin_element && self === end_element

Expand All @@ -180,11 +186,7 @@ def self.range(period)

case end_element
when Time
if exclude_end
end_ulid = min(end_element)
else
end_ulid = max(end_element)
end
end_ulid = exclude_end ? min(end_element) : max(end_element)
when nil
# The end should be max and include end, because nil end means to cover endless ULIDs until the limit
end_ulid = MAX
Expand Down Expand Up @@ -335,7 +337,7 @@ def self.from_milliseconds_and_entropy(milliseconds:, entropy:)
n32encoded_randomness = entropy.to_s(32).rjust(RANDOMNESS_ENCODED_LENGTH, '0')
integer = (n32encoded_timestamp + n32encoded_randomness).to_i(32)

new milliseconds: milliseconds, entropy: entropy, integer: integer
new(milliseconds: milliseconds, entropy: entropy, integer: integer)
end

attr_reader :milliseconds, :entropy
Expand Down Expand Up @@ -406,7 +408,7 @@ def to_time
def octets
digits = @integer.digits(256)
(OCTETS_LENGTH - digits.size).times do
digits.push 0
digits.push(0)
end
digits.reverse!
end
Expand Down
2 changes: 2 additions & 0 deletions lib/ulid/crockford_base32.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# coding: us-ascii
# frozen_string_literal: true

# Copyright (C) 2021 Kenichi Kamiya

class ULID
Expand Down Expand Up @@ -48,6 +49,7 @@ class SetupError < ScriptError; end
end
end.freeze
raise SetupError, 'obvious bug exists in the mapping algorithm' unless N32_CHAR_BY_CROCKFORD_BASE32_CHAR.keys == crockford_base32_mappings.keys

CROCKFORD_BASE32_CHAR_PATTERN = /[#{N32_CHAR_BY_CROCKFORD_BASE32_CHAR.keys.join}]/.freeze

CROCKFORD_BASE32_CHAR_BY_N32_CHAR = N32_CHAR_BY_CROCKFORD_BASE32_CHAR.invert.freeze
Expand Down
25 changes: 15 additions & 10 deletions lib/ulid/monotonic_generator.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# coding: us-ascii
# frozen_string_literal: true

# Copyright (C) 2021 Kenichi Kamiya

class ULID
Expand Down Expand Up @@ -36,19 +37,23 @@ def generate(moment: ULID.current_milliseconds)

milliseconds = ULID.milliseconds_from_moment(moment)

ulid = if @prev.milliseconds < milliseconds
ULID.generate(moment: milliseconds)
else
ULID.from_milliseconds_and_entropy(milliseconds: @prev.milliseconds, entropy: @prev.entropy.succ)
end
ulid = (
if @prev.milliseconds < milliseconds
ULID.generate(moment: milliseconds)
else
ULID.from_milliseconds_and_entropy(milliseconds: @prev.milliseconds, entropy: @prev.entropy.succ)
end
)

unless ulid > @prev
base_message = "monotonicity broken from unexpected reasons # generated: #{ulid.inspect}, prev: #{@prev.inspect}"
additional_information = if Thread.list == [Thread.main]
'# NOTE: looks single thread only exist'
else
'# NOTE: ran on multi threads, so this might from concurrency issue'
end
additional_information = (
if Thread.list == [Thread.main]
'# NOTE: looks single thread only exist'
else
'# NOTE: ran on multi threads, so this might from concurrency issue'
end
)

raise UnexpectedError, base_message + additional_information
end
Expand Down
3 changes: 2 additions & 1 deletion lib/ulid/uuid.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# coding: us-ascii
# frozen_string_literal: true

# Copyright (C) 2021 Kenichi Kamiya

# Extracted features around UUID from some reasons
Expand All @@ -8,7 +9,7 @@
# * https://github.com/kachick/ruby-ulid/issues/76
class ULID
# Imported from https://stackoverflow.com/a/38191104/1212807, thank you!
UUIDV4_PATTERN = /\A[0-9A-F]{8}-[0-9A-F]{4}-[4][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}\z/i.freeze
UUIDV4_PATTERN = /\A[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}\z/i.freeze
private_constant :UUIDV4_PATTERN

# @param [String, #to_str] uuid
Expand Down
6 changes: 3 additions & 3 deletions ruby-ulid.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ repository_url = "https://github.com/kachick/#{lib_name}"

Gem::Specification.new do |gem|
gem.summary = %q{A handy ULID library}
gem.description = <<-EOF
gem.description = <<-'DESCRIPTION'
The ULID(Universally Unique Lexicographically Sortable Identifier) has useful specs for applications (e.g. `Database key`), especially possess all `uniqueness`, `randomness`, `extractable timestamps` and `sortable` features.
This gem aims to provide the generator, monotonic generator, parser and handy manipulation features around the ULID.
Also providing `ruby/rbs` signature files.
EOF
DESCRIPTION
gem.homepage = repository_url
gem.license = 'MIT'
gem.name = lib_name
Expand All @@ -22,7 +22,7 @@ Gem::Specification.new do |gem|
'documentation_uri' => 'https://kachick.github.io/ruby-ulid/',
'homepage_uri' => repository_url,
'source_code_uri' => repository_url,
'bug_tracker_uri' => "#{repository_url}/issues",
'bug_tracker_uri' => "#{repository_url}/issues"
}

gem.add_development_dependency 'test-unit', '>= 3.4.1', '< 4.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ def test_thread_safe_generate_with_randomized_time
assert_equal(thread_count, worked_thread_numbers.uniq.size)
assert_not_equal(worked_thread_numbers.sort, worked_thread_numbers)
assert_equal(2000, given_times.uniq.size)
assert_equal(later_than_initial_and_median_count, given_times.count{ |time| time > initial_and_median.to_time })
assert_equal(later_than_initial_and_median_count, given_times.count { |time| time > initial_and_median.to_time })

ulids_by_time = ulids.group_by(&:to_time)
uniq_times = ulids_by_time.keys
Expand All @@ -156,7 +156,7 @@ def test_thread_safe_generate_with_randomized_time

# This is a crucial spec. But I don't know the 420000 is reasonable or not...
assert do
(pred.entropy - succ.entropy).abs > 420000
(pred.entropy - succ.entropy).abs > 420000
end
assert_acceptable_randomized_string(pred)
when pred.to_time == succ.to_time
Expand Down Expand Up @@ -200,7 +200,7 @@ def test_prev_can_not_ensure_thread_safety

# This branch does not mean to omit Ruby 2.6. Just to use Enumerable#tally for debug
if RUBY_VERSION >= '2.7'
weirds = prevs.tally.select{ |_ulid, count| count > 1 }
weirds = prevs.tally.select { |_ulid, count| count > 1 }
assert do
weirds.empty?
end
Expand Down Expand Up @@ -261,7 +261,7 @@ def test_generate_prev_and_inspect_ensure_thread_safety_when_called_in_synchroni

# This branch does not mean to omit Ruby 2.6. Just to use Enumerable#tally for debug
if RUBY_VERSION >= '2.7'
weirds = prevs.tally.select{ |_ulid, count| count > 1 }
weirds = prevs.tally.select { |_ulid, count| count > 1 }
assert do
weirds.empty?
end
Expand Down
6 changes: 3 additions & 3 deletions test/core/test_ulid_class.rb
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ def test_floor
end

def test_scan
json_string =<<-'EOD'
json_string = <<-'JSON'
{
"id": "01F4GNAV5ZR6FJQ5SFQC7WDSY3",
"author": {
Expand All @@ -364,7 +364,7 @@ def test_scan
}
]
}
EOD
JSON

enum = ULID.scan(json_string)
assert_instance_of(Enumerator, enum)
Expand All @@ -384,7 +384,7 @@ def test_scan
ULID.parse('01F4GNCNC3CH0BCRZBPPDEKBKS'),
ULID.parse('01F4GNBXW1AM2KWW52PVT3ZY9X'),
ULID.parse('01F4GNCXAMXQ1SGBH5XCR6ZH0M'),
ULID.parse('01F4GND4RYYSKNAADHQ9BNXAWJ'),
ULID.parse('01F4GND4RYYSKNAADHQ9BNXAWJ')
]
assert_equal(expectation, yielded)
assert_equal(2, expectation.count(ULID.parse('01F4GNBXW1AM2KWW52PVT3ZY9X')))
Expand Down
13 changes: 7 additions & 6 deletions test/core/test_ulid_instance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def test_to_i
def test_hash_key
ulid1_1 = ULID.parse('01ARZ3NDEKTSV4RRFFQ69G5FAV')
ulid1_2 = ULID.parse('01ARZ3NDEKTSV4RRFFQ69G5FAV')
ulid2 = ULID.parse('01BX5ZZKBKACTAV9WEVGEMMVRZ')
ulid2 = ULID.parse('01BX5ZZKBKACTAV9WEVGEMMVRZ')

hash = {
ulid1_1 => :ulid1_1,
Expand All @@ -159,11 +159,12 @@ def test_hash_key

hash[having_same_hash] = :having_same_hash

assert_equal({
ulid1_2 => :ulid1_2,
ulid2 => :ulid2,
having_same_hash => :having_same_hash
}, hash)
assert_equal(
{
ulid1_2 => :ulid1_2,
ulid2 => :ulid2,
having_same_hash => :having_same_hash
}, hash)
end

def test_dup
Expand Down
2 changes: 1 addition & 1 deletion test/core/test_ulid_usecase.rb
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def test_filter_ulids_by_time
ulid3_2 = ULID.generate(moment: time3_2),
ulid3_3 = ULID.generate(moment: time3_3),
_ulid3_4 = ULID.generate(moment: time3_4),
_ulid4 = ULID.generate(moment: time4),
_ulid4 = ULID.generate(moment: time4)
]

assert_equal([ulid1_2, ulid1_3, ulid2, ulid3_1, ulid3_2, ulid3_3], ulids.grep(ULID.range(time1_2..time3_2)))
Expand Down

0 comments on commit 73df9f7

Please sign in to comment.