pod4 0.10.6 → 1.0.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 +5 -5
- data/.bugs/bugs +2 -1
- data/.bugs/details/b5368c7ef19065fc597b5692314da71772660963.txt +53 -0
- data/.hgtags +1 -0
- data/Gemfile +5 -5
- data/README.md +157 -46
- data/lib/pod4/basic_model.rb +9 -22
- data/lib/pod4/connection.rb +67 -0
- data/lib/pod4/connection_pool.rb +154 -0
- data/lib/pod4/errors.rb +20 -0
- data/lib/pod4/interface.rb +34 -12
- data/lib/pod4/model.rb +32 -27
- data/lib/pod4/nebulous_interface.rb +25 -30
- data/lib/pod4/null_interface.rb +22 -16
- data/lib/pod4/pg_interface.rb +84 -104
- data/lib/pod4/sequel_interface.rb +138 -82
- data/lib/pod4/tds_interface.rb +83 -70
- data/lib/pod4/tweaking.rb +105 -0
- data/lib/pod4/version.rb +1 -1
- data/md/breaking_changes.md +80 -0
- data/spec/common/basic_model_spec.rb +67 -70
- data/spec/common/connection_pool_parallelism_spec.rb +154 -0
- data/spec/common/connection_pool_spec.rb +246 -0
- data/spec/common/connection_spec.rb +129 -0
- data/spec/common/model_ai_missing_id_spec.rb +256 -0
- data/spec/common/model_plus_encrypting_spec.rb +16 -4
- data/spec/common/model_plus_tweaking_spec.rb +128 -0
- data/spec/common/model_plus_typecasting_spec.rb +10 -4
- data/spec/common/model_spec.rb +283 -363
- data/spec/common/nebulous_interface_spec.rb +159 -108
- data/spec/common/null_interface_spec.rb +88 -65
- data/spec/common/sequel_interface_pg_spec.rb +217 -161
- data/spec/common/shared_examples_for_interface.rb +50 -50
- data/spec/jruby/sequel_encrypting_jdbc_pg_spec.rb +1 -1
- data/spec/jruby/sequel_interface_jdbc_ms_spec.rb +3 -3
- data/spec/jruby/sequel_interface_jdbc_pg_spec.rb +3 -23
- data/spec/mri/pg_encrypting_spec.rb +1 -1
- data/spec/mri/pg_interface_spec.rb +311 -223
- data/spec/mri/sequel_encrypting_spec.rb +1 -1
- data/spec/mri/sequel_interface_spec.rb +177 -180
- data/spec/mri/tds_encrypting_spec.rb +1 -1
- data/spec/mri/tds_interface_spec.rb +296 -212
- data/tags +340 -174
- metadata +19 -11
- data/md/fixme.md +0 -3
- data/md/roadmap.md +0 -125
- data/md/typecasting.md +0 -80
- data/spec/common/model_new_validate_spec.rb +0 -204
@@ -1,17 +1,16 @@
|
|
1
|
-
require
|
1
|
+
require "octothorpe"
|
2
2
|
|
3
3
|
|
4
4
|
##
|
5
|
-
# These are the shared tests for all interfaces. To use them you need to
|
6
|
-
# supply:
|
5
|
+
# These are the shared tests for all interfaces. To use them you need to supply:
|
7
6
|
#
|
8
7
|
# * record - a record to insert
|
9
8
|
# * interface - an instance of the interface to call.
|
10
9
|
#
|
11
10
|
# For example (from nebulous_interface_spec):
|
12
11
|
#
|
13
|
-
# it_behaves_like
|
14
|
-
# let(:record) { {id: 1, name:
|
12
|
+
# it_behaves_like "an interface" do
|
13
|
+
# let(:record) { {id: 1, name: "percy"} }
|
15
14
|
#
|
16
15
|
# let(:interface) do
|
17
16
|
# init_nebulous
|
@@ -19,36 +18,45 @@ require 'octothorpe'
|
|
19
18
|
# end
|
20
19
|
# end
|
21
20
|
#
|
22
|
-
# 'record' does not have to include every column in your table, and you should
|
23
|
-
#
|
24
|
-
#
|
25
|
-
# in the table definition.
|
21
|
+
# 'record' does not have to include every column in your table, and you should actively exclude
|
22
|
+
# floats (where you cannot guarantee equality) or anything that is supposed to render to a
|
23
|
+
# BigDecimal. Make sure these fields are NULL in the table definition.
|
26
24
|
#
|
27
|
-
# Note that these shared tests only test the common parts of the API that the
|
28
|
-
#
|
29
|
-
#
|
30
|
-
# the interface is an adapter to.
|
25
|
+
# Note that these shared tests only test the common parts of the API that the interface exposes to
|
26
|
+
# the *model* They don't attempt to test your Interface's communication to it's data access layer,
|
27
|
+
# or how it talks to the Connection class. It makes no assumptions about where your test data is
|
28
|
+
# coming from, or how you are calling or mocking whatever library the interface is an adapter to.
|
31
29
|
#
|
32
|
-
# It's up to the individual specs to test that the interface is calling its
|
33
|
-
#
|
34
|
-
#
|
30
|
+
# It's up to the individual specs to test that the interface is calling its library correctly and
|
31
|
+
# deal with all the things specific to that interface - which includes how the model calls new()
|
32
|
+
# and list(), for example.
|
35
33
|
#
|
36
|
-
RSpec.shared_examples
|
34
|
+
RSpec.shared_examples "an interface" do
|
37
35
|
|
38
36
|
let(:record_as_ot) { Octothorpe.new(record) }
|
39
37
|
|
40
38
|
|
41
|
-
describe
|
42
|
-
|
39
|
+
describe "#id_fld" do
|
40
|
+
|
41
|
+
it "is an attribute" do
|
43
42
|
expect( interface.id_fld ).not_to be_nil
|
44
43
|
end
|
45
|
-
end
|
46
|
-
##
|
47
44
|
|
45
|
+
end # of #id_fld
|
46
|
+
|
47
|
+
|
48
|
+
describe "#id_ai" do
|
49
|
+
|
50
|
+
it "is an attribute" do
|
51
|
+
expect( interface.id_ai ).not_to be_nil
|
52
|
+
end
|
48
53
|
|
49
|
-
|
54
|
+
end # of #id_ai
|
50
55
|
|
51
|
-
|
56
|
+
|
57
|
+
describe "#create" do
|
58
|
+
|
59
|
+
it "requires a hash or an Octothorpe" do
|
52
60
|
expect{ interface.create }.to raise_exception ArgumentError
|
53
61
|
expect{ interface.create(nil) }.to raise_exception ArgumentError
|
54
62
|
expect{ interface.create(3) }.to raise_exception ArgumentError
|
@@ -57,47 +65,43 @@ RSpec.shared_examples 'an interface' do
|
|
57
65
|
expect{ interface.create(record_as_ot) }.not_to raise_exception
|
58
66
|
end
|
59
67
|
|
60
|
-
it
|
68
|
+
it "returns the ID" do
|
61
69
|
record_id = interface.create(record)
|
62
70
|
expect{ interface.read(record_id) }.not_to raise_exception
|
63
71
|
expect( interface.read(record_id).to_h ).to include record
|
64
72
|
end
|
65
73
|
|
66
|
-
end
|
67
|
-
##
|
74
|
+
end # of #create
|
68
75
|
|
69
76
|
|
70
|
-
describe
|
77
|
+
describe "#read" do
|
71
78
|
|
72
79
|
before do
|
73
80
|
interface.create(record)
|
74
81
|
@id = interface.list.first[interface.id_fld]
|
75
82
|
end
|
76
83
|
|
77
|
-
it
|
84
|
+
it "requires an ID" do
|
78
85
|
expect{ interface.read }.to raise_exception ArgumentError
|
79
86
|
expect{ interface.read(nil) }.to raise_exception ArgumentError
|
80
87
|
|
81
88
|
expect{ interface.read(@id) }.not_to raise_exception
|
82
89
|
end
|
83
90
|
|
84
|
-
it
|
91
|
+
it "returns an Octothorpe" do
|
85
92
|
expect( interface.read(@id) ).to be_a_kind_of Octothorpe
|
86
93
|
end
|
87
94
|
|
88
|
-
end
|
89
|
-
##
|
95
|
+
end # of #read
|
90
96
|
|
91
97
|
|
92
|
-
describe
|
93
|
-
|
98
|
+
describe "#update" do
|
94
99
|
before do
|
95
100
|
interface.create(record)
|
96
101
|
@id = interface.list.first[interface.id_fld]
|
97
102
|
end
|
98
103
|
|
99
|
-
|
100
|
-
it 'requires an ID and a record (hash or OT)' do
|
104
|
+
it "requires an ID and a record (hash or OT)" do
|
101
105
|
expect{ interface.update }.to raise_exception ArgumentError
|
102
106
|
expect{ interface.update(nil) }.to raise_exception ArgumentError
|
103
107
|
expect{ interface.update(14) }.to raise_exception ArgumentError
|
@@ -105,55 +109,51 @@ RSpec.shared_examples 'an interface' do
|
|
105
109
|
expect{ interface.update(@id, record) }.not_to raise_exception
|
106
110
|
end
|
107
111
|
|
108
|
-
it
|
112
|
+
it "returns self" do
|
109
113
|
expect( interface.update(@id, record) ).to eq interface
|
110
114
|
end
|
111
115
|
|
112
|
-
end
|
113
|
-
##
|
114
|
-
|
116
|
+
end # of #update
|
115
117
|
|
116
|
-
describe '#delete' do
|
117
118
|
|
119
|
+
describe "#delete" do
|
118
120
|
before do
|
119
121
|
interface.create(record)
|
120
122
|
@id = interface.list.first[interface.id_fld]
|
121
123
|
end
|
122
124
|
|
123
|
-
it
|
125
|
+
it "requires an id" do
|
124
126
|
expect{ interface.delete }.to raise_exception ArgumentError
|
125
127
|
expect{ interface.delete(nil) }.to raise_exception ArgumentError
|
126
128
|
|
127
129
|
expect{ interface.delete(@id) }.not_to raise_exception
|
128
130
|
end
|
129
131
|
|
130
|
-
it
|
132
|
+
it "returns self" do
|
131
133
|
expect( interface.delete(@id) ).to eq interface
|
132
134
|
end
|
133
135
|
|
134
|
-
end
|
135
|
-
##
|
136
|
+
end # of #delete
|
136
137
|
|
137
138
|
|
138
|
-
describe
|
139
|
+
describe "#list" do
|
139
140
|
|
140
|
-
it
|
141
|
+
it "will allow itself to be called with no parameter" do
|
141
142
|
expect{ interface.list }.not_to raise_exception
|
142
143
|
end
|
143
144
|
|
144
|
-
it
|
145
|
+
it "returns an array of Octothorpes" do
|
145
146
|
interface.create(record)
|
146
147
|
expect( interface.list ).to be_a_kind_of Array
|
147
148
|
expect( interface.list.first ).to be_a_kind_of Octothorpe
|
148
149
|
end
|
149
150
|
|
150
|
-
it
|
151
|
+
it "has the ID field as one of the Octothorpe keys" do
|
151
152
|
interface.create(record)
|
152
153
|
expect( interface.list.first.to_h ).to have_key interface.id_fld
|
153
154
|
end
|
154
155
|
|
155
|
-
end
|
156
|
-
##
|
156
|
+
end # of #list
|
157
157
|
|
158
158
|
|
159
159
|
end
|
@@ -19,7 +19,7 @@ describe "SequelInterface (JDBC/MSSQL)" do
|
|
19
19
|
let(:sequel_interface_class) do
|
20
20
|
Class.new SequelInterface do
|
21
21
|
set_table :customer
|
22
|
-
set_id_fld :id
|
22
|
+
set_id_fld :id, autoincrement: true
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
@@ -27,14 +27,14 @@ describe "SequelInterface (JDBC/MSSQL)" do
|
|
27
27
|
Class.new SequelInterface do
|
28
28
|
set_schema :public
|
29
29
|
set_table :customer
|
30
|
-
set_id_fld :id
|
30
|
+
set_id_fld :id, autoincrement: true
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
34
|
let(:prod_interface_class) do
|
35
35
|
Class.new SequelInterface do
|
36
36
|
set_table :product
|
37
|
-
set_id_fld :code
|
37
|
+
set_id_fld :code, autoincrement: false
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
@@ -14,32 +14,12 @@ require 'bigdecimal'
|
|
14
14
|
require 'jdbc/postgres'
|
15
15
|
|
16
16
|
|
17
|
-
=begin
|
18
|
-
class TestSequelInterfacePg < SequelInterface
|
19
|
-
set_table :customer
|
20
|
-
set_id_fld :id
|
21
|
-
end
|
22
|
-
|
23
|
-
class SchemaSequelInterfacePg < SequelInterface
|
24
|
-
set_schema :public
|
25
|
-
set_table :customer
|
26
|
-
set_id_fld :id
|
27
|
-
end
|
28
|
-
|
29
|
-
class ProdSequelInterfacePg < SequelInterface
|
30
|
-
set_table :product
|
31
|
-
set_id_fld :code
|
32
|
-
end
|
33
|
-
=end
|
34
|
-
|
35
|
-
|
36
|
-
|
37
17
|
describe "SequelInterface (JDBC/Pg)" do
|
38
18
|
|
39
19
|
let(:sequel_interface_class) do
|
40
20
|
Class.new SequelInterface do
|
41
21
|
set_table :customer
|
42
|
-
set_id_fld :id
|
22
|
+
set_id_fld :id, autoincrement: true
|
43
23
|
end
|
44
24
|
end
|
45
25
|
|
@@ -47,14 +27,14 @@ describe "SequelInterface (JDBC/Pg)" do
|
|
47
27
|
Class.new SequelInterface do
|
48
28
|
set_schema :public
|
49
29
|
set_table :customer
|
50
|
-
set_id_fld :id
|
30
|
+
set_id_fld :id, autoincrement: true
|
51
31
|
end
|
52
32
|
end
|
53
33
|
|
54
34
|
let(:prod_interface_class) do
|
55
35
|
Class.new SequelInterface do
|
56
36
|
set_table :product
|
57
|
-
set_id_fld :code
|
37
|
+
set_id_fld :code, autoincrement: false
|
58
38
|
end
|
59
39
|
end
|
60
40
|
|
@@ -1,18 +1,58 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "pod4/pg_interface"
|
2
|
+
require "pg"
|
3
3
|
|
4
|
-
require_relative
|
5
|
-
require_relative
|
4
|
+
require_relative "../common/shared_examples_for_interface"
|
5
|
+
require_relative "../fixtures/database"
|
6
6
|
|
7
7
|
|
8
8
|
describe "PgInterface" do
|
9
9
|
|
10
|
+
def db_setup(connect)
|
11
|
+
client = PG.connect(connect)
|
12
|
+
|
13
|
+
client.exec(%Q|
|
14
|
+
drop table if exists customer;
|
15
|
+
drop table if exists product;
|
16
|
+
|
17
|
+
create table customer (
|
18
|
+
id serial primary key,
|
19
|
+
name text,
|
20
|
+
level real null,
|
21
|
+
day date null,
|
22
|
+
timestamp timestamp null,
|
23
|
+
price money null,
|
24
|
+
flag boolean null,
|
25
|
+
qty numeric null );
|
26
|
+
|
27
|
+
create table product (
|
28
|
+
code text,
|
29
|
+
name text );| )
|
30
|
+
|
31
|
+
ensure
|
32
|
+
client.finish if client
|
33
|
+
end
|
34
|
+
|
35
|
+
def fill_data(ifce)
|
36
|
+
@data.each{|r| ifce.create(r) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def fill_product_data(ifce)
|
40
|
+
ifce.create( {code: "foo", name: "bar"} )
|
41
|
+
end
|
42
|
+
|
43
|
+
def list_contains(ifce, id)
|
44
|
+
ifce.list.find {|x| x[ifce.id_fld] == id }
|
45
|
+
end
|
46
|
+
|
47
|
+
def float_price(row)
|
48
|
+
row[:price] = row[:price].to_f
|
49
|
+
row
|
50
|
+
end
|
51
|
+
|
10
52
|
let(:pg_interface_class) do
|
11
53
|
Class.new PgInterface do
|
12
54
|
set_table :customer
|
13
55
|
set_id_fld :id
|
14
|
-
|
15
|
-
def stop; close; end # We open a lot of connections, unusually
|
16
56
|
end
|
17
57
|
end
|
18
58
|
|
@@ -20,14 +60,14 @@ describe "PgInterface" do
|
|
20
60
|
Class.new PgInterface do
|
21
61
|
set_schema :public
|
22
62
|
set_table :customer
|
23
|
-
set_id_fld :id
|
63
|
+
set_id_fld :id, autoincrement: true
|
24
64
|
end
|
25
65
|
end
|
26
66
|
|
27
67
|
let(:prod_interface_class) do
|
28
68
|
Class.new PgInterface do
|
29
69
|
set_table :product
|
30
|
-
set_id_fld :code
|
70
|
+
set_id_fld :code, autoincrement: false
|
31
71
|
end
|
32
72
|
end
|
33
73
|
|
@@ -43,76 +83,49 @@ describe "PgInterface" do
|
|
43
83
|
end
|
44
84
|
end
|
45
85
|
|
46
|
-
|
47
|
-
|
48
|
-
client = PG.connect(connect)
|
49
|
-
|
50
|
-
client.exec(%Q|
|
51
|
-
drop table if exists customer;
|
52
|
-
drop table if exists product;
|
53
|
-
|
54
|
-
create table customer (
|
55
|
-
id serial primary key,
|
56
|
-
name text,
|
57
|
-
level real null,
|
58
|
-
day date null,
|
59
|
-
timestamp timestamp null,
|
60
|
-
price money null,
|
61
|
-
flag boolean null,
|
62
|
-
qty numeric null );
|
63
|
-
|
64
|
-
create table product (
|
65
|
-
code text,
|
66
|
-
name text );| )
|
67
|
-
|
68
|
-
ensure
|
69
|
-
client.finish if client
|
70
|
-
end
|
71
|
-
|
72
|
-
|
73
|
-
def fill_data(ifce)
|
74
|
-
@data.each{|r| ifce.create(r) }
|
86
|
+
let(:interface) do
|
87
|
+
pg_interface_class.new(@pool)
|
75
88
|
end
|
76
89
|
|
77
|
-
|
78
|
-
|
79
|
-
ifce.create( {code: "foo", name: "bar"} )
|
90
|
+
let(:prod_interface) do
|
91
|
+
prod_interface_class.new(@pool)
|
80
92
|
end
|
81
93
|
|
82
|
-
|
83
94
|
before(:all) do
|
84
95
|
@connect_hash = DB[:pg]
|
85
96
|
db_setup(@connect_hash)
|
86
97
|
|
87
98
|
@data = []
|
88
|
-
@data << { name:
|
99
|
+
@data << { name: "Barney",
|
89
100
|
level: 1.23,
|
90
101
|
day: Date.parse("2016-01-01"),
|
91
|
-
timestamp: Time.parse(
|
102
|
+
timestamp: Time.parse("2015-01-01 12:11"),
|
92
103
|
price: BigDecimal("1.24"),
|
93
104
|
flag: true,
|
94
105
|
qty: BigDecimal("1.25") }
|
95
106
|
|
96
|
-
@data << { name:
|
107
|
+
@data << { name: "Fred",
|
97
108
|
level: 2.34,
|
98
109
|
day: Date.parse("2016-02-02"),
|
99
|
-
timestamp: Time.parse(
|
110
|
+
timestamp: Time.parse("2015-01-02 12:22"),
|
100
111
|
price: BigDecimal("2.35"),
|
101
112
|
flag: false,
|
102
113
|
qty: BigDecimal("2.36") }
|
103
114
|
|
104
|
-
@data << { name:
|
115
|
+
@data << { name: "Betty",
|
105
116
|
level: 3.45,
|
106
117
|
day: Date.parse("2016-03-03"),
|
107
|
-
timestamp: Time.parse(
|
118
|
+
timestamp: Time.parse("2015-01-03 12:33"),
|
108
119
|
price: BigDecimal("3.46"),
|
109
120
|
flag: nil,
|
110
121
|
qty: BigDecimal("3.47") }
|
111
122
|
|
123
|
+
# one connection pool for the whole suite, so it doesn't grab (number of tests) connections.
|
124
|
+
@pool = ConnectionPool.new(interface: PgInterface)
|
125
|
+
@pool.data_layer_options = @connect_hash
|
112
126
|
end
|
113
127
|
|
114
|
-
|
115
|
-
before do
|
128
|
+
before(:each) do
|
116
129
|
interface.execute(%Q|
|
117
130
|
truncate table customer restart identity;
|
118
131
|
truncate table product;|)
|
@@ -120,127 +133,147 @@ describe "PgInterface" do
|
|
120
133
|
end
|
121
134
|
|
122
135
|
|
123
|
-
|
124
|
-
# We open a lot of connections, unusually
|
125
|
-
interface.stop if interface
|
126
|
-
end
|
127
|
-
|
128
|
-
|
129
|
-
let(:interface) do
|
130
|
-
pg_interface_class.new(@connect_hash)
|
131
|
-
end
|
132
|
-
|
133
|
-
let(:prod_interface) do
|
134
|
-
prod_interface_class.new(@connect_hash)
|
135
|
-
end
|
136
|
-
|
137
|
-
#####
|
138
|
-
|
139
|
-
|
140
|
-
it_behaves_like 'an interface' do
|
141
|
-
|
136
|
+
it_behaves_like "an interface" do
|
142
137
|
let(:interface) do
|
143
138
|
pg_interface_class.new(@connect_hash)
|
144
139
|
end
|
145
140
|
|
146
|
-
let(:record) { {name:
|
141
|
+
let(:record) { {name: "Barney"} }
|
147
142
|
|
148
|
-
end
|
149
|
-
##
|
143
|
+
end # of it_behaves_like
|
150
144
|
|
151
145
|
|
152
|
-
describe
|
153
|
-
|
146
|
+
describe "PgInterface.set_schema" do
|
147
|
+
|
148
|
+
it "takes one argument" do
|
154
149
|
expect( pg_interface_class ).to respond_to(:set_schema).with(1).argument
|
155
150
|
end
|
156
|
-
|
157
|
-
|
151
|
+
|
152
|
+
end # of PgInterface.set_schema
|
158
153
|
|
159
154
|
|
160
|
-
describe
|
161
|
-
|
155
|
+
describe "PgInterface.schema" do
|
156
|
+
|
157
|
+
it "returns the schema" do
|
162
158
|
expect( schema_interface_class.schema ).to eq :public
|
163
159
|
end
|
164
160
|
|
165
|
-
it
|
161
|
+
it "is optional" do
|
166
162
|
expect{ pg_interface_class.schema }.not_to raise_exception
|
167
163
|
expect( pg_interface_class.schema ).to eq nil
|
168
164
|
end
|
169
|
-
end
|
170
|
-
##
|
171
165
|
|
166
|
+
end # of PgInterface.schema
|
172
167
|
|
173
|
-
|
174
|
-
|
168
|
+
|
169
|
+
describe "PgInterface.set_table" do
|
170
|
+
|
171
|
+
it "takes one argument" do
|
175
172
|
expect( pg_interface_class ).to respond_to(:set_table).with(1).argument
|
176
173
|
end
|
177
|
-
|
178
|
-
|
174
|
+
|
175
|
+
end # of PgInterface.set_table
|
179
176
|
|
180
177
|
|
181
|
-
describe
|
182
|
-
|
178
|
+
describe "PgInterface.table" do
|
179
|
+
|
180
|
+
it "returns the table" do
|
183
181
|
expect( pg_interface_class.table ).to eq :customer
|
184
182
|
end
|
185
|
-
end
|
186
|
-
##
|
187
183
|
|
184
|
+
end # of PgInterface.table
|
185
|
+
|
186
|
+
|
187
|
+
describe "PgInterface.set_id_fld" do
|
188
188
|
|
189
|
-
|
190
|
-
it 'takes one argument' do
|
189
|
+
it "takes one argument" do
|
191
190
|
expect( pg_interface_class ).to respond_to(:set_id_fld).with(1).argument
|
192
191
|
end
|
193
|
-
end
|
194
|
-
##
|
195
192
|
|
193
|
+
it "takes an optional second 'autoincrement' argument" do
|
194
|
+
expect{ PgInterface.set_id_fld(:foo, autoincrement: false) }.not_to raise_error
|
195
|
+
end
|
196
196
|
|
197
|
-
|
198
|
-
|
197
|
+
end # of PgInterface.set_id_fld
|
198
|
+
|
199
|
+
|
200
|
+
describe "PgInterface.id_fld" do
|
201
|
+
|
202
|
+
it "returns the ID field name" do
|
199
203
|
expect( pg_interface_class.id_fld ).to eq :id
|
200
204
|
end
|
201
|
-
end
|
202
|
-
##
|
203
205
|
|
206
|
+
end # of PgInterface.id_fld
|
207
|
+
|
208
|
+
|
209
|
+
describe "PgInterface.id_ai" do
|
210
|
+
|
211
|
+
it "returns true if autoincrement is true" do
|
212
|
+
expect( schema_interface_class.id_ai ).to eq true
|
213
|
+
end
|
214
|
+
|
215
|
+
it "returns false if autoincrement is false" do
|
216
|
+
expect( prod_interface_class.id_ai ).to eq false
|
217
|
+
end
|
218
|
+
|
219
|
+
it "returns true if autoincrement is not specified" do
|
220
|
+
expect( pg_interface_class.id_ai ).to eq true
|
221
|
+
end
|
222
|
+
|
223
|
+
end # of PgInterface.id_ai
|
224
|
+
|
225
|
+
|
226
|
+
describe "#new" do
|
227
|
+
|
228
|
+
it "creates a ConnectionPool when passed a PG connection hash" do
|
229
|
+
ifce = pg_interface_class.new(@connect_hash)
|
230
|
+
expect( ifce._connection ).to be_a ConnectionPool
|
231
|
+
end
|
232
|
+
|
233
|
+
it "uses the ConnectionPool when given one" do
|
234
|
+
pool = ConnectionPool.new(interface: pg_interface_class)
|
235
|
+
ifce = pg_interface_class.new(pool)
|
204
236
|
|
205
|
-
|
237
|
+
expect( ifce._connection ).to eq pool
|
238
|
+
end
|
206
239
|
|
207
|
-
it
|
240
|
+
it "raises ArgumentError when passed something else" do
|
208
241
|
expect{ pg_interface_class.new }.to raise_exception ArgumentError
|
209
242
|
expect{ pg_interface_class.new(nil) }.to raise_exception ArgumentError
|
210
|
-
expect{ pg_interface_class.new(
|
211
|
-
|
212
|
-
expect{ pg_interface_class.new(@connect_hash) }.not_to raise_exception
|
243
|
+
expect{ pg_interface_class.new("foo") }.to raise_exception ArgumentError
|
213
244
|
end
|
214
245
|
|
215
|
-
end
|
216
|
-
##
|
246
|
+
end # of #new
|
217
247
|
|
218
248
|
|
219
|
-
describe
|
249
|
+
describe "#quoted_table" do
|
220
250
|
|
221
|
-
it
|
251
|
+
it "returns just the table when the schema is not set" do
|
222
252
|
expect( interface.quoted_table ).to eq( %Q|"customer"| )
|
223
253
|
end
|
224
254
|
|
225
|
-
it
|
255
|
+
it "returns the schema plus table when the schema is set" do
|
226
256
|
ifce = schema_interface_class.new(@connect_hash)
|
227
257
|
expect( ifce.quoted_table ).to eq( %|"public"."customer"| )
|
228
258
|
end
|
229
259
|
|
230
|
-
end
|
231
|
-
##
|
260
|
+
end # of #quoted_table
|
232
261
|
|
233
262
|
|
234
|
-
describe
|
263
|
+
describe "#create" do
|
264
|
+
let(:hash) { {name: "Bam-Bam", price: 4.44} }
|
265
|
+
let(:ot) { Octothorpe.new(name: "Wilma", price: 5.55) }
|
235
266
|
|
236
|
-
|
237
|
-
|
267
|
+
it "raises a Pod4::DatabaseError if anything goes wrong" do
|
268
|
+
expect{ interface.create(one: "two") }.to raise_exception DatabaseError
|
269
|
+
end
|
238
270
|
|
239
|
-
it
|
240
|
-
|
271
|
+
it "raises an ArgumentError if ID field is missing in hash and not AI" do
|
272
|
+
hash = {name: "bar"}
|
273
|
+
expect{ prod_interface.create(Octothorpe.new hash) }.to raise_error ArgumentError
|
241
274
|
end
|
242
275
|
|
243
|
-
it
|
276
|
+
it "creates the record when given a hash" do
|
244
277
|
# kinda impossible to seperate these two tests
|
245
278
|
id = interface.create(hash)
|
246
279
|
|
@@ -248,28 +281,28 @@ describe "PgInterface" do
|
|
248
281
|
expect( interface.read(id).to_h ).to include hash
|
249
282
|
end
|
250
283
|
|
251
|
-
it
|
284
|
+
it "creates the record when given an Octothorpe" do
|
252
285
|
id = interface.create(ot)
|
253
286
|
|
254
287
|
expect{ interface.read(id) }.not_to raise_exception
|
255
288
|
expect( interface.read(id).to_h ).to include ot.to_h
|
256
289
|
end
|
257
290
|
|
258
|
-
it
|
259
|
-
record = {name:
|
291
|
+
it "has no problem with record values of nil" do
|
292
|
+
record = {name: "Ranger", price: nil}
|
260
293
|
expect{ interface.create(record) }.not_to raise_exception
|
261
294
|
id = interface.create(record)
|
262
295
|
expect( interface.read(id).to_h ).to include(record)
|
263
296
|
end
|
264
297
|
|
265
|
-
it
|
266
|
-
record = {name: %Q|T
|
298
|
+
it "has no problem with strings containing special characters" do
|
299
|
+
record = {name: %Q|T"Challa""|, price: nil}
|
267
300
|
expect{ interface.create(record) }.not_to raise_exception
|
268
301
|
id = interface.create(record)
|
269
302
|
expect( interface.read(id).to_h ).to include(record)
|
270
303
|
end
|
271
304
|
|
272
|
-
it
|
305
|
+
it "has no problem with non-integer keys" do
|
273
306
|
hash = {code: "foo", name: "bar"}
|
274
307
|
id = prod_interface.create( Octothorpe.new(hash) )
|
275
308
|
|
@@ -278,65 +311,79 @@ describe "PgInterface" do
|
|
278
311
|
expect( prod_interface.read("foo").to_h ).to include hash
|
279
312
|
end
|
280
313
|
|
281
|
-
|
282
|
-
|
314
|
+
it "calls ConnectionPool#client" do
|
315
|
+
expect( interface._connection ).to receive(:client).at_least(:once).and_call_original
|
316
|
+
interface.create(ot)
|
317
|
+
end
|
318
|
+
|
319
|
+
it "copes with an OT with an ID field when autoincrement = true" do
|
320
|
+
h = {id: nil, name: "Bam-Bam", qty: 4.44}
|
321
|
+
expect{ interface.create(h) }.not_to raise_error
|
322
|
+
|
323
|
+
id = interface.create(h)
|
324
|
+
expect( id ).not_to be_nil
|
325
|
+
expect{ interface.read(id) }.not_to raise_exception
|
326
|
+
expect( interface.read(id).to_h ).to include( {name: "Bam-Bam", qty: 4.44} )
|
327
|
+
end
|
328
|
+
|
329
|
+
end # of #create
|
283
330
|
|
284
331
|
|
285
|
-
describe
|
332
|
+
describe "#read" do
|
286
333
|
before { fill_data(interface) }
|
287
334
|
|
288
|
-
it
|
335
|
+
it "returns the record for the id as an Octothorpe" do
|
289
336
|
rec = interface.read(2)
|
290
337
|
expect( rec ).to be_a_kind_of Octothorpe
|
291
|
-
expect( rec.>>.name ).to eq
|
338
|
+
expect( rec.>>.name ).to eq "Fred"
|
292
339
|
end
|
293
340
|
|
294
|
-
it
|
341
|
+
it "raises a Pod4::CantContinue if the ID is bad" do
|
295
342
|
expect{ interface.read(:foo) }.to raise_exception CantContinue
|
296
343
|
end
|
297
344
|
|
298
|
-
it
|
345
|
+
it "returns an empty Octothorpe if no record matches the ID" do
|
299
346
|
expect{ interface.read(99) }.not_to raise_exception
|
300
347
|
expect( interface.read(99) ).to be_a_kind_of Octothorpe
|
301
348
|
expect( interface.read(99) ).to be_empty
|
302
349
|
end
|
303
350
|
|
304
|
-
it
|
351
|
+
it "returns real fields as Float" do
|
305
352
|
level = interface.read(1).>>.level
|
306
353
|
|
307
354
|
expect( level ).to be_a_kind_of Float
|
308
355
|
expect( level ).to be_within(0.001).of( @data.first[:level] )
|
309
356
|
end
|
310
357
|
|
311
|
-
it
|
358
|
+
it "returns date fields as Date" do
|
312
359
|
date = interface.read(1).>>.day
|
313
360
|
|
314
361
|
expect( date ).to be_a_kind_of Date
|
315
362
|
expect( date ).to eq @data.first[:day]
|
316
363
|
end
|
317
364
|
|
318
|
-
it
|
365
|
+
it "returns datetime fields as Time" do
|
319
366
|
timestamp = interface.read(1).>>.timestamp
|
320
367
|
|
321
368
|
expect( timestamp ).to be_a_kind_of Time
|
322
369
|
expect( timestamp ).to eq @data.first[:timestamp]
|
323
370
|
end
|
324
371
|
|
325
|
-
it
|
372
|
+
it "returns numeric fields as BigDecimal" do
|
326
373
|
qty = interface.read(1).>>.qty
|
327
374
|
|
328
375
|
expect( qty ).to be_a_kind_of BigDecimal
|
329
376
|
expect( qty ).to eq @data.first[:qty]
|
330
377
|
end
|
331
378
|
|
332
|
-
it
|
379
|
+
it "returns money fields as BigDecimal" do
|
333
380
|
price = interface.read(1).>>.price
|
334
381
|
|
335
382
|
expect( price ).to be_a_kind_of BigDecimal
|
336
383
|
expect( price ).to eq @data.first[:price]
|
337
384
|
end
|
338
385
|
|
339
|
-
it
|
386
|
+
it "returns boolean fields as boolean" do
|
340
387
|
[1,2,3].each do |i|
|
341
388
|
flag = interface.read(i).>>.flag
|
342
389
|
expect( [true, false, nil].include? flag ).to be true
|
@@ -344,7 +391,7 @@ describe "PgInterface" do
|
|
344
391
|
end
|
345
392
|
end
|
346
393
|
|
347
|
-
it
|
394
|
+
it "has no problem with non-integer keys" do
|
348
395
|
# this is a 100% overlap with the create test above...
|
349
396
|
fill_product_data(prod_interface)
|
350
397
|
|
@@ -352,260 +399,301 @@ describe "PgInterface" do
|
|
352
399
|
expect( prod_interface.read("foo").to_h ).to include(code: "foo", name: "bar")
|
353
400
|
end
|
354
401
|
|
355
|
-
|
356
|
-
|
402
|
+
it "calls ConnectionPool#client" do
|
403
|
+
expect( interface._connection ).to receive(:client).at_least(:once).and_call_original
|
404
|
+
interface.read(99)
|
405
|
+
end
|
406
|
+
|
407
|
+
end # of #read
|
357
408
|
|
358
409
|
|
359
|
-
describe
|
410
|
+
describe "#list" do
|
360
411
|
before { fill_data(interface) }
|
361
412
|
|
362
|
-
it
|
413
|
+
it "has an optional selection parameter, a hash" do
|
363
414
|
# Actually it does not have to be a hash, but FTTB we only support that.
|
364
|
-
expect{ interface.list(name:
|
415
|
+
expect{ interface.list(name: "Barney") }.not_to raise_exception
|
365
416
|
end
|
366
417
|
|
367
|
-
it
|
418
|
+
it "returns an array of Octothorpes that match the records" do
|
368
419
|
# convert each OT to a hash and remove the ID key
|
369
420
|
arr = interface.list.map {|ot| x = ot.to_h; x.delete(:id); x }
|
370
421
|
|
371
422
|
expect( arr ).to match_array @data
|
372
423
|
end
|
373
424
|
|
374
|
-
it
|
375
|
-
expect( interface.list(name:
|
425
|
+
it "returns a subset of records based on the selection parameter" do
|
426
|
+
expect( interface.list(name: "Fred").size ).to eq 1
|
376
427
|
|
377
|
-
expect( interface.list(name:
|
378
|
-
to include(name:
|
428
|
+
expect( interface.list(name: "Betty").first.to_h ).
|
429
|
+
to include(name: "Betty")
|
379
430
|
|
380
431
|
end
|
381
432
|
|
382
|
-
it
|
383
|
-
expect( interface.list(name:
|
433
|
+
it "returns an empty Array if nothing matches" do
|
434
|
+
expect( interface.list(name: "Yogi") ).to eq([])
|
384
435
|
end
|
385
436
|
|
386
|
-
it
|
387
|
-
expect{ interface.list(
|
437
|
+
it "raises ArgumentError if the selection criteria is nonsensical" do
|
438
|
+
expect{ interface.list("foo") }.to raise_exception ArgumentError
|
388
439
|
end
|
389
440
|
|
390
|
-
|
391
|
-
|
441
|
+
it "calls ConnectionPool#client" do
|
442
|
+
expect( interface._connection ).to receive(:client).at_least(:once).and_call_original
|
443
|
+
interface.list
|
444
|
+
end
|
445
|
+
|
446
|
+
end # of #list
|
392
447
|
|
393
448
|
|
394
|
-
describe
|
449
|
+
describe "#update" do
|
395
450
|
before { fill_data(interface) }
|
396
451
|
|
397
452
|
let(:id) { interface.list.first[:id] }
|
398
453
|
|
399
|
-
|
400
|
-
|
401
|
-
row
|
402
|
-
end
|
403
|
-
|
404
|
-
it 'updates the record at ID with record parameter' do
|
405
|
-
record = {name: 'Booboo', price: 99.99}
|
454
|
+
it "updates the record at ID with record parameter" do
|
455
|
+
record = {name: "Booboo", price: 99.99}
|
406
456
|
interface.update(id, record)
|
407
457
|
|
408
458
|
expect( float_price( interface.read(id).to_h ) ).to include(record)
|
409
459
|
end
|
410
460
|
|
411
|
-
it
|
412
|
-
expect{ interface.update(99, name:
|
461
|
+
it "raises a CantContinue if anything weird happens with the ID" do
|
462
|
+
expect{ interface.update(99, name: "Booboo") }.
|
413
463
|
to raise_exception CantContinue
|
414
464
|
|
415
465
|
end
|
416
466
|
|
417
|
-
it
|
418
|
-
expect{ interface.update(id, smarts:
|
467
|
+
it "raises a DatabaseError if anything weird happens with the record" do
|
468
|
+
expect{ interface.update(id, smarts: "more") }.
|
419
469
|
to raise_exception DatabaseError
|
420
470
|
|
421
471
|
end
|
422
472
|
|
423
|
-
it
|
424
|
-
record = {name:
|
473
|
+
it "has no problem with record values of nil" do
|
474
|
+
record = {name: "Ranger", price: nil}
|
425
475
|
expect{ interface.update(id, record) }.not_to raise_exception
|
426
476
|
expect( interface.read(id).to_h ).to include(record)
|
427
477
|
end
|
428
478
|
|
429
|
-
it
|
430
|
-
record = {name: %Q|T'Challa"
|
479
|
+
it "has no problem with strings containing special characters" do
|
480
|
+
record = {name: %Q|T'Challa"|, price: nil}
|
431
481
|
expect{ interface.update(id, record) }.not_to raise_exception
|
432
482
|
expect( interface.read(id).to_h ).to include(record)
|
433
483
|
end
|
434
484
|
|
435
|
-
it
|
485
|
+
it "has no problem with non-integer keys" do
|
436
486
|
fill_product_data(prod_interface)
|
437
487
|
expect{ prod_interface.update("foo", name: "baz") }.not_to raise_error
|
438
488
|
expect( prod_interface.read("foo").to_h[:name] ).to eq "baz"
|
439
489
|
end
|
440
490
|
|
441
|
-
|
442
|
-
|
443
|
-
|
491
|
+
it "calls ConnectionPool#client" do
|
492
|
+
expect( interface._connection ).to receive(:client).at_least(:once).and_call_original
|
493
|
+
record = {name: "Booboo", price: 99.99}
|
494
|
+
interface.update(id, record)
|
495
|
+
end
|
444
496
|
|
445
|
-
|
497
|
+
end # of #update
|
446
498
|
|
447
|
-
def list_contains(ifce, id)
|
448
|
-
ifce.list.find {|x| x[ifce.id_fld] == id }
|
449
|
-
end
|
450
499
|
|
500
|
+
describe "#delete" do
|
451
501
|
let(:id) { interface.list.first[:id] }
|
452
502
|
|
453
503
|
before { fill_data(interface) }
|
454
504
|
|
455
|
-
it
|
505
|
+
it "raises CantContinue if anything hinky happens with the id" do
|
456
506
|
expect{ interface.delete(:foo) }.to raise_exception CantContinue
|
457
507
|
expect{ interface.delete(99) }.to raise_exception CantContinue
|
458
508
|
end
|
459
509
|
|
460
|
-
it
|
510
|
+
it "makes the record at ID go away" do
|
461
511
|
expect( list_contains(interface, id) ).to be_truthy
|
462
512
|
interface.delete(id)
|
463
513
|
expect( list_contains(interface, id) ).to be_falsy
|
464
514
|
end
|
465
515
|
|
466
|
-
it
|
516
|
+
it "has no roblem with non-integer keys" do
|
467
517
|
fill_product_data(prod_interface)
|
468
518
|
expect( list_contains(prod_interface, "foo") ).to be_truthy
|
469
519
|
prod_interface.delete("foo")
|
470
520
|
expect( list_contains(prod_interface, "foo") ).to be_falsy
|
471
521
|
end
|
472
522
|
|
473
|
-
|
474
|
-
|
523
|
+
it "calls ConnectionPool#client" do
|
524
|
+
expect( interface._connection ).to receive(:client).at_least(:once).and_call_original
|
525
|
+
interface.delete(id)
|
526
|
+
end
|
475
527
|
|
528
|
+
end # of #delete
|
476
529
|
|
477
|
-
describe '#execute' do
|
478
530
|
|
479
|
-
|
531
|
+
describe "#execute" do
|
532
|
+
let(:sql) { "delete from customer where cast(price as numeric) < 2.0;" }
|
480
533
|
|
481
534
|
before { fill_data(interface) }
|
482
535
|
|
483
|
-
it
|
536
|
+
it "requires an SQL string" do
|
484
537
|
expect{ interface.execute }.to raise_exception ArgumentError
|
485
538
|
expect{ interface.execute(nil) }.to raise_exception ArgumentError
|
486
539
|
expect{ interface.execute(14) }.to raise_exception ArgumentError
|
487
540
|
end
|
488
541
|
|
489
|
-
it
|
490
|
-
expect{ interface.execute(
|
542
|
+
it "raises some sort of Pod4 error if it runs into problems" do
|
543
|
+
expect{ interface.execute("delete from not_a_table") }.
|
491
544
|
to raise_exception Pod4Error
|
492
545
|
|
493
546
|
end
|
494
547
|
|
495
|
-
it
|
548
|
+
it "executes the string" do
|
496
549
|
expect{ interface.execute(sql) }.not_to raise_exception
|
497
550
|
expect( interface.list.size ).to eq(@data.size - 1)
|
498
|
-
expect( interface.list.map{|r| r[:name] } ).not_to include
|
551
|
+
expect( interface.list.map{|r| r[:name] } ).not_to include "Barney"
|
499
552
|
end
|
500
553
|
|
501
|
-
|
502
|
-
|
554
|
+
it "calls ConnectionPool#client" do
|
555
|
+
expect( interface._connection ).to receive(:client).at_least(:once).and_call_original
|
556
|
+
interface.execute(sql)
|
557
|
+
end
|
503
558
|
|
559
|
+
end # of #execute
|
504
560
|
|
505
|
-
describe '#executep' do
|
506
561
|
|
507
|
-
|
562
|
+
describe "#executep" do
|
563
|
+
let(:sql) { "delete from customer where cast(price as numeric) < %s and name = %s;" }
|
508
564
|
|
509
565
|
before { fill_data(interface) }
|
510
566
|
|
511
|
-
it
|
567
|
+
it "requires an SQL string" do
|
512
568
|
expect{ interface.executep }.to raise_exception ArgumentError
|
513
569
|
expect{ interface.executep(nil) }.to raise_exception ArgumentError
|
514
570
|
expect{ interface.executep(14) }.to raise_exception ArgumentError
|
515
571
|
end
|
516
572
|
|
517
|
-
it
|
518
|
-
expect{ interface.executep(
|
573
|
+
it "raises some sort of Pod4 error if it runs into problems" do
|
574
|
+
expect{ interface.executep("delete from not_a_table where foo = %s", 12) }.
|
519
575
|
to raise_exception Pod4Error
|
520
576
|
|
521
577
|
end
|
522
578
|
|
523
|
-
it
|
524
|
-
expect{ interface.executep(sql, 12.0,
|
579
|
+
it "executes the string with the given parameters" do
|
580
|
+
expect{ interface.executep(sql, 12.0, "Barney") }.not_to raise_exception
|
525
581
|
expect( interface.list.size ).to eq(@data.size - 1)
|
526
|
-
expect( interface.list.map{|r| r[:name] } ).not_to include
|
582
|
+
expect( interface.list.map{|r| r[:name] } ).not_to include "Barney"
|
527
583
|
end
|
528
584
|
|
529
|
-
|
530
|
-
|
585
|
+
it "calls ConnectionPool#client" do
|
586
|
+
expect( interface._connection ).to receive(:client).at_least(:once).and_call_original
|
587
|
+
interface.executep(sql, 12.0, "Barney")
|
588
|
+
end
|
531
589
|
|
590
|
+
end # of #executep
|
532
591
|
|
533
|
-
describe '#select' do
|
534
592
|
|
593
|
+
describe "#select" do
|
535
594
|
before { fill_data(interface) }
|
536
595
|
|
537
|
-
it
|
596
|
+
it "requires an SQL string" do
|
538
597
|
expect{ interface.select }.to raise_exception ArgumentError
|
539
598
|
expect{ interface.select(nil) }.to raise_exception ArgumentError
|
540
599
|
expect{ interface.select(14) }.to raise_exception ArgumentError
|
541
600
|
end
|
542
601
|
|
543
|
-
it
|
544
|
-
expect{ interface.select(
|
602
|
+
it "raises some sort of Pod4 error if it runs into problems" do
|
603
|
+
expect{ interface.select("select * from not_a_table") }.
|
545
604
|
to raise_exception Pod4Error
|
546
605
|
|
547
606
|
end
|
548
607
|
|
549
|
-
it
|
550
|
-
sql1 =
|
551
|
-
sql2 =
|
608
|
+
it "returns the result of the sql" do
|
609
|
+
sql1 = "select name from customer where cast(price as numeric) < 2.0;"
|
610
|
+
sql2 = "select name from customer where cast(price as numeric) < 0.0;"
|
552
611
|
|
553
612
|
expect{ interface.select(sql1) }.not_to raise_exception
|
554
|
-
expect( interface.select(sql1) ).to eq( [{name:
|
613
|
+
expect( interface.select(sql1) ).to eq( [{name: "Barney"}] )
|
555
614
|
expect( interface.select(sql2) ).to eq( [] )
|
556
615
|
end
|
557
616
|
|
558
|
-
it
|
617
|
+
it "works if you pass a non-select" do
|
559
618
|
# By which I mean: still executes the SQL; returns []
|
560
|
-
sql =
|
619
|
+
sql = "delete from customer where cast(price as numeric) < 2.0;"
|
561
620
|
ret = interface.select(sql)
|
562
621
|
|
563
622
|
expect( interface.list.size ).to eq(@data.size - 1)
|
564
|
-
expect( interface.list.map{|r| r[:name] } ).not_to include
|
623
|
+
expect( interface.list.map{|r| r[:name] } ).not_to include "Barney"
|
565
624
|
expect( ret ).to eq( [] )
|
566
625
|
end
|
567
626
|
|
568
|
-
|
569
|
-
|
627
|
+
it "calls ConnectionPool#client" do
|
628
|
+
expect( interface._connection ).to receive(:client).at_least(:once).and_call_original
|
629
|
+
sql = "select name from customer;"
|
630
|
+
interface.select(sql)
|
631
|
+
end
|
570
632
|
|
633
|
+
end # of #select
|
571
634
|
|
572
|
-
describe '#selectp' do
|
573
635
|
|
636
|
+
describe "#selectp" do
|
574
637
|
before { fill_data(interface) }
|
575
638
|
|
576
|
-
it
|
639
|
+
it "requires an SQL string" do
|
577
640
|
expect{ interface.selectp }.to raise_exception ArgumentError
|
578
641
|
expect{ interface.selectp(nil) }.to raise_exception ArgumentError
|
579
642
|
expect{ interface.selectp(14) }.to raise_exception ArgumentError
|
580
643
|
end
|
581
644
|
|
582
|
-
it
|
583
|
-
expect{ interface.selectp(
|
645
|
+
it "raises some sort of Pod4 error if it runs into problems" do
|
646
|
+
expect{ interface.selectp("select * from not_a_table where thingy = %s", 12) }.
|
584
647
|
to raise_exception Pod4Error
|
585
648
|
|
586
649
|
end
|
587
650
|
|
588
|
-
it
|
589
|
-
sql =
|
651
|
+
it "returns the result of the sql" do
|
652
|
+
sql = "select name from customer where cast(price as numeric) < %s;"
|
590
653
|
|
591
654
|
expect{ interface.selectp(sql, 2.0) }.not_to raise_exception
|
592
|
-
expect( interface.selectp(sql, 2.0) ).to eq( [{name:
|
655
|
+
expect( interface.selectp(sql, 2.0) ).to eq( [{name: "Barney"}] )
|
593
656
|
expect( interface.selectp(sql, 0.0) ).to eq( [] )
|
594
657
|
end
|
595
658
|
|
596
|
-
it
|
597
|
-
sql =
|
659
|
+
it "works if you pass a non-select" do
|
660
|
+
sql = "delete from customer where cast(price as numeric) < %s;"
|
598
661
|
ret = interface.selectp(sql, 2.0)
|
599
662
|
|
600
663
|
expect( interface.list.size ).to eq(@data.size - 1)
|
601
|
-
expect( interface.list.map{|r| r[:name] } ).not_to include
|
664
|
+
expect( interface.list.map{|r| r[:name] } ).not_to include "Barney"
|
602
665
|
expect( ret ).to eq( [] )
|
603
666
|
end
|
604
667
|
|
668
|
+
it "calls ConnectionPool#client" do
|
669
|
+
expect( interface._connection ).to receive(:client).at_least(:once).and_call_original
|
670
|
+
sql = "select name from customer where cast(price as numeric) < %s;"
|
671
|
+
interface.selectp(sql, 2.0)
|
672
|
+
end
|
673
|
+
|
674
|
+
end # of #selectp
|
675
|
+
|
676
|
+
|
677
|
+
describe "#new_connection" do
|
678
|
+
|
679
|
+
it "returns a PG Client object" do
|
680
|
+
expect( interface.new_connection @connect_hash ).to be_a PG::Connection
|
681
|
+
end
|
682
|
+
|
683
|
+
end # of #new_connection
|
605
684
|
|
606
|
-
end
|
607
|
-
##
|
608
685
|
|
686
|
+
describe "#close_connection" do
|
609
687
|
|
688
|
+
it "closes the given PG Client object" do
|
689
|
+
client = interface.new_connection(@connect_hash)
|
690
|
+
|
691
|
+
expect( client ).to receive(:finish)
|
692
|
+
|
693
|
+
interface.close_connection(client)
|
694
|
+
end
|
695
|
+
|
696
|
+
end # of #close_connection
|
697
|
+
|
610
698
|
end
|
611
699
|
|