pgcrypto 0.3.6 → 0.4.1
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 +4 -4
- data/CHANGES.md +5 -1
- data/Gemfile +1 -4
- data/README.markdown +126 -33
- data/VERSION +1 -1
- data/lib/active_record/connection_adapters/pgcrypto_adapter.rb +4 -0
- data/lib/active_record/connection_adapters/pgcrypto_adapter/rails_3.rb +20 -0
- data/lib/active_record/connection_adapters/pgcrypto_adapter/rails_4.rb +22 -0
- data/lib/pgcrypto.rb +15 -105
- data/lib/pgcrypto/adapter.rb +162 -0
- data/lib/pgcrypto/column.rb +9 -8
- data/lib/pgcrypto/column_converter.rb +26 -0
- data/lib/pgcrypto/generators/base_generator.rb +12 -0
- data/lib/{generators/pgcrypto → pgcrypto/generators}/install/USAGE +0 -0
- data/lib/{generators/pgcrypto → pgcrypto/generators}/install/install_generator.rb +2 -5
- data/lib/{generators/pgcrypto → pgcrypto/generators}/install/templates/initializer.rb +0 -0
- data/lib/pgcrypto/generators/install/templates/migration.rb +5 -0
- data/lib/pgcrypto/generators/upgrade/USAGE +8 -0
- data/lib/pgcrypto/generators/upgrade/templates/migration.rb +22 -0
- data/lib/pgcrypto/generators/upgrade/upgrade_generator.rb +15 -0
- data/lib/pgcrypto/has_encrypted_column.rb +25 -0
- data/lib/pgcrypto/key.rb +0 -10
- data/lib/pgcrypto/key_manager.rb +11 -0
- data/lib/pgcrypto/railtie.rb +15 -0
- data/lib/pgcrypto/table.rb +11 -0
- data/lib/pgcrypto/table_manager.rb +2 -10
- data/lib/tasks/pgcrypto.rake +7 -0
- data/pgcrypto.gemspec +24 -17
- data/spec/lib/pgcrypto_spec.rb +101 -100
- data/spec/spec_helper.rb +27 -28
- metadata +20 -36
- data/lib/generators/pgcrypto/install/templates/migration.rb +0 -17
- data/lib/pgcrypto/active_record.rb +0 -88
- data/lib/pgcrypto/arel.rb +0 -24
data/lib/pgcrypto/column.rb
CHANGED
@@ -1,18 +1,19 @@
|
|
1
1
|
module PGCrypto
|
2
2
|
class Column < ActiveRecord::Base
|
3
3
|
|
4
|
-
attr_accessible :name if respond_to? :attr_accessible
|
5
|
-
|
6
4
|
self.table_name = 'pgcrypto_columns'
|
7
5
|
|
8
|
-
|
9
|
-
belongs_to :owner, :autosave => false, :inverse_of => :pgcrypto_columns, :polymorphic => true
|
6
|
+
belongs_to :owner, polymorphic: true
|
10
7
|
|
11
|
-
|
8
|
+
has_encrypted_column :value
|
12
9
|
|
13
|
-
|
14
|
-
|
15
|
-
|
10
|
+
def self.tables_and_columns
|
11
|
+
tables_and_columns = []
|
12
|
+
select('DISTINCT owner_type, name').each do |column|
|
13
|
+
tables_and_columns.push [column.owner_type.constantize.table_name, column.name]
|
14
|
+
end
|
15
|
+
tables_and_columns
|
16
16
|
end
|
17
|
+
|
17
18
|
end
|
18
19
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'pgcrypto/column'
|
2
|
+
|
3
|
+
module PGCrypto
|
4
|
+
class ColumnConverter
|
5
|
+
|
6
|
+
def self.migrate!
|
7
|
+
new.migrate!
|
8
|
+
end
|
9
|
+
|
10
|
+
def migrate!
|
11
|
+
PGCrypto::Column.find_each(batch_size: 100) do |column|
|
12
|
+
migrate_column(column)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def migrate_column(column)
|
19
|
+
if column.owner
|
20
|
+
column.owner.update_column(column.name, column.value)
|
21
|
+
puts "Migrated #{column.owner}##{column.name}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rails/generators/migration'
|
2
|
+
require 'rails/generators/active_record/migration'
|
3
|
+
|
4
|
+
class BaseGenerator < Rails::Generators::Base
|
5
|
+
include Rails::Generators::Migration
|
6
|
+
extend ActiveRecord::Generators::Migration
|
7
|
+
|
8
|
+
def self.next_migration_number(*args)
|
9
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S").to_i.to_s
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
File without changes
|
@@ -1,11 +1,8 @@
|
|
1
|
-
require '
|
2
|
-
require 'rails/generators/active_record/migration'
|
1
|
+
require 'pgcrypto/generators/base_generator'
|
3
2
|
|
4
3
|
module Pgcrypto
|
5
4
|
module Generators
|
6
|
-
class InstallGenerator <
|
7
|
-
include Rails::Generators::Migration
|
8
|
-
extend ActiveRecord::Generators::Migration
|
5
|
+
class InstallGenerator < BaseGenerator
|
9
6
|
|
10
7
|
source_root File.expand_path('../templates', __FILE__)
|
11
8
|
|
File without changes
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'pgcrypto/column'
|
2
|
+
require 'pgcrypto/column_converter'
|
3
|
+
|
4
|
+
class UpgradePgcryptoTo040 < ActiveRecord::Migration
|
5
|
+
def up
|
6
|
+
# Add columns based on the ones we already know exist
|
7
|
+
PGCrypto::Column.tables_and_columns do |table, column|
|
8
|
+
add_column table, column, :binary
|
9
|
+
end
|
10
|
+
|
11
|
+
# Migrate column data
|
12
|
+
PGCrypto::ColumnConverter.migrate!
|
13
|
+
|
14
|
+
# Drop the old, now-unused columns table
|
15
|
+
# COMMENT THIS IN IF YOU REALLY WANT IT
|
16
|
+
# drop_table :pgcrypto_columns
|
17
|
+
end
|
18
|
+
|
19
|
+
def down
|
20
|
+
raise ActiveRecord::IrreversibleMigration
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'pgcrypto/generators/base_generator'
|
2
|
+
|
3
|
+
module Pgcrypto
|
4
|
+
module Generators
|
5
|
+
class UpgradeGenerator < BaseGenerator
|
6
|
+
|
7
|
+
source_root File.expand_path('../templates', __FILE__)
|
8
|
+
|
9
|
+
def copy_migration
|
10
|
+
migration_template("migration.rb", "db/migrate/upgrade_pgcrypto_to_0_4_0.rb")
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module PGCrypto
|
2
|
+
module HasEncryptedColumn
|
3
|
+
def has_encrypted_column(*column_names)
|
4
|
+
options = column_names.extract_options!
|
5
|
+
options.reverse_merge(type: :pgp)
|
6
|
+
|
7
|
+
column_names.each do |column_name|
|
8
|
+
# Stash the encryption type in our module
|
9
|
+
PGCrypto[table_name][column_name.to_s] ||= options.symbolize_keys
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def pgcrypto(*args)
|
14
|
+
if defined? Rails
|
15
|
+
Rails.logger.debug "[DEPRECATION WARNING] `pgcrypto' is deprecated. Please use `has_encrypted_column' instead!"
|
16
|
+
end
|
17
|
+
has_encrypted_column(*args)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
if defined? ActiveRecord::Base
|
24
|
+
ActiveRecord::Base.extend PGCrypto::HasEncryptedColumn
|
25
|
+
end
|
data/lib/pgcrypto/key.rb
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
module PGCrypto
|
2
|
+
class Railtie < Rails::Railtie
|
3
|
+
generators do
|
4
|
+
require 'pgcrypto/generators/install/install_generator'
|
5
|
+
require 'pgcrypto/generators/upgrade/upgrade_generator'
|
6
|
+
end
|
7
|
+
|
8
|
+
rake_tasks do
|
9
|
+
tasks = File.join(File.dirname(__FILE__), '../tasks/*.rake')
|
10
|
+
Dir[tasks].each do |file|
|
11
|
+
load file
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,14 +1,6 @@
|
|
1
|
-
|
2
|
-
class Table < Hash
|
3
|
-
def [](key)
|
4
|
-
super(key.to_sym)
|
5
|
-
end
|
6
|
-
|
7
|
-
def []=(key, value)
|
8
|
-
super key.to_sym, value
|
9
|
-
end
|
10
|
-
end
|
1
|
+
require 'pgcrypto/table'
|
11
2
|
|
3
|
+
module PGCrypto
|
12
4
|
class TableManager < Table
|
13
5
|
def [](key)
|
14
6
|
return {} unless key
|
data/pgcrypto.gemspec
CHANGED
@@ -2,14 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
+
# stub: pgcrypto 0.4.0 ruby lib
|
5
6
|
|
6
7
|
Gem::Specification.new do |s|
|
7
8
|
s.name = "pgcrypto"
|
8
|
-
s.version = "0.
|
9
|
+
s.version = "0.4.0"
|
9
10
|
|
10
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
|
+
s.require_paths = ["lib"]
|
11
13
|
s.authors = ["Flip Sasser"]
|
12
|
-
s.date = "
|
14
|
+
s.date = "2014-08-24"
|
13
15
|
s.description = "\n PGCrypto is an ActiveRecord::Base extension that allows you to asymmetrically\n encrypt PostgreSQL columns with as little trouble as possible. It's totally\n freaking rad.\n "
|
14
16
|
s.email = "flip@x451.com"
|
15
17
|
s.extra_rdoc_files = [
|
@@ -26,16 +28,28 @@ Gem::Specification.new do |s|
|
|
26
28
|
"README.markdown",
|
27
29
|
"Rakefile",
|
28
30
|
"VERSION",
|
29
|
-
"lib/
|
30
|
-
"lib/
|
31
|
-
"lib/
|
32
|
-
"lib/generators/pgcrypto/install/templates/migration.rb",
|
31
|
+
"lib/active_record/connection_adapters/pgcrypto_adapter.rb",
|
32
|
+
"lib/active_record/connection_adapters/pgcrypto_adapter/rails_3.rb",
|
33
|
+
"lib/active_record/connection_adapters/pgcrypto_adapter/rails_4.rb",
|
33
34
|
"lib/pgcrypto.rb",
|
34
|
-
"lib/pgcrypto/
|
35
|
-
"lib/pgcrypto/arel.rb",
|
35
|
+
"lib/pgcrypto/adapter.rb",
|
36
36
|
"lib/pgcrypto/column.rb",
|
37
|
+
"lib/pgcrypto/column_converter.rb",
|
38
|
+
"lib/pgcrypto/generators/base_generator.rb",
|
39
|
+
"lib/pgcrypto/generators/install/USAGE",
|
40
|
+
"lib/pgcrypto/generators/install/install_generator.rb",
|
41
|
+
"lib/pgcrypto/generators/install/templates/initializer.rb",
|
42
|
+
"lib/pgcrypto/generators/install/templates/migration.rb",
|
43
|
+
"lib/pgcrypto/generators/upgrade/USAGE",
|
44
|
+
"lib/pgcrypto/generators/upgrade/templates/migration.rb",
|
45
|
+
"lib/pgcrypto/generators/upgrade/upgrade_generator.rb",
|
46
|
+
"lib/pgcrypto/has_encrypted_column.rb",
|
37
47
|
"lib/pgcrypto/key.rb",
|
48
|
+
"lib/pgcrypto/key_manager.rb",
|
49
|
+
"lib/pgcrypto/railtie.rb",
|
50
|
+
"lib/pgcrypto/table.rb",
|
38
51
|
"lib/pgcrypto/table_manager.rb",
|
52
|
+
"lib/tasks/pgcrypto.rake",
|
39
53
|
"pgcrypto.gemspec",
|
40
54
|
"spec/lib/pgcrypto_spec.rb",
|
41
55
|
"spec/spec_helper.rb",
|
@@ -45,25 +59,18 @@ Gem::Specification.new do |s|
|
|
45
59
|
"spec/support/public.password.key"
|
46
60
|
]
|
47
61
|
s.homepage = "http://github.com/Plinq/pgcrypto"
|
48
|
-
s.
|
49
|
-
s.rubygems_version = "1.8.24"
|
62
|
+
s.rubygems_version = "2.4.1"
|
50
63
|
s.summary = "A transparent ActiveRecord::Base extension for encrypted columns"
|
51
64
|
|
52
65
|
if s.respond_to? :specification_version then
|
53
|
-
s.specification_version =
|
66
|
+
s.specification_version = 4
|
54
67
|
|
55
68
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
56
|
-
s.add_runtime_dependency(%q<activerecord>, [">= 3.2"])
|
57
|
-
s.add_runtime_dependency(%q<big_spoon>, [">= 0.2.1"])
|
58
69
|
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
59
70
|
else
|
60
|
-
s.add_dependency(%q<activerecord>, [">= 3.2"])
|
61
|
-
s.add_dependency(%q<big_spoon>, [">= 0.2.1"])
|
62
71
|
s.add_dependency(%q<jeweler>, [">= 0"])
|
63
72
|
end
|
64
73
|
else
|
65
|
-
s.add_dependency(%q<activerecord>, [">= 3.2"])
|
66
|
-
s.add_dependency(%q<big_spoon>, [">= 0.2.1"])
|
67
74
|
s.add_dependency(%q<jeweler>, [">= 0"])
|
68
75
|
end
|
69
76
|
end
|
data/spec/lib/pgcrypto_spec.rb
CHANGED
@@ -4,102 +4,100 @@ require 'spec_helper'
|
|
4
4
|
# ActiveRecord::Base.logger = Logger.new(STDOUT)
|
5
5
|
|
6
6
|
specs = proc do
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
end
|
27
|
-
|
28
|
-
it "
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
|
-
it "
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
model
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
PGCryptoTestModel.
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
model
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
7
|
+
|
8
|
+
let(:stored_raw) {
|
9
|
+
connection = PGCryptoTestModel.connection
|
10
|
+
result = connection.select_one("SELECT encrypted_text FROM pgcrypto_test_models LIMIT 1")
|
11
|
+
result['encrypted_text']
|
12
|
+
}
|
13
|
+
|
14
|
+
# Default test text
|
15
|
+
let(:text) { "text to encrypt" }
|
16
|
+
# That text as it appears un-encrypted in a binary column - we'll compare
|
17
|
+
# this to what gets set to ensure the text is properly encrypted
|
18
|
+
let(:text_raw) { "\\x7465787420746f20656e6372797074" }
|
19
|
+
|
20
|
+
let(:text_2) { "something else entirely" }
|
21
|
+
let(:text_2_raw) { "\\x736f6d657468696e6720656c736520656e746972656c79" }
|
22
|
+
|
23
|
+
it "extends ActiveRecord::Base" do
|
24
|
+
expect(PGCryptoTestModel).to respond_to(:has_encrypted_column)
|
25
|
+
expect(PGCryptoTestModel).to respond_to(:pgcrypto)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "encrypts text on insert" do
|
29
|
+
PGCryptoTestModel.create!(name: 'foobar', encrypted_text: text)
|
30
|
+
expect(stored_raw).not_to eq(text_raw)
|
31
|
+
expect(PGCryptoTestModel.last.name).to eq('foobar')
|
32
|
+
end
|
33
|
+
|
34
|
+
it "encrypts new text on update" do
|
35
|
+
PGCryptoTestModel.create.tap do |model|
|
36
|
+
model.encrypted_text = text
|
37
|
+
model.save!
|
38
|
+
end
|
39
|
+
expect(stored_raw).not_to eq(text_raw)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "encrypts changed text on update" do
|
43
|
+
PGCryptoTestModel.create!(encrypted_text: text).tap do |model|
|
44
|
+
model.update_attributes!(encrypted_text: text_2)
|
45
|
+
end
|
46
|
+
expect(stored_raw).not_to eq(text_2_raw)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "keeps plaintext versions of the encrypted text" do
|
50
|
+
model = PGCryptoTestModel.create!(encrypted_text: text)
|
51
|
+
expect(model.encrypted_text).to eq(text)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "decrypts text when it is selected" do
|
55
|
+
model = PGCryptoTestModel.create!(encrypted_text: text)
|
56
|
+
expect(PGCryptoTestModel.find(model.id).encrypted_text).to eq(text)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "retrieves decrypted text after update" do
|
60
|
+
model = PGCryptoTestModel.create!(:encrypted_text => 'i will update')
|
61
|
+
expect(PGCryptoTestModel.find(model.id).encrypted_text).to eq('i will update')
|
62
|
+
model.update_attributes!(encrypted_text: 'i updated', name: 'testy mctesterson')
|
63
|
+
expect(PGCryptoTestModel.find(model.id).encrypted_text).to eq('i updated')
|
64
|
+
end
|
65
|
+
|
66
|
+
it "retrieves decrypted text without update" do
|
67
|
+
model = PGCryptoTestModel.create!(:encrypted_text => 'i will update')
|
68
|
+
expect(PGCryptoTestModel.find(model.id).encrypted_text).to eq('i will update')
|
69
|
+
model.encrypted_text = 'i updated'
|
70
|
+
expect(model.encrypted_text).to eq('i updated')
|
71
|
+
end
|
72
|
+
|
73
|
+
it "supports querying encrypted columns transparently" do
|
74
|
+
model = PGCryptoTestModel.create!(:encrypted_text => 'i am findable!')
|
75
|
+
expect(PGCryptoTestModel.where(encrypted_text: model.encrypted_text)).to eq([model])
|
76
|
+
end
|
77
|
+
|
78
|
+
it "tracks changes" do
|
79
|
+
model = PGCryptoTestModel.create!(:encrypted_text => 'i am clean')
|
80
|
+
model.encrypted_text = "now i'm not!"
|
81
|
+
expect(model.encrypted_text_changed?).to be_truthy
|
82
|
+
end
|
83
|
+
|
84
|
+
it "is not dirty if attributes are unchanged" do
|
85
|
+
model = PGCryptoTestModel.create!(:encrypted_text => 'i am clean')
|
86
|
+
model.encrypted_text = 'i am clean'
|
87
|
+
expect(model.encrypted_text_changed?).not_to be_truthy
|
88
|
+
end
|
89
|
+
|
90
|
+
it "reloads with the class" do
|
91
|
+
model = PGCryptoTestModel.create!(:encrypted_text => 'i am clean')
|
92
|
+
model.encrypted_text = 'i am dirty'
|
78
93
|
model.reload
|
79
|
-
model.
|
80
|
-
model.
|
81
|
-
end
|
82
|
-
|
83
|
-
it "
|
84
|
-
model = PGCryptoTestModel.create!(:
|
85
|
-
model.
|
86
|
-
model.test_column = 'two'
|
87
|
-
model.save!.should be_true
|
88
|
-
model.select_pgcrypto_column(:test_column).value.should == 'two'
|
89
|
-
end
|
90
|
-
|
91
|
-
it "should delete the column when I set the value to nil" do
|
92
|
-
model = PGCryptoTestModel.create!(:test_column => 'one')
|
93
|
-
model.test_column = nil
|
94
|
-
model.save!
|
95
|
-
model.select_pgcrypto_column(:test_column).should be_nil
|
96
|
-
end
|
97
|
-
|
98
|
-
it "should plz work" do
|
99
|
-
model = PGCryptoTestModel.find(PGCryptoTestModel.create!(:test_column => 'one'))
|
100
|
-
model.test_column = 'two'
|
101
|
-
model.save!
|
102
|
-
model.select_pgcrypto_column(:test_column).value.should == 'two'
|
94
|
+
expect(model.encrypted_text).to eq('i am clean')
|
95
|
+
expect(model.encrypted_text_changed?).not_to be_truthy
|
96
|
+
end
|
97
|
+
|
98
|
+
it "decrypts direct selects" do
|
99
|
+
model = PGCryptoTestModel.create!(:encrypted_text => 'to be selected...')
|
100
|
+
expect(PGCryptoTestModel.select([:id, :encrypted_text]).where(id: model.id).first).to eq(model)
|
103
101
|
end
|
104
102
|
end
|
105
103
|
|
@@ -111,7 +109,7 @@ describe PGCrypto do
|
|
111
109
|
PGCrypto.keys[:public] = {:path => File.join(keypath, 'public.key')}
|
112
110
|
end
|
113
111
|
|
114
|
-
|
112
|
+
instance_eval(&specs)
|
115
113
|
end
|
116
114
|
|
117
115
|
describe "with password-protected keys" do
|
@@ -123,10 +121,13 @@ describe PGCrypto do
|
|
123
121
|
instance_eval(&specs)
|
124
122
|
end
|
125
123
|
|
126
|
-
describe "with
|
127
|
-
before :
|
128
|
-
|
129
|
-
|
124
|
+
describe "with the PostGIS adapter" do
|
125
|
+
before :all do
|
126
|
+
gem 'activerecord-postgis-adapter', ActiveRecord::VERSION::MAJOR == 3 ? '< 0.7' : '>= 1.1'
|
127
|
+
require 'activerecord-postgis-adapter'
|
128
|
+
PGCrypto.keys[:private] = {:path => File.join(keypath, 'private.key')}
|
129
|
+
PGCrypto.keys[:public] = {:path => File.join(keypath, 'public.key')}
|
130
|
+
PGCrypto.base_adapter = ActiveRecord::ConnectionAdapters::PostGISAdapter::MainAdapter
|
130
131
|
end
|
131
132
|
|
132
133
|
instance_eval(&specs)
|