pg_random_id 0.0.1 → 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.
data/README.md CHANGED
@@ -2,9 +2,19 @@
2
2
 
3
3
  Allow usage of pseudo-random IDs in Postgresql databases.
4
4
  Changes sequential surrogate ids (1, 2, 3...) into a pseudo-random
5
- sequence of unique 30-bits nonnegative integer values (eg. 760280231, 110168588, 1029278017...).
5
+ sequence of unique 30-bits nonnegative integer values (eg. 760280231, 110168588, 1029278017...)
6
+ or 6-character human-friendly-ish strings (eg. kn5xx1, qy2kp8, e5f67z...).
6
7
 
7
- Integrates with ActiveRecord. Sequel integration is upcoming.
8
+ Since surrogate IDs are often used in REST-ful URLs, this makes the addresses less revealing and harder to guess
9
+ (while preserving the straightforward mapping from URLs to database IDs):
10
+ - http://example.com/products/1 → http://example.com/products/134178313
11
+ - http://example.com/products/2 → http://example.com/products/121521131
12
+ - http://example.com/widgets/1 → http://example.com/widgets/2agc30
13
+ - http://example.com/widgets/2 → http://example.com/widgets/4zkabg
14
+
15
+
16
+ Although the code is 100% database-side, it has been packaged into Ruby functions plugging
17
+ into ActiveRecord and Sequel migrations for ease of use.
8
18
 
9
19
  ## Installation
10
20
 
@@ -20,49 +30,103 @@ Or install it yourself as:
20
30
 
21
31
  $ gem install pg_random_id
22
32
 
23
- ## Usage
33
+ ## Synopsis
24
34
 
25
- The easiest way to use it is to use the supplied migration functions.
35
+ ### ActiveRecord
26
36
 
27
- First, make sure you put
37
+ ```ruby
38
+ class InstallRandomId < ActiveRecord::Migration
39
+ def up
40
+ # install the necessary SQL functions in the DB
28
41
  create_random_id_functions
29
- in a migration (a single one will do).
42
+ end
43
+ end
30
44
 
31
- Then to apply random ids to a table, use random_id function:
45
+ class CreateProducts < ActiveRecord::Migration
46
+ def up
47
+ create_table :products do |t|
48
+ t.string :name
49
+ end
50
+
51
+ # make the table use random ids
52
+ random_id :products
53
+ end
54
+ end
32
55
 
33
- ```ruby
34
- class RandomizeIdsOnFoo < ActiveRecord::Migration
56
+ class RandomizeIdsOnWidgets < ActiveRecord::Migration
35
57
  def up
36
- KEY = 21315
37
- random_id :foo, :foo_id, KEY
58
+ # make ids on a previously created table
59
+ # 'widgets' random (using string ids)
60
+ random_str_id :widgets, :widget_id # you can specify id column name
38
61
  end
39
62
  end
40
63
  ```
41
64
 
42
- If you don't supply the key, a random one will be generated;
43
- similarly, the id column name defaults to :id.
44
- This means that you can create a vanilla AR table with random ids
45
- with the following simple migration:
65
+ ### Sequel
46
66
 
47
67
  ```ruby
48
- class CreateProducts < ActiveRecord::Migration
49
- def up
50
- create_table :products do |t|
51
- t.string :name
68
+ Sequel.migration do
69
+ up do
70
+ # install the necessary SQL functions in the DB
71
+ create_random_id_functions
72
+ end
73
+ end
74
+
75
+ Sequel.migration do
76
+ up do
77
+ create_table :products do
78
+ primary_key :id
79
+ String :name
52
80
  end
81
+
82
+ # make the table use random ids
53
83
  random_id :products
54
84
  end
55
85
  end
86
+
87
+ Sequel.migration do
88
+ up do
89
+ # make ids on a previously created table
90
+ # 'widgets' random (using string ids)
91
+ random_str_id :widgets, :widget_id # you can specify id column name
92
+ end
93
+ end
56
94
  ```
57
95
 
96
+ ## Considerations
97
+
58
98
  No model modification is necessary, just use the table as usual and it will simply work.
59
- You can even use it without ActiveRecord.
99
+ Each table will use its own unique sequence, chosen at random at migration time.
100
+
101
+ If you use `random_str_id` make sure to use a string type in
102
+ foreign key columns:
103
+ ```ruby
104
+ class CreateContraptions < ActiveRecord::Migration
105
+ def up
106
+ create_table :contraptions do |t|
107
+ t.string :widget_id, limit: 6
108
+ end
109
+ end
110
+ end
111
+ ```
112
+ ```ruby
113
+ Sequel.migration do
114
+ up do
115
+ create_table :contraptions do
116
+ String :widget_id, size: 6
117
+ end
118
+ end
119
+ end
120
+ ```
121
+
122
+ ## Notes
60
123
 
61
- ### Text ids
124
+ The `random_id` function changes the default value of the ID column to a scrambled next sequence value.
125
+ The scrambling function is a simple Feistel network, with a variable parameter which is used to choose the sequence.
62
126
 
63
- If you use random_str_id function instead, it will additionally
64
- change the column type to character(6) and store the ids as base32-encoded
65
- strings of handy human-friendly form (eg. kn5xx1, qy2kp8, e5f67z...).
127
+ With `random_str_id` function, the column type is changed to character(6)
128
+ and the sequence is additionally base32-encoded
129
+ with [Crockford encoding](http://www.crockford.com/wrmg/base32.html).
66
130
 
67
131
  ## Contributing
68
132
 
@@ -1,9 +1,6 @@
1
1
  require "pg_random_id/sql"
2
2
  require "pg_random_id/version"
3
-
4
- if defined? ActiveRecord
5
- require "pg_random_id/migrations/active_record"
6
- end
3
+ require "pg_random_id/migrations"
7
4
 
8
5
  module PgRandomId
9
6
  end
@@ -0,0 +1,43 @@
1
+ require 'pg_random_id/sql'
2
+
3
+ module PgRandomId
4
+ module Migrations
5
+ # Create in the database the functions
6
+ # necessary for this gem to work.
7
+ def create_random_id_functions
8
+ execute PgRandomId::Sql::install
9
+ end
10
+
11
+ # Apply a random id to a table.
12
+ # If you don't give a key, a random one will be generated.
13
+ # The ids will be based on sequence "#{table}_#{column}_seq".
14
+ # You need to make sure the table is empty; migrating existing records is not implemented.
15
+ def random_id table, column = :id, key = nil
16
+ execute PgRandomId::Sql::apply(table, column, key)
17
+ end
18
+
19
+ # Apply a random string id to a table.
20
+ # Also changes the type of the id column to char(6).
21
+ # If you don't give a key, a random one will be generated.
22
+ # The ids will be based on sequence "#{table}_#{column}_seq",
23
+ # scrambled and base32-encoded.
24
+ # You need to make sure the table is empty; migrating existing records is not implemented.
25
+ def random_str_id table, column = :id, key = nil
26
+ execute PgRandomId::Sql::apply_str(table, column, key)
27
+ end
28
+
29
+ # Install the migration functions for ActiveRecord
30
+ def self.install_active_record
31
+ require 'active_record/migration'
32
+ ActiveRecord::Migration.send :include, self
33
+ end
34
+
35
+ # Install the migration functions for Sequel
36
+ def self.install_sequel
37
+ Sequel::Database.send :include, self
38
+ end
39
+ end
40
+ end
41
+
42
+ PgRandomId::Migrations::install_active_record if defined? ActiveRecord
43
+ PgRandomId::Migrations::install_sequel if defined? Sequel
@@ -1,3 +1,3 @@
1
1
  module PgRandomId
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -8,7 +8,7 @@ Gem::Specification.new do |gem|
8
8
  gem.version = PgRandomId::VERSION
9
9
  gem.authors = ["Rafał Rzepecki"]
10
10
  gem.email = ["divided.mind@gmail.com"]
11
- gem.description = %q{Easily use randomized integers instead of sequential values for your record surrogate ids.}
11
+ gem.description = %q{Easily use randomized keys instead of sequential values for your record surrogate ids.}
12
12
  gem.summary = %q{Pseudo-random record ids in Postgres}
13
13
  gem.homepage = "https://github.com/dividedmind/pg_random_id"
14
14
 
@@ -17,7 +17,7 @@ Gem::Specification.new do |gem|
17
17
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
18
  gem.require_paths = ["lib"]
19
19
 
20
- %w(activerecord rspec).each do |g|
20
+ %w(activerecord rspec sequel).each do |g|
21
21
  gem.add_development_dependency g
22
22
  end
23
23
  end
@@ -1,9 +1,15 @@
1
1
  shared_context 'active_record' do
2
+ require 'active_record'
3
+
2
4
  before do
3
5
  dburl = ENV['TEST_DATABASE_URL'] || 'postgres:///pg_random_id_test'
4
6
  ActiveRecord::Base.establish_connection dburl
5
7
  ActiveRecord::Base.connection.execute 'BEGIN;'
6
8
  end
9
+
10
+ before do
11
+ ActiveRecord::Migration.verbose = false
12
+ end
7
13
 
8
14
  after do
9
15
  ActiveRecord::Base.connection.execute 'ROLLBACK;'
@@ -16,4 +22,8 @@ shared_context 'active_record' do
16
22
  def execute code
17
23
  ActiveRecord::Base.connection.select_one(code)
18
24
  end
25
+
26
+ def create_table *a
27
+ migration.create_table *a
28
+ end
19
29
  end
@@ -1,12 +1,6 @@
1
- require 'spec_helper'
2
-
3
- require 'active_record'
4
- require 'pg_random_id/migrations/active_record'
5
-
6
- describe PgRandomId::Migrations::ActiveRecord do
7
- include_context 'active_record'
1
+ shared_context 'test_migration' do
8
2
  describe '#create_random_id_functions' do
9
- it "installs the pri_scramble function" do
3
+ it "installs the functions" do
10
4
  migration.create_random_id_functions
11
5
 
12
6
  execute("SELECT 1 FROM pg_proc WHERE proname = 'crockford'").should be
@@ -17,37 +11,37 @@ describe PgRandomId::Migrations::ActiveRecord do
17
11
  describe '#random_id' do
18
12
  it "changes the default value" do
19
13
  migration.create_random_id_functions
20
- migration.create_table :foo
14
+ create_table :foo
21
15
  migration.random_id :foo
22
16
  execute("SELECT 1 FROM pg_attrdef WHERE adrelid = 'foo'::regclass AND adsrc LIKE '%pri_scramble%'").should be
23
17
  end
24
18
 
25
19
  it "creates a few values without error" do
26
20
  migration.create_random_id_functions
27
- migration.create_table :foo
21
+ create_table :foo
28
22
  migration.random_id :foo
29
23
  10.times do
30
24
  expect {
31
- ActiveRecord::Base.connection.execute('INSERT INTO foo VALUES(default)')
25
+ execute('INSERT INTO foo VALUES(default)')
32
26
  }.to_not raise_error
33
27
  end
34
- execute("SELECT COUNT(*) FROM foo")['count'].to_i.should == 10
28
+ execute("SELECT COUNT(*) FROM foo").first[1].to_i.should == 10
35
29
  end
36
30
  end
37
31
 
38
32
  describe '#random_str_id' do
39
33
  it "changes the default value" do
40
34
  migration.create_random_id_functions
41
- migration.create_table :foo
35
+ create_table :foo
42
36
  migration.random_str_id :foo
43
37
  execute("SELECT 1 FROM pg_attrdef WHERE adrelid = 'foo'::regclass AND adsrc LIKE '%pri_scramble%'").should be
44
38
  execute("SELECT 1 FROM pg_attrdef WHERE adrelid = 'foo'::regclass AND adsrc LIKE '%crockford%'").should be
45
- execute("INSERT INTO foo VALUES (DEFAULT) RETURNING id;")['id'].should_not == '1'
39
+ execute("INSERT INTO foo VALUES (DEFAULT) RETURNING id;").first[1].should_not == '1'
46
40
  end
47
41
 
48
42
  it "changes the type" do
49
43
  migration.create_random_id_functions
50
- migration.create_table :foo
44
+ create_table :foo
51
45
  migration.random_str_id :foo
52
46
  execute("""
53
47
  SELECT typname FROM pg_attribute, pg_type
@@ -60,14 +54,14 @@ describe PgRandomId::Migrations::ActiveRecord do
60
54
 
61
55
  it "creates a few values without error" do
62
56
  migration.create_random_id_functions
63
- migration.create_table :foo
57
+ create_table :foo
64
58
  migration.random_str_id :foo
65
59
  10.times do
66
60
  expect {
67
- ActiveRecord::Base.connection.execute('INSERT INTO foo VALUES(default)')
61
+ execute('INSERT INTO foo VALUES(default)')
68
62
  }.to_not raise_error
69
63
  end
70
- execute("SELECT COUNT(*) FROM foo")['count'].to_i.should == 10
64
+ execute("SELECT COUNT(*) FROM foo").first[1].to_i.should == 10
71
65
  end
72
66
  end
73
67
  end
@@ -0,0 +1,24 @@
1
+ shared_context 'sequel' do
2
+ require 'sequel'
3
+
4
+ let(:dburl) { ENV['TEST_DATABASE_URL'] || 'postgres:///pg_random_id_test' }
5
+ let(:db) { Sequel::connect dburl }
6
+
7
+ around do |example|
8
+ db.transaction rollback: :always do
9
+ example.run
10
+ end
11
+ end
12
+
13
+ let(:migration) { db }
14
+
15
+ def execute code
16
+ db[code].first
17
+ end
18
+
19
+ def create_table *a
20
+ db.create_table *a do
21
+ primary_key :id
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe PgRandomId::Migrations do
4
+ context "with ActiveRecord" do
5
+ include_context 'active_record'
6
+ PgRandomId::Migrations.install_active_record
7
+ include_context 'test_migration'
8
+ end
9
+
10
+ context "with Sequel" do
11
+ include_context 'sequel'
12
+ PgRandomId::Migrations.install_sequel
13
+ include_context 'test_migration'
14
+ end
15
+ end
@@ -1 +1,5 @@
1
+ require 'pg_random_id'
2
+
1
3
  require 'helpers/active_record_helper'
4
+ require 'helpers/migration_spec_helper'
5
+ require 'helpers/sequel_helper'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_random_id
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-05 00:00:00.000000000 Z
12
+ date: 2013-02-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -43,8 +43,24 @@ dependencies:
43
43
  - - ! '>='
44
44
  - !ruby/object:Gem::Version
45
45
  version: '0'
46
- description: Easily use randomized integers instead of sequential values for your
47
- record surrogate ids.
46
+ - !ruby/object:Gem::Dependency
47
+ name: sequel
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Easily use randomized keys instead of sequential values for your record
63
+ surrogate ids.
48
64
  email:
49
65
  - divided.mind@gmail.com
50
66
  executables: []
@@ -57,7 +73,7 @@ files:
57
73
  - README.md
58
74
  - Rakefile
59
75
  - lib/pg_random_id.rb
60
- - lib/pg_random_id/migrations/active_record.rb
76
+ - lib/pg_random_id/migrations.rb
61
77
  - lib/pg_random_id/sql.rb
62
78
  - lib/pg_random_id/sql/crockford-pure.sql
63
79
  - lib/pg_random_id/sql/crockford.sql
@@ -65,7 +81,9 @@ files:
65
81
  - lib/pg_random_id/version.rb
66
82
  - pg_random_id.gemspec
67
83
  - spec/helpers/active_record_helper.rb
68
- - spec/migrations/active_record_spec.rb
84
+ - spec/helpers/migration_spec_helper.rb
85
+ - spec/helpers/sequel_helper.rb
86
+ - spec/migrations_spec.rb
69
87
  - spec/spec_helper.rb
70
88
  - spec/sql/crockford_spec.rb
71
89
  - spec/sql/scramble_spec.rb
@@ -95,7 +113,9 @@ specification_version: 3
95
113
  summary: Pseudo-random record ids in Postgres
96
114
  test_files:
97
115
  - spec/helpers/active_record_helper.rb
98
- - spec/migrations/active_record_spec.rb
116
+ - spec/helpers/migration_spec_helper.rb
117
+ - spec/helpers/sequel_helper.rb
118
+ - spec/migrations_spec.rb
99
119
  - spec/spec_helper.rb
100
120
  - spec/sql/crockford_spec.rb
101
121
  - spec/sql/scramble_spec.rb
@@ -1,35 +0,0 @@
1
- require 'active_record'
2
- require 'active_record/migration'
3
- require 'pg_random_id/sql'
4
-
5
- module PgRandomId
6
- module Migrations
7
- module ActiveRecord
8
- # Create in the database the function (pri_scramble(bigint, bigint))
9
- # necessary for this gem to work.
10
- def create_random_id_functions
11
- execute PgRandomId::Sql::install
12
- end
13
-
14
- # Apply a random id to a table.
15
- # If you don't give a key, a random one will be generated.
16
- # The ids will be based on sequence "#{table}_#{column}_seq".
17
- # You need to make sure the table is empty; migrating existing records is not implemented.
18
- def random_id table, column = :id, key = nil
19
- execute PgRandomId::Sql::apply(table, column, key)
20
- end
21
-
22
- # Apply a random string id to a table.
23
- # Also changes the type of the id column to char(6).
24
- # If you don't give a key, a random one will be generated.
25
- # The ids will be based on sequence "#{table}_#{column}_seq",
26
- # scrambled and base32-encoded.
27
- # You need to make sure the table is empty; migrating existing records is not implemented.
28
- def random_str_id table, column = :id, key = nil
29
- execute PgRandomId::Sql::apply_str(table, column, key)
30
- end
31
- end
32
- end
33
- end
34
-
35
- ActiveRecord::Migration.send :include, PgRandomId::Migrations::ActiveRecord