Skip to content

Commit

Permalink
Add prefix option
Browse files Browse the repository at this point in the history
  • Loading branch information
drexed committed Sep 20, 2024
1 parent efaebcf commit 3787310
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 11 deletions.
2 changes: 2 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ Metrics/MethodLength:
Enabled: false
RSpec/MultipleExpectations:
Enabled: false
RSpec/MultipleMemoizedHelpers:
Enabled: false
Style/ArgumentsForwarding:
Enabled: false
Style/Documentation:
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [1.5.0] - 2024-09-20
### Added
- Added uuid version option
- Added prefix option to hashid and nanoid
### Changed
- Move reversible and irreversible files to base namespace
- Ruby send perf improvements

## [1.4.0] - 2022-11-20
Expand Down
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,19 @@ Lite::Uxid::Uuid.encode #=> '4376a67e-1189-44b3-a599-7f7566bf105b'
Local options can be passed to override global options.

```ruby
Lite::Uxid::Ulid.encode(chars: 'abc123', size: 12) #=> 'a3b12c12c3ca'
Lite::Uxid::Ulid.encode(charset: 'abc123', size: 12) #=> 'a3b12c12c3ca'
```

Passable options are:

```ruby
{
charset: 'string', # Available for: hashid, nanoid, ulid
salt: 'string', # Available for: hashid
size: 'integer', # Available for: hashid, nanoid, ulid
version: 'integer', # Available for: uuid
prefix: 'string' # Available for: hashid, nanoid
}
```

## ActiveRecord
Expand Down Expand Up @@ -149,6 +161,18 @@ class User < ActiveRecord::Base
end
```

Add a prefix to `hashid` and `nanoid` record types by adding a `uxid_prefix` method.

```ruby
class User < ActiveRecord::Base
include Lite::Uxid::Record::Hashid

def uxid_prefix
"sub_"
end
end
```

**Usage**

Using one of the mixins above provides a handy method to find records by uxid.
Expand Down
4 changes: 4 additions & 0 deletions lib/lite/uxid/base/irreversible.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ def coder_length
@coder_length ||= coder_charset.size
end

def coder_prefix
@coder_prefix ||= opts.delete(:prefix)
end

def coder_salt
@coder_salt ||= coder_value_for(:salt)
end
Expand Down
6 changes: 4 additions & 2 deletions lib/lite/uxid/hashid.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ module Uxid
class Hashid < Base::Reversible

def encode
encode_chars((id + coder_salt) << coder_size)
uxid = encode_chars((id + coder_salt) << coder_size)
"#{coder_prefix}#{uxid}"
end

def decode
(decode_chars(id) >> coder_size) - coder_salt
uxid = id.delete_prefix(coder_prefix.to_s)
(decode_chars(uxid) >> coder_size) - coder_salt
end

private
Expand Down
4 changes: 3 additions & 1 deletion lib/lite/uxid/nanoid.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ module Uxid
class Nanoid < Base::Irreversible

def encode
(0...coder_size).each_with_object(+"") do |i, str|
uxid = (0...coder_size).each_with_object(+"") do |i, str|
str << coder_charset[coder_bytes[i] & 63]
end

"#{coder_prefix}#{uxid}"
end

end
Expand Down
10 changes: 7 additions & 3 deletions lib/lite/uxid/record/hashid.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module Hashid

class_methods do
def find_by_uxid(uxid)
decoded_id = Lite::Uxid::Hashid.decode(uxid)
decoded_id = Lite::Uxid::Hashid.decode(uxid, prefix: new.uxid_prefix)
find_by(id: decoded_id)
end

Expand All @@ -30,13 +30,17 @@ def find_by_uxid!(uxid)
def id_to_uxid
return unless respond_to?(:uxid)

Lite::Uxid::Hashid.encode(id)
Lite::Uxid::Hashid.encode(id, prefix: uxid_prefix)
end

def uxid_to_id
return unless respond_to?(:uxid)

Lite::Uxid::Hashid.decode(uxid)
Lite::Uxid::Hashid.decode(uxid, prefix: uxid_prefix)
end

def uxid_prefix
nil
end

private
Expand Down
6 changes: 5 additions & 1 deletion lib/lite/uxid/record/nanoid.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@ module Nanoid
before_create :callback_generate_uxid!, if: proc { respond_to?(:uxid) && !uxid? }
end

def uxid_prefix
nil
end

private

def callback_generate_uxid!
random_nanoid = nil

loop do
random_nanoid = Lite::Uxid::Nanoid.encode
random_nanoid = Lite::Uxid::Nanoid.encode(prefix: uxid_prefix)
break unless self.class.exists?(uxid: random_nanoid)
end

Expand Down
24 changes: 22 additions & 2 deletions spec/lite/uxid/hashid_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,27 @@
require "spec_helper"

RSpec.describe Lite::Uxid::Hashid do
let(:prefix) { nil }
let(:id) { 10 }
let(:hashid) { "1zWr1m0" }
let(:encoder) { described_class.new(id) }
let(:decoder) { described_class.new(hashid) }
let(:prefixed_hashid) { "test_1zWr1m0" }
let(:encoder) { described_class.new(id, prefix: prefix) }
let(:decoder) { described_class.new(hashid, prefix: prefix) }

describe ".decode" do
it "to be 10" do
expect(described_class.decode(hashid)).to eq(id)
expect(decoder.decode).to eq(id)
end

context "with prefix" do
let(:prefix) { "test_" }

it "to be 10" do
expect(described_class.decode(hashid, prefix: prefix)).to eq(id)
expect(decoder.decode).to eq(id)
end
end
end

describe ".encode" do
Expand All @@ -25,6 +36,15 @@
expect(described_class.encode(id).size).to eq(7)
expect(encoder.encode.size).to eq(7)
end

context "with prefix" do
let(:prefix) { "test_" }

it 'to be "test_1zWr1m0"' do
expect(described_class.encode(id, prefix: prefix)).to eq(prefixed_hashid)
expect(encoder.encode).to eq(prefixed_hashid)
end
end
end

end
12 changes: 11 additions & 1 deletion spec/lite/uxid/nanoid_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,23 @@
require "spec_helper"

RSpec.describe Lite::Uxid::Nanoid do
let(:encoder) { described_class.new }
let(:prefix) { nil }
let(:encoder) { described_class.new(prefix: prefix) }

describe ".encode" do
it "to be 21" do
expect(described_class.encode.size).to eq(21)
expect(encoder.encode.size).to eq(21)
end

context "with prefix" do
let(:prefix) { "test_" }

it 'to be "test_1zWr1m0"' do
expect(described_class.encode(prefix: prefix).starts_with?(prefix)).to be(true)
expect(encoder.encode.starts_with?(prefix)).to be(true)
end
end
end

end

0 comments on commit 3787310

Please sign in to comment.