pg_random_id 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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