prettyid 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5f1d1eaec32642fac2fb8040eab5029eba0e11dac498e661ee1f220c8628f32e
4
+ data.tar.gz: 56399f1ad4530619fbda6e71cced771336c351ddb4704a9062c127ec03c87813
5
+ SHA512:
6
+ metadata.gz: 7d4bb5c9a4676b3454d9e30d93d46c9d9c90a298ba0e1197896504b91150c454c37dc1711ace222135e847005de2a7822b1d38f479814c5acd74bf475cc82b8c
7
+ data.tar.gz: 6a1930e2c2cec62139a846f0c6456c90d13f3a76e433275e7a2e39742af991fec9a5d442cd48777b2c2d9b624bb10389c95a29446006543555a7aa43d67b86b5
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.3
5
+ before_install: gem install bundler -v 1.16.1
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in pretty_id.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,58 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ pretty_id (0.1.0)
5
+ activerecord (>= 4.2.0)
6
+ activesupport (>= 4.2.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activemodel (5.2.0)
12
+ activesupport (= 5.2.0)
13
+ activerecord (5.2.0)
14
+ activemodel (= 5.2.0)
15
+ activesupport (= 5.2.0)
16
+ arel (>= 9.0)
17
+ activesupport (5.2.0)
18
+ concurrent-ruby (~> 1.0, >= 1.0.2)
19
+ i18n (>= 0.7, < 2)
20
+ minitest (~> 5.1)
21
+ tzinfo (~> 1.1)
22
+ arel (9.0.0)
23
+ concurrent-ruby (1.0.5)
24
+ diff-lcs (1.3)
25
+ i18n (1.0.1)
26
+ concurrent-ruby (~> 1.0)
27
+ minitest (5.11.3)
28
+ rake (12.3.1)
29
+ rspec (3.7.0)
30
+ rspec-core (~> 3.7.0)
31
+ rspec-expectations (~> 3.7.0)
32
+ rspec-mocks (~> 3.7.0)
33
+ rspec-core (3.7.1)
34
+ rspec-support (~> 3.7.0)
35
+ rspec-expectations (3.7.0)
36
+ diff-lcs (>= 1.2.0, < 2.0)
37
+ rspec-support (~> 3.7.0)
38
+ rspec-mocks (3.7.0)
39
+ diff-lcs (>= 1.2.0, < 2.0)
40
+ rspec-support (~> 3.7.0)
41
+ rspec-support (3.7.1)
42
+ sqlite3 (1.3.13)
43
+ thread_safe (0.3.6)
44
+ tzinfo (1.2.5)
45
+ thread_safe (~> 0.1)
46
+
47
+ PLATFORMS
48
+ ruby
49
+
50
+ DEPENDENCIES
51
+ bundler
52
+ pretty_id!
53
+ rake
54
+ rspec
55
+ sqlite3
56
+
57
+ BUNDLED WITH
58
+ 1.16.1
data/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # PrettyId
2
+
3
+ How does Stripe generate object ids?
4
+
5
+ **[Patrick Collison](https://twitter.com/patrickc)**, Stripe CEO:
6
+ > They're randomly generated by our Ruby application code. We use the ```ch_```-style prefixes because we find it really useful to be able to immediately recognize the type of an ID when looking at logs or stacktraces.
7
+
8
+ [source](https://www.quora.com/How-does-Stripe-generate-object-ids)
9
+
10
+ With PrettyId you can generate Stripe-like IDs for your ActiveRecord models.
11
+
12
+ ## Usage
13
+
14
+ For each model when you create table inside migration, you have to:
15
+
16
+ 1. tell AR to not automatically add primary key column.
17
+ 1. create ```id``` column as string
18
+ 2. add unique index for ```id``` column
19
+
20
+ ```
21
+ create_table :users, id: false do |t|
22
+ t.string :id, null: false
23
+
24
+ # your other columns
25
+ end
26
+
27
+ add_index(:users, :id, unique: true)
28
+ ```
29
+
30
+ Inside model class just include PrettyId module:
31
+
32
+ ```
33
+ class User < ActiveRecord::Base
34
+ include PrettyId
35
+ end
36
+ ```
37
+
38
+ By default first 3 letters of model class will be taken as id prefix. You can change it by specifying your own value:
39
+
40
+ ```
41
+ class User < ActiveRecord::Base
42
+ include PrettyId
43
+
44
+ self.id_prefix = 'usr'
45
+ end
46
+ ```
47
+
48
+ Or, if you want to add some tricky logic for id prefix (let's say for test account it should be ```acc_test``` and for live account it should be ```acc_live```) you can provide a Proc that will return id prefix:
49
+
50
+ ```
51
+ class Account < ActiveRecord::Base
52
+ include PrettyId
53
+
54
+ attr_accessor :type
55
+
56
+ self.id_prefix = -> (model) { model.type == 'test' ? 'acc_test' : 'acc_live' }
57
+ end
58
+
59
+ account = Account.new
60
+ account.type = 'test'
61
+ account.save
62
+
63
+ account.id #=> acc_test_....
64
+ ```
65
+
66
+ ## Installation
67
+
68
+ Add this line to your application's Gemfile:
69
+
70
+ ```ruby
71
+ gem 'prettyid', require: 'pretty_id'
72
+ ```
73
+
74
+ And then execute:
75
+
76
+ $ bundle
77
+
78
+ Or install it yourself as:
79
+
80
+ $ gem install prettyid
81
+
82
+
83
+
84
+ ## Perfomance
85
+
86
+ If you care about performance, it's better to measure it yourself :) I have measured method that generates id (```rake bm```). Here are results for 1M calls:
87
+
88
+ user system total real
89
+ PrettyId::Generator#id: 12.880000 0.020000 12.900000 ( 12.907587)
90
+
91
+
92
+ ## Contributing
93
+
94
+ Bug reports and pull requests are welcome on GitHub at https://github.com/alovak/pretty_id.
95
+
96
+ ## License
97
+
98
+ This code is available under [MIT license](/LICENSE).
99
+
100
+ &copy; Pavel Gabriel, 2018
data/Rakefile ADDED
@@ -0,0 +1,24 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require 'benchmark'
4
+ require 'pretty_id'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task :default => :spec
9
+
10
+ task :bm do
11
+ n = 1_000_000
12
+
13
+ class One
14
+ def self.id_prefix
15
+ 'one'
16
+ end
17
+ end
18
+
19
+ generator = PrettyId::Generator.new(One.new)
20
+
21
+ Benchmark.bm(20) do |x|
22
+ x.report("PrettyId::Generator#id:") { n.times { generator.id } }
23
+ end
24
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "pretty_id"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,25 @@
1
+ module PrettyId
2
+ class Generator
3
+ attr_reader :record
4
+
5
+ def initialize(record)
6
+ @record = record
7
+ end
8
+
9
+ def id
10
+ "#{prefix}_#{SecRandom.alphanumeric(length)}"
11
+ end
12
+
13
+ def length
14
+ 12
15
+ end
16
+
17
+ def prefix
18
+ if record.class.id_prefix.is_a? Proc
19
+ record.class.id_prefix.call(record)
20
+ else
21
+ record.class.id_prefix
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,42 @@
1
+ require 'securerandom'
2
+
3
+ module PrettyId
4
+ # taken from Ruby 2.5 implementation
5
+ class SecRandom
6
+ ALPHANUMERIC = [*'A'..'Z', *'a'..'z', *'0'..'9']
7
+
8
+ def self.alphanumeric(n=nil)
9
+ n = 16 if n.nil?
10
+ choose(ALPHANUMERIC, n)
11
+ end
12
+
13
+ def self.choose(source, n)
14
+ size = source.size
15
+ m = 1
16
+ limit = size
17
+ while limit * size <= 0x100000000
18
+ limit *= size
19
+ m += 1
20
+ end
21
+ result = ''.dup
22
+ while m <= n
23
+ rs = SecureRandom.random_number(limit)
24
+ is = rs.digits(size)
25
+ (m-is.length).times { is << 0 }
26
+ result << source.values_at(*is).join('')
27
+ n -= m
28
+ end
29
+ if 0 < n
30
+ rs = SecureRandom.random_number(limit)
31
+ is = rs.digits(size)
32
+ if is.length < n
33
+ (n-is.length).times { is << 0 }
34
+ else
35
+ is.pop while n < is.length
36
+ end
37
+ result.concat source.values_at(*is).join('')
38
+ end
39
+ result
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,3 @@
1
+ module PrettyId
2
+ VERSION = "0.1.0"
3
+ end
data/lib/pretty_id.rb ADDED
@@ -0,0 +1,35 @@
1
+ require "pretty_id/version"
2
+ require "pretty_id/sec_random"
3
+ require "pretty_id/generator"
4
+ require 'active_support/concern'
5
+
6
+ module PrettyId
7
+ extend ActiveSupport::Concern
8
+
9
+ class_methods do
10
+ def id_prefix=(prefix)
11
+ @id_prefix = prefix
12
+ end
13
+
14
+ def id_prefix
15
+ @id_prefix || name.downcase[0, 3]
16
+ end
17
+ end
18
+
19
+ def _create_record(*)
20
+ attempt ||= 1
21
+ set_pretty_id
22
+ super
23
+ rescue ActiveRecord::RecordNotUnique => e
24
+ attempt += 1
25
+ retry if attempt < 4
26
+
27
+ raise
28
+ end
29
+
30
+ private
31
+
32
+ def set_pretty_id
33
+ self.id = PrettyId::Generator.new(self).id
34
+ end
35
+ end
data/pretty_id.gemspec ADDED
@@ -0,0 +1,30 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "pretty_id/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "prettyid"
8
+ spec.version = PrettyId::VERSION
9
+ spec.authors = ["Pavel Gabriel"]
10
+ spec.email = ["alovak@gmail.com"]
11
+ spec.licenses = ['MIT']
12
+
13
+ spec.summary = %q{Generate Stripe-like ids for your ActiveRecord models}
14
+ spec.description = %q{Generate Stripe-like ids for your ActiveRecord models}
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec"
26
+ spec.add_development_dependency "sqlite3"
27
+
28
+ spec.add_dependency "activerecord", ">= 4.2.0"
29
+ spec.add_dependency "activesupport", ">= 4.2.0"
30
+ end
metadata ADDED
@@ -0,0 +1,142 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: prettyid
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Pavel Gabriel
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-05-27 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: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
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
+ - !ruby/object:Gem::Dependency
56
+ name: sqlite3
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: activerecord
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 4.2.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 4.2.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: activesupport
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: 4.2.0
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: 4.2.0
97
+ description: Generate Stripe-like ids for your ActiveRecord models
98
+ email:
99
+ - alovak@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - ".rspec"
106
+ - ".travis.yml"
107
+ - Gemfile
108
+ - Gemfile.lock
109
+ - README.md
110
+ - Rakefile
111
+ - bin/console
112
+ - bin/setup
113
+ - lib/pretty_id.rb
114
+ - lib/pretty_id/generator.rb
115
+ - lib/pretty_id/sec_random.rb
116
+ - lib/pretty_id/version.rb
117
+ - pretty_id.gemspec
118
+ homepage:
119
+ licenses:
120
+ - MIT
121
+ metadata: {}
122
+ post_install_message:
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubyforge_project:
138
+ rubygems_version: 2.7.5
139
+ signing_key:
140
+ specification_version: 4
141
+ summary: Generate Stripe-like ids for your ActiveRecord models
142
+ test_files: []