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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +5 -1
  3. data/Gemfile +1 -4
  4. data/README.markdown +126 -33
  5. data/VERSION +1 -1
  6. data/lib/active_record/connection_adapters/pgcrypto_adapter.rb +4 -0
  7. data/lib/active_record/connection_adapters/pgcrypto_adapter/rails_3.rb +20 -0
  8. data/lib/active_record/connection_adapters/pgcrypto_adapter/rails_4.rb +22 -0
  9. data/lib/pgcrypto.rb +15 -105
  10. data/lib/pgcrypto/adapter.rb +162 -0
  11. data/lib/pgcrypto/column.rb +9 -8
  12. data/lib/pgcrypto/column_converter.rb +26 -0
  13. data/lib/pgcrypto/generators/base_generator.rb +12 -0
  14. data/lib/{generators/pgcrypto → pgcrypto/generators}/install/USAGE +0 -0
  15. data/lib/{generators/pgcrypto → pgcrypto/generators}/install/install_generator.rb +2 -5
  16. data/lib/{generators/pgcrypto → pgcrypto/generators}/install/templates/initializer.rb +0 -0
  17. data/lib/pgcrypto/generators/install/templates/migration.rb +5 -0
  18. data/lib/pgcrypto/generators/upgrade/USAGE +8 -0
  19. data/lib/pgcrypto/generators/upgrade/templates/migration.rb +22 -0
  20. data/lib/pgcrypto/generators/upgrade/upgrade_generator.rb +15 -0
  21. data/lib/pgcrypto/has_encrypted_column.rb +25 -0
  22. data/lib/pgcrypto/key.rb +0 -10
  23. data/lib/pgcrypto/key_manager.rb +11 -0
  24. data/lib/pgcrypto/railtie.rb +15 -0
  25. data/lib/pgcrypto/table.rb +11 -0
  26. data/lib/pgcrypto/table_manager.rb +2 -10
  27. data/lib/tasks/pgcrypto.rake +7 -0
  28. data/pgcrypto.gemspec +24 -17
  29. data/spec/lib/pgcrypto_spec.rb +101 -100
  30. data/spec/spec_helper.rb +27 -28
  31. metadata +20 -36
  32. data/lib/generators/pgcrypto/install/templates/migration.rb +0 -17
  33. data/lib/pgcrypto/active_record.rb +0 -88
  34. data/lib/pgcrypto/arel.rb +0 -24
@@ -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
- before_save :set_owner_table
9
- belongs_to :owner, :autosave => false, :inverse_of => :pgcrypto_columns, :polymorphic => true
6
+ belongs_to :owner, polymorphic: true
10
7
 
11
- default_scope { select(%w(id owner_id owner_type owner_table)) }
8
+ has_encrypted_column :value
12
9
 
13
- protected
14
- def set_owner_table
15
- self.owner_table = self.owner.class.table_name
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
@@ -1,11 +1,8 @@
1
- require 'rails/generators/migration'
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 < Rails::Generators::Base
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
 
@@ -0,0 +1,5 @@
1
+ class InstallPgcrypto < ActiveRecord::Migration
2
+ def change
3
+ enable_extension "pgcrypto"
4
+ end
5
+ end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Generates the PGCrypto upgrade-from-0.3.x migration
3
+
4
+ Example:
5
+ rails generate pgcrypto:install
6
+
7
+ This will create:
8
+ db/migrate/XXXXXX_upgrade_pgcrypto_to_0_4_0.rb
@@ -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
@@ -1,14 +1,4 @@
1
1
  module PGCrypto
2
- class KeyManager < Hash
3
- def []=(key, value)
4
- unless value.is_a?(Key)
5
- value = Key.new(value)
6
- end
7
- value.name = key
8
- super key, value
9
- end
10
- end
11
-
12
2
  class Key
13
3
  attr_accessor :name, :password, :value
14
4
  attr_reader :path
@@ -0,0 +1,11 @@
1
+ module PGCrypto
2
+ class KeyManager < Hash
3
+ def []=(key, value)
4
+ unless value.is_a?(Key)
5
+ value = Key.new(value)
6
+ end
7
+ value.name = key
8
+ super key, value
9
+ end
10
+ end
11
+ end
@@ -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
@@ -0,0 +1,11 @@
1
+ module PGCrypto
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
11
+ end
@@ -1,14 +1,6 @@
1
- module PGCrypto
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
@@ -0,0 +1,7 @@
1
+ namespace :pgcrypto do
2
+ desc "Migrate PGCrypto 0.3.x-style columns to 0.4 style"
3
+ task migrate_old_columns: :environment do
4
+ require 'pgcrypto/column_converter'
5
+ PGCrypto::ColumnConverter.migrate!
6
+ end
7
+ end
@@ -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.3.5"
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 = "2012-09-02"
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/generators/pgcrypto/install/USAGE",
30
- "lib/generators/pgcrypto/install/install_generator.rb",
31
- "lib/generators/pgcrypto/install/templates/initializer.rb",
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/active_record.rb",
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.require_paths = ["lib"]
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 = 3
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
@@ -4,102 +4,100 @@ require 'spec_helper'
4
4
  # ActiveRecord::Base.logger = Logger.new(STDOUT)
5
5
 
6
6
  specs = proc do
7
- it "should extend ActiveRecord::Base" do
8
- PGCryptoTestModel.should respond_to(:pgcrypto)
9
- end
10
-
11
- it "should have readers and writers" do
12
- model = PGCryptoTestModel.new
13
- model.should respond_to(:test_column)
14
- model.should respond_to(:test_column=)
15
- end
16
-
17
- it "should be settable on create" do
18
- model = PGCryptoTestModel.new(:test_column => 'this is a test')
19
- model.save!.should be_true
20
- end
21
-
22
- it "should be settable on update" do
23
- model = PGCryptoTestModel.create!
24
- model.test_column = 'this is another test'
25
- model.save!.should be_true
26
- end
27
-
28
- it "should be update-able" do
29
- model = PGCryptoTestModel.create!(:test_column => 'i am test column')
30
- model.update_attributes!(:test_column => 'but now i am a different column, son').should be_true
31
- model.test_column.should == 'but now i am a different column, son'
32
- end
33
-
34
- it "should be retrievable at create" do
35
- model = PGCryptoTestModel.create!(:test_column => 'i am test column')
36
- model.test_column.should == 'i am test column'
37
- end
38
-
39
- it "should be retrievable after create" do
40
- model = PGCryptoTestModel.create!(:test_column => 'i should return to you')
41
- PGCryptoTestModel.find(model.id).test_column.should == 'i should return to you'
42
- end
43
-
44
- it "should be retrievable at update" do
45
- model = PGCryptoTestModel.create!(:test_column => 'i will update')
46
- model.test_column.should == 'i will update'
47
- model.update_attributes!(:test_column => 'i updated')
48
- model.test_column.should == 'i updated'
49
- end
50
-
51
- it "should be retrievable without update" do
52
- model = PGCryptoTestModel.create!(:test_column => 'i will update')
53
- model.test_column.should == 'i will update'
54
- model.test_column = 'i updated'
55
- model.test_column.should == 'i updated'
56
- end
57
-
58
- it "should be searchable" do
59
- model = PGCryptoTestModel.create!(:test_column => 'i am findable!')
60
- PGCryptoTestModel.where(:test_column => model.test_column).should == [model]
61
- end
62
-
63
- it "should track changes" do
64
- model = PGCryptoTestModel.create!(:test_column => 'i am clean')
65
- model.test_column = "now i'm not!"
66
- model.test_column_changed?.should be_true
67
- end
68
-
69
- it "should not be dirty if unchanged" do
70
- model = PGCryptoTestModel.create!(:test_column => 'i am clean')
71
- model.test_column = 'i am clean'
72
- model.test_column_changed?.should_not be_true
73
- end
74
-
75
- it "should reload with the class" do
76
- model = PGCryptoTestModel.create!(:test_column => 'i am clean')
77
- model.test_column = 'i am dirty'
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.test_column.should == 'i am clean'
80
- model.test_column_changed?.should_not be_true
81
- end
82
-
83
- it "should allow direct setting of values as well" do
84
- model = PGCryptoTestModel.create!(:test_column => 'one')
85
- model.test_column.should == 'one'
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
- # instance_eval(&specs)
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 Brett's keys" do
127
- before :each do
128
- PGCrypto.keys[:private] = {:path => File.join(keypath, 'private.brett.key'), :password => '4a13zhUF'}
129
- PGCrypto.keys[:public] = {:path => File.join(keypath, 'public.brett.key')}
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)