user_friendly_id 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.md +54 -0
- data/Rakefile +6 -0
- data/lib/user_friendly_id.rb +4 -0
- data/lib/user_friendly_id/digits.rb +8 -0
- data/lib/user_friendly_id/integer.rb +43 -0
- data/lib/user_friendly_id/string.rb +37 -0
- data/lib/user_friendly_id/version.rb +3 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/user_friendly_id_spec.rb +111 -0
- data/user_friendly_id.gemspec +21 -0
- metadata +103 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9cf76745e7b09f8d74ec33802bdb3e4ce3cd854b
|
4
|
+
data.tar.gz: 215888ec2be67489940feb426807a2bc9ee8eeb8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 89976bd39fce604428b45c02269f4c8ccfd7f996224a27c3d9d5445c0d628af70678cdd2691bcb694d2e7eed031322a1bc563936866524b60ba11b1c120f11e1
|
7
|
+
data.tar.gz: 650d7d09da855c6c8d78dcdaa7bcb85b46d53ef57f0e48d704578e27acaa78559e05708e0dc486ff87a6841cd8067ce8ac892ffbe9bbd26827c67d59bfd24c75
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
(The MIT License)
|
2
|
+
|
3
|
+
Copyright (c) 2009 Alex Agranov
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
20
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
21
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
22
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
user_friendly_id
|
2
|
+
======
|
3
|
+
Add methods to Integer and String to convert between base10 and base34 representations of a number.
|
4
|
+
|
5
|
+
Base34 rocks!!!
|
6
|
+
- shorter than decimal
|
7
|
+
- shorter than hexadecimal
|
8
|
+
- case insensitive when compared with base64
|
9
|
+
- avoids user input confusion associated with 1/I and 0/O
|
10
|
+
|
11
|
+
Base34 is represented with the following characters: [0-9] and [A-Z, minus I & O to avoid any readability issues]
|
12
|
+
|
13
|
+
Applications of user_friendly_id could include, but are not limited to:
|
14
|
+
- gift/promotion/discount/redemption code generation
|
15
|
+
- much shorter string representation of long decimal strings, such as a UUID or MongoDB object id
|
16
|
+
|
17
|
+
Example Usage:
|
18
|
+
==============
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
>> 33.to_base34
|
22
|
+
=> "Z"
|
23
|
+
|
24
|
+
>> 34.to_base34
|
25
|
+
=> "10"
|
26
|
+
|
27
|
+
>> "10".to_base10
|
28
|
+
=> 34
|
29
|
+
|
30
|
+
>> 1155.to_base34
|
31
|
+
=> "ZZ"
|
32
|
+
|
33
|
+
>> "ZZ".from_base34
|
34
|
+
=> 1155
|
35
|
+
|
36
|
+
>> "0000000000ZZ".from_base34
|
37
|
+
=> 1155
|
38
|
+
|
39
|
+
>> " 00ZZ ".from_base34
|
40
|
+
=> 1155
|
41
|
+
|
42
|
+
# you can represent 10^24 - one septillion - with just 16 base34 digits, aka, a string of length 16
|
43
|
+
>> (10 ** 24).to_base34
|
44
|
+
=> "ANGMLFL5UA0AW72G"
|
45
|
+
|
46
|
+
# typical MongoDB ObjectId
|
47
|
+
>> "507f191e810c19729de860ea".hex.to_base34
|
48
|
+
=> "6RRUV0LLDJG0N6MAYBL"
|
49
|
+
|
50
|
+
# typical UUID
|
51
|
+
>> "6948DF80-14BD-4E04-8842-7668D9C001F5".gsub(/-/,'').hex.to_base34
|
52
|
+
=> "QKH6RDPUM5ACPE8GWKWB2PSMB"
|
53
|
+
|
54
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
class Integer
|
2
|
+
|
3
|
+
=begin rdoc
|
4
|
+
Turn a base10 integer into a base34 string representation.
|
5
|
+
The basic methodology is to decompose work into multiples of 34 - we create the base34 number by taking base34 chunks away from the base10 number.
|
6
|
+
=end
|
7
|
+
def to_base34
|
8
|
+
# our storage string...
|
9
|
+
base34_str = String.new
|
10
|
+
|
11
|
+
# the zero case...
|
12
|
+
if self == 0
|
13
|
+
base34_str << UserFriendlyId::BASE34_DIGITS[0]
|
14
|
+
return base34_str
|
15
|
+
end
|
16
|
+
|
17
|
+
# get sign...
|
18
|
+
sign = self/(self.abs)
|
19
|
+
|
20
|
+
# make a working copy of self...
|
21
|
+
work = self.abs
|
22
|
+
|
23
|
+
# we build the base34 number from right to left...
|
24
|
+
place = 0
|
25
|
+
while work > 0 do
|
26
|
+
# grab the portion that is representable in our 'place' before we have to increment up to the next 'place'
|
27
|
+
remainder = work % (34**(place+1))
|
28
|
+
# build the base34 number from right to left (i.e, tack on at the front)...
|
29
|
+
base34_str.insert(0, UserFriendlyId::BASE34_DIGITS[remainder/(34**place)])
|
30
|
+
# remove the portion we just represented...
|
31
|
+
work = work - remainder
|
32
|
+
# keep going...
|
33
|
+
place += 1
|
34
|
+
end
|
35
|
+
|
36
|
+
# append a negative sign if negative...
|
37
|
+
if sign < 0
|
38
|
+
base34_str.insert(0,'-')
|
39
|
+
end
|
40
|
+
|
41
|
+
return base34_str
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class String
|
2
|
+
|
3
|
+
=begin rdoc
|
4
|
+
Turn a base34 string representation into a base10 integer.
|
5
|
+
=end
|
6
|
+
def from_base34
|
7
|
+
raise ArgumentError.new("Invalid characters for a base34 number found") unless self.valid_base34?
|
8
|
+
# strip whitespace, negative sign, and upcase
|
9
|
+
kleen = self.kleened.upcase
|
10
|
+
total = 0
|
11
|
+
size = kleen.length
|
12
|
+
for i in 0..size-1
|
13
|
+
total += UserFriendlyId::BASE34_DIGITS.index(kleen[i,1]) * 34**(size-(i+1))
|
14
|
+
end
|
15
|
+
total *= -1 if is_negative?
|
16
|
+
return total
|
17
|
+
end
|
18
|
+
|
19
|
+
def valid_base34?
|
20
|
+
# only the alpha minus I & O is what we want...
|
21
|
+
not /[^0-9a-zA-Z&&[^ioIO]]/.match(self.kleened)
|
22
|
+
end
|
23
|
+
|
24
|
+
def is_negative?
|
25
|
+
self.chr == '-'
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def kleened
|
31
|
+
# get rid of leading/trailing whitespace...
|
32
|
+
kleen = self.strip
|
33
|
+
# strip any leading negative sign...
|
34
|
+
kleen = kleen[1..-1] if is_negative?
|
35
|
+
kleen
|
36
|
+
end
|
37
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'user_friendly_id'
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe UserFriendlyId do
|
4
|
+
subject { UserFriendlyId.new }
|
5
|
+
|
6
|
+
describe '#to_base34' do
|
7
|
+
context 'method existence' do
|
8
|
+
it 'is callable on any Integer' do
|
9
|
+
expect(Integer(45)).to respond_to :to_base34
|
10
|
+
expect(45).to respond_to :to_base34
|
11
|
+
expect(456789012345678901234567).to respond_to :to_base34
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'correct conversion' do
|
16
|
+
it 'handles 0 just fine' do
|
17
|
+
expect(0.to_base34).to eq "0"
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'handles conversion to base34 as expected' do
|
21
|
+
expect((34 ** 1).to_base34).to eq "10"
|
22
|
+
expect((34 ** 5).to_base34).to eq "100000"
|
23
|
+
expect((34 ** 10).to_base34).to eq "10000000000"
|
24
|
+
expect(1155.to_base34).to eq "ZZ"
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'handles negative integer as expected' do
|
28
|
+
expect((-34 ** 1).to_base34).to eq "-10"
|
29
|
+
expect((-34 ** 5).to_base34).to eq "-100000"
|
30
|
+
expect((-34 ** 10).to_base34).to eq "-10000000000"
|
31
|
+
expect(-1155.to_base34).to eq "-ZZ"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#valid_base34?' do
|
37
|
+
context 'method existence' do
|
38
|
+
it 'is callable on any String' do
|
39
|
+
expect(String.new("sdfasdfasdf")).to respond_to :valid_base34?
|
40
|
+
expect("45").to respond_to :valid_base34?
|
41
|
+
expect("45AU34KFH567").to respond_to :valid_base34?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'valid base34 characters' do
|
46
|
+
it "should handle any base34 character" do
|
47
|
+
expect("0123456789".valid_base34?).to eq true
|
48
|
+
expect("abcdefghjklmnpqrstuvwxyz".valid_base34?).to eq true
|
49
|
+
expect("ABCDEFGHJKLMNPQRSTUVWXYZ".valid_base34?).to eq true
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should handle any base34 character beginning with negative sign" do
|
53
|
+
expect("-0123456789".valid_base34?).to eq true
|
54
|
+
expect("-abcdefghjklmnpqrstuvwxyz".valid_base34?).to eq true
|
55
|
+
expect("-ABCDEFGHJKLMNPQRSTUVWXYZ".valid_base34?).to eq true
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should not like negative sign embedded within string" do
|
59
|
+
expect("-01234-56789".valid_base34?).to eq false
|
60
|
+
expect("-abcdefghj-klmnpqrstuvwxyz".valid_base34?).to eq false
|
61
|
+
expect("-ABCDEFGHJK-LMNPQRSTUVWXYZ".valid_base34?).to eq false
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should not like I or O within string" do
|
65
|
+
expect("io".valid_base34?).to eq false
|
66
|
+
expect("IO".valid_base34?).to eq false
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
describe '#from_base34' do
|
73
|
+
context 'method existence' do
|
74
|
+
it 'is callable on any String' do
|
75
|
+
expect(String.new("sdfasdfasdf")).to respond_to :from_base34
|
76
|
+
expect("45").to respond_to :from_base34
|
77
|
+
expect("45AU34KFH567").to respond_to :from_base34
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'correct conversion' do
|
82
|
+
it 'handles 0 just fine' do
|
83
|
+
expect("0".from_base34).to eq 0
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'handles whitespace at front/back just fine' do
|
87
|
+
expect(" 10 ".from_base34).to eq 34
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'handles conversion from base34 as expected' do
|
91
|
+
expect("10".from_base34).to eq (34 ** 1)
|
92
|
+
expect("100000".from_base34).to eq (34 ** 5)
|
93
|
+
expect("10000000000".from_base34).to eq (34 ** 10)
|
94
|
+
expect("ZZ".from_base34).to eq 1155
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'handles negative integer as expected' do
|
98
|
+
expect("-10".from_base34).to eq (-34 ** 1)
|
99
|
+
expect("-100000".from_base34).to eq (-34 ** 5)
|
100
|
+
expect("-10000000000".from_base34).to eq (-34 ** 10)
|
101
|
+
expect("-ZZ".from_base34).to eq -1155
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'bad strings' do
|
106
|
+
it 'should raise ArgumentError' do
|
107
|
+
expect{"-90 0453".from_base34}.to raise_error(ArgumentError)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/user_friendly_id/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Alex Agranov"]
|
6
|
+
gem.email = ["alex@morphogenic.net"]
|
7
|
+
gem.description = %q{Make long integer ids pretty, short and less confusing by shortening via conversion to base34}
|
8
|
+
gem.summary = %q{By leveraging base34 - [0-9, A-Z minus I & O] - we can significantly reduce the number of characters used to represent a number. For instance, a 24-digit base10 number can be represented with just 16 digits in base34.}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "user_friendly_id"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = UserFriendlyId::VERSION
|
17
|
+
|
18
|
+
gem.add_development_dependency 'bundler', '~> 1.3'
|
19
|
+
gem.add_development_dependency 'rake'
|
20
|
+
gem.add_development_dependency 'rspec'
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: user_friendly_id
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alex Agranov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-11-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: Make long integer ids pretty, short and less confusing by shortening
|
56
|
+
via conversion to base34
|
57
|
+
email:
|
58
|
+
- alex@morphogenic.net
|
59
|
+
executables: []
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- ".gitignore"
|
64
|
+
- Gemfile
|
65
|
+
- LICENSE
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- lib/user_friendly_id.rb
|
69
|
+
- lib/user_friendly_id/digits.rb
|
70
|
+
- lib/user_friendly_id/integer.rb
|
71
|
+
- lib/user_friendly_id/string.rb
|
72
|
+
- lib/user_friendly_id/version.rb
|
73
|
+
- spec/spec_helper.rb
|
74
|
+
- spec/user_friendly_id_spec.rb
|
75
|
+
- user_friendly_id.gemspec
|
76
|
+
homepage: ''
|
77
|
+
licenses: []
|
78
|
+
metadata: {}
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options: []
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
93
|
+
requirements: []
|
94
|
+
rubyforge_project:
|
95
|
+
rubygems_version: 2.4.2
|
96
|
+
signing_key:
|
97
|
+
specification_version: 4
|
98
|
+
summary: By leveraging base34 - [0-9, A-Z minus I & O] - we can significantly reduce
|
99
|
+
the number of characters used to represent a number. For instance, a 24-digit base10
|
100
|
+
number can be represented with just 16 digits in base34.
|
101
|
+
test_files:
|
102
|
+
- spec/spec_helper.rb
|
103
|
+
- spec/user_friendly_id_spec.rb
|