pod4 0.10.6 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -33,7 +33,10 @@ describe "(Model with Encryption)" do
|
|
33
33
|
encrypted_columns :name, :ailment, :prescription
|
34
34
|
set_key "dflkasdgklajndgn"
|
35
35
|
set_iv_column :nonce
|
36
|
-
|
36
|
+
|
37
|
+
ifce = NullInterface.new(:id, :nhs_no, :name, :ailment, :prescription, :nonce, [])
|
38
|
+
ifce.id_ai = false
|
39
|
+
set_interface ifce
|
37
40
|
end
|
38
41
|
end
|
39
42
|
|
@@ -44,7 +47,10 @@ describe "(Model with Encryption)" do
|
|
44
47
|
encrypted_columns :name, :ailment, :prescription
|
45
48
|
set_key "d"
|
46
49
|
set_iv_column :nonce
|
47
|
-
|
50
|
+
|
51
|
+
ifce = NullInterface.new(:id, :nhs_no, :name, :ailment, :prescription, :nonce, [])
|
52
|
+
ifce.id_ai = false
|
53
|
+
set_interface ifce
|
48
54
|
end
|
49
55
|
end
|
50
56
|
|
@@ -55,7 +61,10 @@ describe "(Model with Encryption)" do
|
|
55
61
|
encrypted_columns :name, :ailment, :prescription
|
56
62
|
set_key nil
|
57
63
|
set_iv_column :nonce
|
58
|
-
|
64
|
+
|
65
|
+
ifce = NullInterface.new(:id, :nhs_no, :name, :ailment, :prescription, :nonce, [])
|
66
|
+
ifce.id_ai = false
|
67
|
+
set_interface ifce
|
59
68
|
end
|
60
69
|
end
|
61
70
|
|
@@ -65,7 +74,10 @@ describe "(Model with Encryption)" do
|
|
65
74
|
attr_columns :id, :date, :heading, :text
|
66
75
|
encrypted_columns :heading, :text
|
67
76
|
set_key "dflkasdgklajndgn"
|
68
|
-
|
77
|
+
|
78
|
+
ifce = NullInterface.new(:id, :date, :heading, :text, [])
|
79
|
+
ifce.id_ai = false
|
80
|
+
set_interface ifce
|
69
81
|
end
|
70
82
|
end
|
71
83
|
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require "octothorpe"
|
2
|
+
require "bigdecimal"
|
3
|
+
|
4
|
+
require "pod4"
|
5
|
+
require "pod4/tweaking"
|
6
|
+
require "pod4/interface"
|
7
|
+
|
8
|
+
|
9
|
+
describe "(Model plus Tweaking)" do
|
10
|
+
|
11
|
+
# ##
|
12
|
+
# # I can't use NullInterface this time because I need to define a custom method on the interface.
|
13
|
+
# # But I'm _only_ calling that custom method, and there is nothing to say that the custom method
|
14
|
+
# # actually has to talk to a database...
|
15
|
+
# #
|
16
|
+
# let(:customer_interface_class) do
|
17
|
+
# Class.new Pod4::Interface do
|
18
|
+
#
|
19
|
+
# def my_list(*args)
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
|
24
|
+
let(:customer_model_class) do
|
25
|
+
Class.new Pod4::Model do
|
26
|
+
include Pod4::Tweaking
|
27
|
+
|
28
|
+
class MyInterface < Pod4::Interface
|
29
|
+
attr_writer :response
|
30
|
+
|
31
|
+
# stuff that lets us fake being an interface
|
32
|
+
def initialize; end
|
33
|
+
def id_fld; :id; end
|
34
|
+
|
35
|
+
def test; end # something we can use to test attr_columns method
|
36
|
+
def thing; end # there's already a method called this on the model
|
37
|
+
|
38
|
+
# main test method; responds with whatever we want it to
|
39
|
+
def my_list(*args)
|
40
|
+
@response
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
attr_columns :id, :code, :band
|
45
|
+
set_interface MyInterface.new
|
46
|
+
set_custom_list :my_list
|
47
|
+
|
48
|
+
def thing; end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
describe "Model.set_custom_list" do
|
55
|
+
|
56
|
+
it "requires the name of a method" do
|
57
|
+
expect{ customer_model_class.set_custom_list() }.to raise_error ArgumentError
|
58
|
+
expect{ customer_model_class.set_custom_list(:test) }.not_to raise_error
|
59
|
+
end
|
60
|
+
|
61
|
+
it "raises an ArgumentError if the method does not exist on the interface" do
|
62
|
+
expect{ customer_model_class.set_custom_list(:nope) }.to raise_error ArgumentError
|
63
|
+
end
|
64
|
+
|
65
|
+
it "raises an ArgumentError if the method already exists on the model" do
|
66
|
+
expect{ customer_model_class.set_custom_list(:thing) }.to raise_error ArgumentError
|
67
|
+
end
|
68
|
+
|
69
|
+
it "creates the corresponding method on the model class" do
|
70
|
+
expect( customer_model_class ).to respond_to :my_list
|
71
|
+
end
|
72
|
+
|
73
|
+
end # of Model.set_custom_list
|
74
|
+
|
75
|
+
|
76
|
+
describe "(custom method on model)" do
|
77
|
+
|
78
|
+
it "passes the arguments given it through to the interface method of the same name" do
|
79
|
+
expect( customer_model_class.interface )
|
80
|
+
.to receive(:my_list).with(:foo, 1, "bar")
|
81
|
+
.and_call_original
|
82
|
+
|
83
|
+
customer_model_class.interface.response = []
|
84
|
+
customer_model_class.my_list(:foo, 1, "bar")
|
85
|
+
end
|
86
|
+
|
87
|
+
it "raises Pod4Error if the return value is not an Array" do
|
88
|
+
customer_model_class.interface.response = :nope
|
89
|
+
expect{ customer_model_class.my_list(:foo) }.to raise_error(Pod4Error, /array/i)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "raises Pod4Error if the return value is not an Array of Octothorpe/Hash" do
|
93
|
+
customer_model_class.interface.response = [:nope]
|
94
|
+
expect{ customer_model_class.my_list }.to raise_error(Pod4Error, /hash|record/i)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "raises Pod4Error if any of the returned Octothorpe/Hashes are missing the ID field" do
|
98
|
+
customer_model_class.interface.response = [{id: 1, code: "one"}, {code: 2}]
|
99
|
+
expect{ customer_model_class.my_list }.to raise_error(Pod4Error, /ID/i)
|
100
|
+
end
|
101
|
+
|
102
|
+
it "returns an empty array if the interface method returns an empty array" do
|
103
|
+
customer_model_class.interface.response = []
|
104
|
+
expect{ customer_model_class.my_list }.not_to raise_error
|
105
|
+
expect( customer_model_class.my_list ).to eq([])
|
106
|
+
end
|
107
|
+
|
108
|
+
it "returns an array of model instances based on the results of the interface method" do
|
109
|
+
rows = [ {id: 1, code: "one"},
|
110
|
+
{id: 3, code: "three"},
|
111
|
+
{id: 5, code: "five", band: 12} ]
|
112
|
+
|
113
|
+
customer_model_class.interface.response = rows
|
114
|
+
list = customer_model_class.my_list
|
115
|
+
|
116
|
+
expect( list ).to be_an Array
|
117
|
+
expect( list ).to all( be_a customer_model_class )
|
118
|
+
expect( list.map(&:id) ).to match_array( rows.map{|x| x[:id]} )
|
119
|
+
expect( list.map(&:code) ).to match_array( rows.map{|x| x[:code]} )
|
120
|
+
expect( list.find{|x| x.id == 5}.band ).to eq 12
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
end # of (custom method on model)
|
125
|
+
|
126
|
+
|
127
|
+
end
|
128
|
+
|
@@ -6,14 +6,16 @@ require "pod4/typecasting"
|
|
6
6
|
require "pod4/null_interface"
|
7
7
|
|
8
8
|
|
9
|
-
describe "
|
9
|
+
describe "(Model plus typecasting)" do
|
10
10
|
|
11
11
|
let(:product_model_class) do
|
12
12
|
Class.new Pod4::Model do
|
13
13
|
include Pod4::TypeCasting
|
14
14
|
force_encoding Encoding::ISO_8859_1 # I assume we are running as UTF8 here
|
15
15
|
attr_columns :id, :code, :product, :price
|
16
|
-
|
16
|
+
ifce = NullInterface.new(:id, :code, :product, :price, [])
|
17
|
+
ifce.id_ai = false
|
18
|
+
set_interface ifce
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
@@ -28,8 +30,12 @@ describe "ProductModel" do
|
|
28
30
|
typecast :flag, as: :boolean
|
29
31
|
typecast :foo, use: :mycast, bar: 42
|
30
32
|
typecast :bar, as: Float
|
31
|
-
|
32
|
-
|
33
|
+
|
34
|
+
ifce = NullInterface.new( :id, :code, :band, :sales, :created, :yrstart,
|
35
|
+
:flag, :foo, :bar, [] )
|
36
|
+
|
37
|
+
ifce.id_ai = false
|
38
|
+
set_interface ifce
|
33
39
|
|
34
40
|
def mycast(value, opts); "blarg"; end
|
35
41
|
end
|
data/spec/common/model_spec.rb
CHANGED
@@ -1,39 +1,35 @@
|
|
1
|
-
require
|
1
|
+
require "octothorpe"
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "pod4/model"
|
4
|
+
require "pod4/null_interface"
|
5
5
|
|
6
6
|
|
7
|
-
describe
|
7
|
+
describe "Model" do
|
8
8
|
|
9
9
|
##
|
10
10
|
# We define a model class to test, since in normal operation we would never use Model directly,
|
11
11
|
# and since it needs an inner Interface.
|
12
12
|
#
|
13
|
-
# We define an inner class based on the genuinely existing, non-mock NullInterface class; and
|
14
|
-
# then define expectations on it. When we do this, Rspec fails to pass the call on to the object,
|
15
|
-
# unless we specifically say `.and_call_original` instead of `.and_return`.
|
16
|
-
#
|
17
|
-
# This is actually quite nice, but more than a little confusing when you see it for the first
|
18
|
-
# time. Its use isn't spelled out in the RSpec docs AFAICS.
|
19
|
-
#
|
20
|
-
# (Also, we define the class inside an Rspec 'let' so that its scope is limited to this test.)
|
21
|
-
#
|
22
13
|
let(:customer_model_class) do
|
23
14
|
Class.new Pod4::Model do
|
24
15
|
attr_columns :id, :name, :groups
|
25
16
|
attr_columns :price # specifically testing multiple calls to attr_columns
|
26
|
-
|
17
|
+
|
18
|
+
set_interface NullInterface.new(:id, :name, :price, :groups,
|
19
|
+
[ {id: 1, name: "Gomez", price: 1.23, groups: "trains" },
|
20
|
+
{id: 2, name: "Morticia", price: 2.34, groups: "spanish" },
|
21
|
+
{id: 3, name: "Wednesday", price: 3.45, groups: "school" },
|
22
|
+
{id: 4, name: "Pugsley", price: 4.56, groups: "trains,school"} ] )
|
27
23
|
|
28
24
|
def map_to_model(ot)
|
29
25
|
super
|
30
|
-
@groups = @groups ? @groups.split(
|
26
|
+
@groups = @groups ? @groups.split(",") : []
|
31
27
|
self
|
32
28
|
end
|
33
29
|
|
34
30
|
def map_to_interface
|
35
31
|
x = super
|
36
|
-
g = (x.>>.groups || []).join(
|
32
|
+
g = (x.>>.groups || []).join(",")
|
37
33
|
x.merge(groups: g)
|
38
34
|
end
|
39
35
|
|
@@ -41,7 +37,7 @@ describe 'CustomerModel' do
|
|
41
37
|
add_alert(*args) #private method
|
42
38
|
end
|
43
39
|
|
44
|
-
def validate
|
40
|
+
def validate(vmode)
|
45
41
|
add_alert(:error, "falling over now") if name == "fall over"
|
46
42
|
end
|
47
43
|
|
@@ -49,11 +45,36 @@ describe 'CustomerModel' do
|
|
49
45
|
end
|
50
46
|
end
|
51
47
|
|
48
|
+
# Here's a second model for a non-autoincrementing table
|
49
|
+
let(:product_model_class) do
|
50
|
+
Class.new Pod4::Model do
|
51
|
+
i = NullInterface.new(:code, :level, [{code: "foo", level: 1},
|
52
|
+
{code: "bar", level: 2}] )
|
53
|
+
|
54
|
+
i.id_ai = false
|
55
|
+
|
56
|
+
attr_columns :code, :level
|
57
|
+
set_interface i
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def without_groups(ot)
|
63
|
+
ot.to_h.reject {|k,_| k == :groups}
|
64
|
+
end
|
65
|
+
|
66
|
+
def arr_without_groups(arr)
|
67
|
+
arr
|
68
|
+
.map {|m| without_groups(m.to_ot) }
|
69
|
+
.flatten
|
70
|
+
|
71
|
+
end
|
72
|
+
|
52
73
|
let(:records) do
|
53
|
-
[ {id:
|
54
|
-
{id:
|
55
|
-
{id:
|
56
|
-
{id:
|
74
|
+
[ {id: 1, name: "Gomez", price: 1.23, groups: "trains" },
|
75
|
+
{id: 2, name: "Morticia", price: 2.34, groups: "spanish" },
|
76
|
+
{id: 3, name: "Wednesday", price: 3.45, groups: "school" },
|
77
|
+
{id: 4, name: "Pugsley", price: 4.56, groups: "trains,school"} ]
|
57
78
|
|
58
79
|
end
|
59
80
|
|
@@ -64,50 +85,34 @@ describe 'CustomerModel' do
|
|
64
85
|
let(:records_as_ot) { records.map{|r| Octothorpe.new(r) } }
|
65
86
|
let(:recordsx_as_ot) { recordsx.map{|r| Octothorpe.new(r) } }
|
66
87
|
|
67
|
-
def without_groups(ot)
|
68
|
-
ot.to_h.reject {|k,_| k == :groups}
|
69
|
-
end
|
70
|
-
|
71
88
|
# model is just a plain newly created object that you can call read on.
|
72
89
|
# model2 and model3 are in an identical state - they have been filled with a
|
73
|
-
# read(). We have two so that we can RSpec
|
90
|
+
# read(). We have two so that we can RSpec "allow" on one and not the other.
|
74
91
|
|
75
|
-
let(:model) { customer_model_class.new(
|
92
|
+
let(:model) { customer_model_class.new(2) }
|
76
93
|
|
77
94
|
let(:model2) do
|
78
|
-
m = customer_model_class.new(
|
95
|
+
m = customer_model_class.new(3)
|
79
96
|
|
80
|
-
allow( m.interface ).to receive(:read).and_return( Octothorpe.new(records[2]) )
|
97
|
+
#allow( m.interface ).to receive(:read).and_return( Octothorpe.new(records[2]) )
|
81
98
|
m.read.or_die
|
82
99
|
end
|
83
100
|
|
84
101
|
let(:model3) do
|
85
|
-
m = customer_model_class.new(
|
102
|
+
m = customer_model_class.new(4)
|
86
103
|
|
87
|
-
allow( m.interface ).to receive(:read).and_return( Octothorpe.new(records[3]) )
|
104
|
+
#allow( m.interface ).to receive(:read).and_return( Octothorpe.new(records[3]) )
|
88
105
|
m.read.or_die
|
89
106
|
end
|
90
107
|
|
91
|
-
# Model4 is for a non-integer id
|
92
|
-
let(:thing) { Octothorpe.new(id: 'eek', name: 'thing', price: 9.99, groups: 'scuttering') }
|
93
108
|
|
94
|
-
|
95
|
-
m = customer_model_class.new('eek')
|
109
|
+
describe "Model.attr_columns" do
|
96
110
|
|
97
|
-
|
98
|
-
m.read.or_die
|
99
|
-
end
|
100
|
-
|
101
|
-
##
|
102
|
-
|
103
|
-
|
104
|
-
describe 'Model.attr_columns' do
|
105
|
-
|
106
|
-
it 'requires a list of columns' do
|
111
|
+
it "requires a list of columns" do
|
107
112
|
expect( customer_model_class ).to respond_to(:attr_columns).with(1).argument
|
108
113
|
end
|
109
114
|
|
110
|
-
it
|
115
|
+
it "exposes the columns just like attr_accessor" do
|
111
116
|
expect( customer_model_class.new ).to respond_to(:id)
|
112
117
|
expect( customer_model_class.new ).to respond_to(:name)
|
113
118
|
expect( customer_model_class.new ).to respond_to(:price)
|
@@ -120,198 +125,168 @@ describe 'CustomerModel' do
|
|
120
125
|
|
121
126
|
# it adds the columns to Model.columns -- covered by the columns test
|
122
127
|
end
|
123
|
-
##
|
124
128
|
|
125
129
|
|
126
|
-
describe
|
127
|
-
|
130
|
+
describe "Model.columns" do
|
131
|
+
|
132
|
+
it "lists the columns" do
|
128
133
|
expect( customer_model_class.columns ).to match_array( [:id,:name,:price,:groups] )
|
129
134
|
end
|
135
|
+
|
130
136
|
end
|
131
|
-
##
|
132
137
|
|
133
138
|
|
134
|
-
describe
|
135
|
-
|
139
|
+
describe "Model.set_interface" do
|
140
|
+
|
141
|
+
it "requires an Interface object" do
|
136
142
|
expect( customer_model_class ).to respond_to(:set_interface).with(1).argument
|
137
143
|
end
|
138
144
|
|
139
|
-
# it
|
145
|
+
# it "sets interface" - covered by the interface test
|
140
146
|
end
|
141
|
-
##
|
142
147
|
|
143
148
|
|
144
|
-
describe
|
145
|
-
|
149
|
+
describe "Model.interface" do
|
150
|
+
|
151
|
+
it "is the interface object" do
|
146
152
|
expect( customer_model_class.interface ).to be_a_kind_of NullInterface
|
147
153
|
expect( customer_model_class.interface.id_fld ).to eq :id
|
148
154
|
end
|
149
|
-
end
|
150
|
-
##
|
151
155
|
|
156
|
+
end
|
152
157
|
|
153
|
-
describe 'Model.list' do
|
154
158
|
|
159
|
+
describe "Model.list" do
|
155
160
|
let(:list1) { customer_model_class.list }
|
156
161
|
|
157
|
-
|
158
|
-
arr
|
159
|
-
.map {|m| without_groups(m.to_ot) }
|
160
|
-
.flatten
|
161
|
-
|
162
|
-
end
|
163
|
-
|
164
|
-
it 'allows an optional selection parameter' do
|
162
|
+
it "allows an optional selection parameter" do
|
165
163
|
expect{ customer_model_class.list }.not_to raise_exception
|
166
|
-
expect{ customer_model_class.list(name:
|
164
|
+
expect{ customer_model_class.list(name: "Betty") }.not_to raise_exception
|
167
165
|
end
|
168
166
|
|
169
|
-
it
|
170
|
-
expect( customer_model_class.interface ).
|
171
|
-
to receive(:list).with(nil).
|
172
|
-
and_return( records_as_ot )
|
173
|
-
|
167
|
+
it "returns an array of customer_model_class records" do
|
174
168
|
expect( list1 ).to be_a_kind_of Array
|
175
169
|
expect( list1 ).to all(be_a_kind_of customer_model_class)
|
176
170
|
end
|
177
171
|
|
178
|
-
it
|
179
|
-
expect( customer_model_class.interface ).
|
180
|
-
to receive(:list).with(nil).
|
181
|
-
and_return(records_as_ot)
|
182
|
-
|
172
|
+
it "returns the data from the interface" do
|
183
173
|
expect( list1.size ).to eq records.size
|
184
174
|
expect( arr_without_groups(list1) ).to include( *recordsx )
|
185
175
|
end
|
186
176
|
|
187
|
-
it
|
188
|
-
|
189
|
-
|
190
|
-
expect(
|
191
|
-
to receive(:list).with(hash).
|
192
|
-
and_return( [Octothorpe.new(records[1])] )
|
193
|
-
|
194
|
-
list2 = customer_model_class.list(hash)
|
195
|
-
expect( list2.size ).to eq 1
|
196
|
-
expect( arr_without_groups(list2).first ).to eq( recordsx[1] )
|
177
|
+
it "honours passed selection criteria" do
|
178
|
+
list = customer_model_class.list(price: 2.34)
|
179
|
+
expect( list.size ).to eq 1
|
180
|
+
expect( arr_without_groups(list).first ).to eq( recordsx[1] )
|
197
181
|
end
|
198
182
|
|
199
|
-
it
|
200
|
-
|
201
|
-
|
202
|
-
expect( customer_model_class.interface ).
|
203
|
-
to receive(:list).with(hash).
|
204
|
-
and_return([])
|
205
|
-
|
206
|
-
expect( customer_model_class.list(hash) ).to eq []
|
183
|
+
it "returns an empty array if nothing matches" do
|
184
|
+
expect( customer_model_class.list(price: 3.21) ).to eq []
|
207
185
|
end
|
208
186
|
|
209
|
-
it
|
187
|
+
it "returns an empty array if there are no records" do
|
188
|
+
customer_model_class.list.each{|r| r.read; r.delete}
|
210
189
|
expect( customer_model_class.list ).to eq []
|
211
190
|
end
|
212
191
|
|
213
|
-
it
|
214
|
-
|
215
|
-
|
216
|
-
and_return(records_as_ot)
|
217
|
-
|
218
|
-
expect( customer_model_class.list.last.groups ).to eq(['trains', 'school'])
|
192
|
+
it "calls map_to_model to set the record data" do
|
193
|
+
# groups is an array because we coded it to represent that way in the model above.
|
194
|
+
expect( customer_model_class.list.last.groups ).to eq(["trains", "school"])
|
219
195
|
end
|
220
196
|
|
221
|
-
end
|
222
|
-
##
|
197
|
+
end # of Model.list
|
223
198
|
|
224
199
|
|
225
|
-
describe
|
200
|
+
describe "#new" do
|
226
201
|
|
227
|
-
it
|
202
|
+
it "takes an optional ID" do
|
228
203
|
expect{ customer_model_class.new }.not_to raise_exception
|
229
204
|
expect{ customer_model_class.new(1) }.not_to raise_exception
|
230
205
|
end
|
231
206
|
|
232
|
-
it
|
207
|
+
it "sets the ID attribute" do
|
233
208
|
expect( customer_model_class.new(23).model_id ).to eq 23
|
234
209
|
end
|
235
210
|
|
236
|
-
it
|
237
|
-
expect( customer_model_class.new.model_status ).to eq :
|
211
|
+
it "sets the status to unknown" do
|
212
|
+
expect( customer_model_class.new.model_status ).to eq :unknown
|
238
213
|
end
|
239
214
|
|
240
|
-
it
|
215
|
+
it "initializes the alerts attribute" do
|
241
216
|
expect( customer_model_class.new.alerts ).to eq([])
|
242
217
|
end
|
243
218
|
|
244
|
-
it
|
245
|
-
expect{
|
246
|
-
expect(
|
219
|
+
it "doesn't freak out if the non-autoincrementing ID is not an integer" do
|
220
|
+
expect{ product_model_class.new("france") }.not_to raise_exception
|
221
|
+
expect( product_model_class.new("france").model_id ).to eq "france"
|
247
222
|
end
|
248
223
|
|
249
|
-
end
|
250
|
-
##
|
224
|
+
end # of #new
|
251
225
|
|
252
226
|
|
253
|
-
describe
|
254
|
-
|
227
|
+
describe "#interface" do
|
228
|
+
|
229
|
+
it "returns the interface set in the class definition, again" do
|
255
230
|
expect( customer_model_class.new.interface ).to be_a_kind_of NullInterface
|
256
231
|
expect( customer_model_class.new.interface.id_fld ).to eq :id
|
257
232
|
end
|
258
|
-
|
259
|
-
|
233
|
+
|
234
|
+
end # of #interface
|
260
235
|
|
261
236
|
|
262
|
-
describe
|
263
|
-
it 'returns the attr_columns list from the class definition' do
|
237
|
+
describe "#columns" do
|
264
238
|
|
239
|
+
it "returns the attr_columns list from the class definition" do
|
265
240
|
expect( customer_model_class.new.columns ).
|
266
241
|
to match_array( [:id,:name,:price,:groups] )
|
267
242
|
|
268
243
|
end
|
269
|
-
end
|
270
|
-
##
|
271
244
|
|
245
|
+
end # of #columns
|
272
246
|
|
273
|
-
|
274
|
-
|
247
|
+
|
248
|
+
describe "#alerts" do
|
249
|
+
|
250
|
+
it "returns the list of alerts against the model" do
|
275
251
|
cm = customer_model_class.new
|
276
|
-
cm.fake_an_alert(:warning, :foo,
|
277
|
-
cm.fake_an_alert(:error, :bar,
|
252
|
+
cm.fake_an_alert(:warning, :foo, "one")
|
253
|
+
cm.fake_an_alert(:error, :bar, "two")
|
278
254
|
|
279
255
|
expect( cm.alerts.size ).to eq 2
|
280
256
|
expect( cm.alerts.map{|a| a.message} ).to match_array(%w|one two|)
|
281
257
|
end
|
282
|
-
end
|
283
|
-
##
|
284
258
|
|
259
|
+
end # of #alerts
|
285
260
|
|
286
|
-
describe '#add_alert' do
|
287
|
-
# add_alert is a protected method, which is only supposed to be called
|
288
|
-
# within the validate method of a subclass of Method. So we test it by
|
289
|
-
# calling our alert faking method
|
290
261
|
|
291
|
-
|
262
|
+
describe "#add_alert" do
|
263
|
+
# add_alert is a private method, which is only supposed to be called within a subclass of
|
264
|
+
# Model. So we test it by calling our alert faking method
|
265
|
+
|
266
|
+
it "requires type, message or type, field, message" do
|
292
267
|
expect{ model.fake_an_alert }.to raise_exception ArgumentError
|
293
268
|
expect{ model.fake_an_alert(nil) }.to raise_exception ArgumentError
|
294
|
-
expect{ model.fake_an_alert(
|
269
|
+
expect{ model.fake_an_alert("foo") }.to raise_exception ArgumentError
|
295
270
|
|
296
|
-
expect{ model.fake_an_alert(:error,
|
297
|
-
expect{ model.fake_an_alert(:warning, :name,
|
271
|
+
expect{ model.fake_an_alert(:error, "foo") }.not_to raise_exception
|
272
|
+
expect{ model.fake_an_alert(:warning, :name, "bar") }.
|
298
273
|
not_to raise_exception
|
299
274
|
|
300
275
|
end
|
301
276
|
|
302
|
-
it
|
277
|
+
it "only allows valid types" do
|
303
278
|
[:brian, :werning, nil, :alert, :danger].each do |l|
|
304
|
-
expect{ model.fake_an_alert(l,
|
279
|
+
expect{ model.fake_an_alert(l, "foo") }.to raise_exception ArgumentError
|
305
280
|
end
|
306
281
|
|
307
282
|
[:warning, :error, :success, :info].each do |l|
|
308
|
-
expect{ model.fake_an_alert(l,
|
283
|
+
expect{ model.fake_an_alert(l, "foo") }.not_to raise_exception
|
309
284
|
end
|
310
285
|
|
311
286
|
end
|
312
287
|
|
313
|
-
it
|
314
|
-
lurch =
|
288
|
+
it "creates an Alert and adds it to @alerts" do
|
289
|
+
lurch = "Dnhhhhhh"
|
315
290
|
model.fake_an_alert(:error, :price, lurch)
|
316
291
|
|
317
292
|
expect( model.alerts.size ).to eq 1
|
@@ -319,39 +294,37 @@ describe 'CustomerModel' do
|
|
319
294
|
expect( model.alerts.first.message ).to eq lurch
|
320
295
|
end
|
321
296
|
|
322
|
-
it
|
323
|
-
model.fake_an_alert(:warning, :price,
|
297
|
+
it "sets @model_status if the type is worse than @model_status" do
|
298
|
+
model.fake_an_alert(:warning, :price, "xoo")
|
324
299
|
expect( model.model_status ).to eq :warning
|
325
300
|
|
326
|
-
model.fake_an_alert(:success, :price,
|
301
|
+
model.fake_an_alert(:success, :price, "flom")
|
327
302
|
expect( model.model_status ).to eq :warning
|
328
303
|
|
329
|
-
model.fake_an_alert(:info, :price,
|
304
|
+
model.fake_an_alert(:info, :price, "flom")
|
330
305
|
expect( model.model_status ).to eq :warning
|
331
306
|
|
332
|
-
model.fake_an_alert(:error, :price,
|
307
|
+
model.fake_an_alert(:error, :price, "qar")
|
333
308
|
expect( model.model_status ).to eq :error
|
334
309
|
|
335
|
-
model.fake_an_alert(:warning, :price,
|
310
|
+
model.fake_an_alert(:warning, :price, "drazq")
|
336
311
|
expect( model.model_status ).to eq :error
|
337
312
|
end
|
338
313
|
|
339
|
-
it
|
340
|
-
lurch =
|
314
|
+
it "ignores a new alert if identical to an existing one" do
|
315
|
+
lurch = "Dnhhhhhh"
|
341
316
|
2.times { model.fake_an_alert(:error, :price, lurch) }
|
342
317
|
|
343
318
|
expect( model.alerts.size ).to eq 1
|
344
319
|
end
|
345
320
|
|
346
|
-
end
|
347
|
-
##
|
348
|
-
|
321
|
+
end # of #add_alert
|
349
322
|
|
350
|
-
describe '#set' do
|
351
323
|
|
324
|
+
describe "#set" do
|
352
325
|
let (:ot) { records_as_ot[3] }
|
353
326
|
|
354
|
-
it
|
327
|
+
it "takes an Octothorpe or a Hash" do
|
355
328
|
expect{ model.set }.to raise_exception ArgumentError
|
356
329
|
expect{ model.set(nil) }.to raise_exception ArgumentError
|
357
330
|
expect{ model.set(:foo) }.to raise_exception ArgumentError
|
@@ -359,11 +332,11 @@ describe 'CustomerModel' do
|
|
359
332
|
expect{ model.set(ot) }.not_to raise_exception
|
360
333
|
end
|
361
334
|
|
362
|
-
it
|
335
|
+
it "returns self" do
|
363
336
|
expect( model.set(ot) ).to eq model
|
364
337
|
end
|
365
338
|
|
366
|
-
it
|
339
|
+
it "sets the attribute columns from the hash" do
|
367
340
|
model.set(ot)
|
368
341
|
|
369
342
|
expect( model.id ).to eq ot.>>.id
|
@@ -371,270 +344,244 @@ describe 'CustomerModel' do
|
|
371
344
|
expect( model.price ).to eq ot.>>.price
|
372
345
|
end
|
373
346
|
|
374
|
-
it
|
375
|
-
otx = Octothorpe.new(name:
|
347
|
+
it "only sets the attributes on the model that it is given" do
|
348
|
+
otx = Octothorpe.new(name: "Piggy", price: 98.76, weapon: "rake")
|
376
349
|
|
377
350
|
expect{ model3.set(otx) }.not_to raise_exception
|
378
|
-
expect( model3.id ).to eq
|
379
|
-
expect( model3.name ).to eq
|
351
|
+
expect( model3.id ).to eq 4
|
352
|
+
expect( model3.name ).to eq "Piggy"
|
380
353
|
expect( model3.price ).to eq 98.76
|
381
|
-
expect( model3.groups ).to eq( ot.>>.groups.split(
|
354
|
+
expect( model3.groups ).to eq( ot.>>.groups.split(",") )
|
382
355
|
end
|
383
356
|
|
384
|
-
end
|
385
|
-
##
|
357
|
+
end # of #set
|
386
358
|
|
387
359
|
|
388
|
-
describe
|
389
|
-
it 'returns an Octothorpe made of the attribute columns' do
|
390
|
-
expect( model.to_ot ).to be_a_kind_of Octothorpe
|
360
|
+
describe "#to_ot" do
|
391
361
|
|
392
|
-
|
393
|
-
|
362
|
+
it "returns an Octothorpe made of the attribute columns, including the ID field" do
|
363
|
+
m1 = customer_model_class.new
|
364
|
+
expect( m1.to_ot ).to be_a_kind_of Octothorpe
|
365
|
+
expect( m1.to_ot.to_h ).to eq( {id: nil, name: nil, price:nil, groups:nil} )
|
394
366
|
|
395
|
-
|
396
|
-
|
397
|
-
expect(
|
367
|
+
m2 = customer_model_class.new(1)
|
368
|
+
m2.read
|
369
|
+
expect( m2.to_ot ).to be_a_kind_of Octothorpe
|
370
|
+
expect( without_groups(m2.to_ot) ).to eq recordsx[0]
|
398
371
|
|
399
|
-
|
400
|
-
|
401
|
-
expect(
|
372
|
+
m3 = customer_model_class.new(2)
|
373
|
+
m3.read
|
374
|
+
expect( m3.to_ot ).to be_a_kind_of Octothorpe
|
375
|
+
expect( without_groups(m3.to_ot) ).to eq recordsx[1]
|
402
376
|
end
|
403
|
-
|
404
|
-
|
377
|
+
|
378
|
+
end # of #to_ot
|
405
379
|
|
406
380
|
|
407
|
-
describe
|
381
|
+
describe "#map_to_model" do
|
382
|
+
|
383
|
+
it "sets the columns, with groups as an array" do
|
384
|
+
# testing the custom typecasting in customer_model_class
|
408
385
|
|
409
|
-
it 'sets the columns, with groups as an array' do
|
410
386
|
cm = customer_model_class.new
|
411
387
|
cm.map_to_model(records.last)
|
412
388
|
|
413
|
-
expect( cm.groups ).to eq( [
|
389
|
+
expect( cm.groups ).to eq( ["trains","school"] )
|
414
390
|
end
|
415
391
|
|
416
|
-
end
|
417
|
-
|
392
|
+
end # of #map_to_model
|
393
|
+
|
418
394
|
|
395
|
+
describe "#map_to_interface" do
|
419
396
|
|
420
|
-
|
397
|
+
it "returns the columns, with groups as a list" do
|
398
|
+
# testing the custom typecasting in customer_model_class
|
421
399
|
|
422
|
-
it 'returns the columns, with groups as a list' do
|
423
400
|
cm = customer_model_class.new
|
424
401
|
cm.map_to_model(records.last)
|
425
402
|
|
426
|
-
expect( cm.map_to_interface
|
403
|
+
expect( cm.map_to_interface ).to be_an Octothorpe
|
404
|
+
expect( cm.map_to_interface.>>.groups ).to eq( "trains,school" )
|
427
405
|
end
|
428
406
|
|
429
|
-
end
|
430
|
-
##
|
407
|
+
end # of #map_to_interface
|
431
408
|
|
432
409
|
|
433
|
-
describe
|
410
|
+
describe "#raise_exceptions" do
|
434
411
|
|
435
|
-
it
|
412
|
+
it "is also known as .or_die" do
|
436
413
|
cm = customer_model_class.new
|
437
414
|
expect( cm.method(:raise_exceptions) ).to eq( cm.method(:or_die) )
|
438
415
|
end
|
439
416
|
|
440
|
-
it
|
441
|
-
model.fake_an_alert(:error, :price,
|
417
|
+
it "raises ValidationError if model status is :error" do
|
418
|
+
model.fake_an_alert(:error, :price, "qar")
|
442
419
|
expect{ model.raise_exceptions }.to raise_exception Pod4::ValidationError
|
443
420
|
end
|
444
421
|
|
445
|
-
it
|
422
|
+
it "does nothing if model status is not :error" do
|
446
423
|
expect{ model.raise_exceptions }.not_to raise_exception
|
447
424
|
|
448
|
-
model.fake_an_alert(:info, :price,
|
425
|
+
model.fake_an_alert(:info, :price, "qar")
|
449
426
|
expect{ model.raise_exceptions }.not_to raise_exception
|
450
427
|
|
451
|
-
model.fake_an_alert(:success, :price,
|
428
|
+
model.fake_an_alert(:success, :price, "qar")
|
452
429
|
expect{ model.raise_exceptions }.not_to raise_exception
|
453
430
|
|
454
|
-
model.fake_an_alert(:warning, :price,
|
431
|
+
model.fake_an_alert(:warning, :price, "qar")
|
455
432
|
expect{ model.raise_exceptions }.not_to raise_exception
|
456
433
|
end
|
457
434
|
|
458
|
-
end
|
459
|
-
##
|
460
|
-
|
435
|
+
end # of #raise_exceptions
|
461
436
|
|
462
|
-
describe '#create' do
|
463
437
|
|
438
|
+
describe "#create" do
|
464
439
|
let (:new_model) { customer_model_class.new }
|
465
440
|
|
466
|
-
it
|
441
|
+
it "takes no parameters" do
|
467
442
|
expect{ customer_model_class.new.create(12) }.to raise_exception ArgumentError
|
468
443
|
expect{ customer_model_class.new.create }.not_to raise_exception
|
469
444
|
end
|
470
445
|
|
471
|
-
it
|
446
|
+
it "returns self" do
|
472
447
|
expect( new_model.create ).to eq new_model
|
473
448
|
end
|
474
449
|
|
475
|
-
it
|
476
|
-
|
477
|
-
# `expect( new_model ).to receive(:validate)`
|
450
|
+
it "calls validate and passes the parameter" do
|
451
|
+
expect( new_model ).to receive(:validate).with(:create)
|
478
452
|
|
479
|
-
|
480
|
-
|
481
|
-
m.create
|
482
|
-
expect( m.model_status ).to eq :error
|
453
|
+
new_model.name = "foo"
|
454
|
+
new_model.create
|
483
455
|
end
|
484
456
|
|
485
|
-
it
|
457
|
+
it "calls create on the interface if the record is good" do
|
486
458
|
expect( customer_model_class.interface ).to receive(:create)
|
487
459
|
customer_model_class.new.create
|
488
460
|
|
489
|
-
new_model.fake_an_alert(:warning, :name,
|
461
|
+
new_model.fake_an_alert(:warning, :name, "foo")
|
490
462
|
expect( new_model.interface ).to receive(:create)
|
491
463
|
new_model.create
|
492
464
|
end
|
493
465
|
|
494
|
-
|
495
|
-
|
496
|
-
new_model.fake_an_alert(:error, :name, 'foo')
|
466
|
+
it "doesn't call create on the interface if the record is bad" do
|
467
|
+
new_model.fake_an_alert(:error, :name, "foo")
|
497
468
|
expect( new_model.interface ).not_to receive(:create)
|
498
469
|
new_model.create
|
499
470
|
end
|
500
471
|
|
501
|
-
it
|
502
|
-
new_model.id =
|
503
|
-
new_model.name = "Lurch"
|
504
|
-
new_model.create
|
505
|
-
|
506
|
-
expect( new_model.model_id ).to eq 50
|
507
|
-
end
|
508
|
-
|
509
|
-
it 'sets model status to :okay if it was :empty' do
|
510
|
-
new_model.id = 50
|
472
|
+
it "sets model status to :okay if it was :unknown" do
|
473
|
+
new_model.id = 5
|
511
474
|
new_model.name = "Lurch"
|
512
475
|
new_model.create
|
513
476
|
|
514
477
|
expect( new_model.model_status ).to eq :okay
|
515
478
|
end
|
516
479
|
|
517
|
-
it
|
518
|
-
new_model.id =
|
480
|
+
it "leaves the model status alone if it was not :unknown" do
|
481
|
+
new_model.id = 5
|
519
482
|
new_model.name = "Lurch"
|
520
483
|
new_model.create
|
521
484
|
|
522
|
-
new_model.fake_an_alert(:warning, :price,
|
485
|
+
new_model.fake_an_alert(:warning, :price, "qar")
|
523
486
|
expect( new_model.model_status ).to eq :warning
|
524
487
|
end
|
525
488
|
|
526
|
-
it
|
527
|
-
|
528
|
-
expect( new_model ).to receive(:map_to_interface)
|
489
|
+
it "calls map_to_interface to get record data" do
|
490
|
+
m = customer_model_class.new
|
529
491
|
|
530
|
-
|
531
|
-
new_model.name = "Lurch"
|
532
|
-
new_model.create
|
533
|
-
end
|
492
|
+
expect( m ).to receive(:map_to_interface).and_call_original
|
534
493
|
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
494
|
+
m.id = 5
|
495
|
+
m.name = "Lurch"
|
496
|
+
m.create
|
497
|
+
end
|
539
498
|
|
540
|
-
|
499
|
+
it "doesn't freak out if the ID field (non-autoincrementing) is not an integer" do
|
500
|
+
m = product_model_class.new("baz").read
|
501
|
+
m.level = 99
|
502
|
+
expect{ m.create }.not_to raise_error
|
541
503
|
end
|
542
504
|
|
543
505
|
it "creates an alert instead when the interface raises WeakError" do
|
544
506
|
allow( new_model.interface ).to receive(:create).and_raise Pod4::WeakError, "foo"
|
545
507
|
|
546
|
-
new_model.id =
|
508
|
+
new_model.id = 5
|
547
509
|
new_model.name = "Lurch"
|
548
510
|
expect{ new_model.create }.not_to raise_exception
|
549
511
|
expect( new_model.model_status ).to eq :error
|
550
512
|
expect( new_model.alerts.map(&:message) ).to include( include "foo" )
|
551
513
|
end
|
552
514
|
|
553
|
-
|
554
|
-
|
515
|
+
it "updates @model_id" do
|
516
|
+
m = customer_model_class.new
|
517
|
+
m.id = 5
|
518
|
+
m.name = "Lurch"
|
519
|
+
m.create
|
520
|
+
|
521
|
+
expect( m.model_id ).to eq 5
|
522
|
+
end
|
523
|
+
|
524
|
+
end # of #create
|
555
525
|
|
556
526
|
|
557
|
-
describe
|
527
|
+
describe "#read" do
|
558
528
|
|
559
|
-
it
|
529
|
+
it "takes no parameters" do
|
560
530
|
expect{ customer_model_class.new.create(12) }.to raise_exception ArgumentError
|
561
531
|
expect{ customer_model_class.new.create }.not_to raise_exception
|
562
532
|
end
|
563
533
|
|
564
|
-
it
|
565
|
-
allow( model.interface ).
|
566
|
-
to receive(:read).
|
567
|
-
and_return( records_as_ot.first )
|
568
|
-
|
534
|
+
it "returns self" do
|
569
535
|
expect( model.read ).to eq model
|
570
536
|
end
|
571
537
|
|
572
|
-
it
|
573
|
-
expect( model.interface ).
|
574
|
-
to receive(:read).
|
575
|
-
and_return( records_as_ot.first )
|
576
|
-
|
538
|
+
it "calls read on the interface" do
|
539
|
+
expect( model.interface ).to receive(:read).with(2).and_call_original
|
577
540
|
model.read
|
578
541
|
end
|
579
542
|
|
580
|
-
it
|
581
|
-
|
582
|
-
|
583
|
-
allow( model.interface ).
|
584
|
-
to receive(:read).
|
585
|
-
and_return( records_as_ot.first.merge(name: "fall over") )
|
586
|
-
|
543
|
+
it "calls validate and passes the parameter" do
|
544
|
+
expect( model ).to receive(:validate).with(:read)
|
587
545
|
model.read
|
588
|
-
expect( model.model_status ).to eq :error
|
589
546
|
end
|
590
547
|
|
591
|
-
it
|
548
|
+
it "sets the attribute columns using map_to_model" do
|
592
549
|
ot = records_as_ot.last
|
593
|
-
|
594
|
-
|
595
|
-
cm = customer_model_class.new(10).read
|
596
|
-
expect( cm.id ).to eq ot.>>.id
|
550
|
+
cm = customer_model_class.new(4).read
|
597
551
|
expect( cm.name ).to eq ot.>>.name
|
598
552
|
expect( cm.price ).to eq ot.>>.price
|
599
553
|
expect( cm.groups ).to be_a_kind_of(Array)
|
600
|
-
expect( cm.groups ).to eq( ot.>>.groups.split(
|
554
|
+
expect( cm.groups ).to eq( ot.>>.groups.split(",") )
|
601
555
|
end
|
602
556
|
|
603
|
-
it
|
557
|
+
it "sets model status to :okay if it was :unknown" do
|
604
558
|
ot = records_as_ot.last
|
605
|
-
allow( model.interface ).to receive(:read).and_return( ot )
|
606
|
-
|
607
559
|
model.read
|
608
560
|
expect( model.model_status ).to eq :okay
|
609
561
|
end
|
610
562
|
|
611
|
-
it
|
563
|
+
it "leaves the model status alone if it was not :unknown" do
|
612
564
|
ot = records_as_ot.last
|
613
|
-
|
614
|
-
|
615
|
-
model.fake_an_alert(:warning, :price, 'qar')
|
565
|
+
model.fake_an_alert(:warning, :price, "qar")
|
616
566
|
model.read
|
617
567
|
expect( model.model_status ).to eq :warning
|
618
568
|
end
|
619
569
|
|
620
|
-
it
|
621
|
-
|
622
|
-
|
623
|
-
expect{ customer_model_class.new('eek').read }.not_to raise_error
|
570
|
+
it "doesn't freak out if the (non-autoincrementing) ID is non-integer" do
|
571
|
+
expect{ product_model_class.new("foo").read }.not_to raise_error
|
624
572
|
end
|
625
573
|
|
626
|
-
context
|
574
|
+
context "if the interface.read returns an empty Octothorpe" do
|
627
575
|
let(:missing) { customer_model_class.new(99) }
|
628
576
|
|
629
|
-
it
|
577
|
+
it "doesn't throw an exception" do
|
630
578
|
expect{ missing.read }.not_to raise_exception
|
631
579
|
end
|
632
580
|
|
633
|
-
it
|
581
|
+
it "raises an error alert" do
|
634
582
|
expect( missing.read.model_status ).to eq :error
|
635
583
|
expect( missing.read.alerts.first.type ).to eq :error
|
636
584
|
end
|
637
|
-
|
638
585
|
end
|
639
586
|
|
640
587
|
it "creates an alert instead when the interface raises WeakError" do
|
@@ -645,86 +592,74 @@ describe 'CustomerModel' do
|
|
645
592
|
expect( model.alerts.map(&:message) ).to include( include "foo" )
|
646
593
|
end
|
647
594
|
|
648
|
-
|
649
|
-
|
595
|
+
it "updates @model_id" do
|
596
|
+
foo = product_model_class.new("foo")
|
597
|
+
foo.read
|
650
598
|
|
599
|
+
expect( foo.model_id ).to eq "foo"
|
600
|
+
end
|
651
601
|
|
652
|
-
|
602
|
+
end # of #read
|
653
603
|
|
654
|
-
before do
|
655
|
-
allow( model2.interface ).
|
656
|
-
to receive(:update).
|
657
|
-
and_return( model2.interface )
|
658
604
|
|
659
|
-
|
605
|
+
describe "#update" do
|
660
606
|
|
661
|
-
it
|
607
|
+
it "takes no parameters" do
|
662
608
|
expect{ model2.update(12) }.to raise_exception ArgumentError
|
663
609
|
expect{ model2.update }.not_to raise_exception
|
664
610
|
end
|
665
611
|
|
666
|
-
it
|
612
|
+
it "returns self" do
|
667
613
|
expect( model2.update ).to eq model2
|
668
614
|
end
|
669
615
|
|
670
|
-
it
|
671
|
-
|
672
|
-
|
673
|
-
expect( model.model_status ).to eq :empty
|
616
|
+
it "raises a Pod4Error if model status is :unknown" do
|
617
|
+
expect( model.model_status ).to eq :unknown
|
674
618
|
expect{ model.update }.to raise_exception Pod4::Pod4Error
|
675
619
|
end
|
676
620
|
|
677
|
-
it
|
621
|
+
it "raises a Pod4Error if model status is :deleted" do
|
678
622
|
model2.delete
|
679
623
|
expect{ model2.update }.to raise_exception Pod4::Pod4Error
|
680
624
|
end
|
681
625
|
|
682
|
-
it
|
683
|
-
|
684
|
-
|
626
|
+
it "calls validate and passes the parameter" do
|
627
|
+
expect( model2 ).to receive(:validate).with(:update)
|
628
|
+
|
629
|
+
model2.name = "foo"
|
685
630
|
model2.update
|
686
|
-
expect( model2.model_status ).to eq :error
|
687
631
|
end
|
688
632
|
|
689
|
-
it
|
690
|
-
expect( model3.interface ).
|
691
|
-
to receive(:update).
|
692
|
-
and_return( model3.interface )
|
633
|
+
it "calls update on the interface if the validation passes" do
|
634
|
+
expect( model3.interface ).to receive(:update)
|
693
635
|
|
694
636
|
model3.update
|
695
637
|
end
|
696
638
|
|
697
|
-
it
|
639
|
+
it "doesn't call update on the interface if the validation fails" do
|
698
640
|
expect( model3.interface ).not_to receive(:update)
|
699
641
|
|
700
642
|
model3.name = "fall over" # triggers validation
|
701
643
|
model3.update
|
702
644
|
end
|
703
645
|
|
704
|
-
it
|
646
|
+
it "calls map_to_interface to get record data" do
|
705
647
|
expect( model3 ).to receive(:map_to_interface)
|
706
648
|
model3.update
|
707
649
|
end
|
708
650
|
|
709
|
-
it
|
710
|
-
|
711
|
-
|
712
|
-
and_return( model4.interface )
|
713
|
-
|
714
|
-
model4.update
|
651
|
+
it "doesn't freak out if the (non_autoincrementing) ID is non-integer" do
|
652
|
+
m = product_model_class.new("bar").read
|
653
|
+
expect{ m.update }.not_to raise_error
|
715
654
|
end
|
716
655
|
|
717
|
-
context
|
718
|
-
|
719
|
-
|
720
|
-
expect( model3.interface ).
|
721
|
-
to receive(:update).
|
722
|
-
and_return( model3.interface )
|
656
|
+
context "when the record already has error alerts" do
|
657
|
+
it "passes if there is no longer anything wrong" do
|
658
|
+
expect( model3.interface ).to receive(:update)
|
723
659
|
|
724
660
|
model3.fake_an_alert(:error, "bad things")
|
725
661
|
model3.update
|
726
662
|
end
|
727
|
-
|
728
663
|
end
|
729
664
|
|
730
665
|
it "creates an alert instead when the interface raises WeakError" do
|
@@ -735,70 +670,59 @@ describe 'CustomerModel' do
|
|
735
670
|
expect( model3.alerts.map(&:message) ).to include( include "foo" )
|
736
671
|
end
|
737
672
|
|
738
|
-
|
739
|
-
|
740
|
-
|
673
|
+
it "updates @model_id" do
|
674
|
+
foo = product_model_class.new("foo").read
|
675
|
+
foo.code = "bang"
|
676
|
+
foo.update
|
677
|
+
|
678
|
+
expect( foo.model_id ).to eq "bang"
|
679
|
+
end
|
741
680
|
|
742
|
-
|
681
|
+
end # of #update
|
743
682
|
|
744
|
-
before do
|
745
|
-
allow( model2.interface ).
|
746
|
-
to receive(:delete).
|
747
|
-
and_return( model2.interface )
|
748
683
|
|
749
|
-
|
684
|
+
describe "#delete" do
|
750
685
|
|
751
|
-
it
|
686
|
+
it "takes no parameters" do
|
752
687
|
expect{ model2.delete(12) }.to raise_exception ArgumentError
|
753
688
|
expect{ model2.delete }.not_to raise_exception
|
754
689
|
end
|
755
690
|
|
756
|
-
it
|
691
|
+
it "returns self" do
|
757
692
|
expect( model2.delete ).to eq model2
|
758
693
|
end
|
759
694
|
|
760
|
-
it
|
761
|
-
|
762
|
-
|
763
|
-
expect( model.model_status ).to eq :empty
|
695
|
+
it "raises a Pod4Error if model status is :unknown" do
|
696
|
+
expect( model.model_status ).to eq :unknown
|
764
697
|
expect{ model.delete }.to raise_exception Pod4::Pod4Error
|
765
698
|
end
|
766
699
|
|
767
|
-
it
|
700
|
+
it "raises a Pod4Error if model status is :deleted"do
|
768
701
|
model2.delete
|
769
702
|
expect{ model2.delete }.to raise_exception Pod4::Pod4Error
|
770
703
|
end
|
771
704
|
|
772
|
-
it
|
773
|
-
|
774
|
-
# `expect(model2).to receive(:validate)`. But for delete we are only running validation as a
|
775
|
-
# courtesy -- a validation fail does not stop the delete, it just sets alerts. So the model
|
776
|
-
# status should be :deleted and not :error
|
777
|
-
model2.name = "fall over"
|
705
|
+
it "calls validate and passes the parameter" do
|
706
|
+
expect( model2 ).to receive(:validate).with(:delete)
|
778
707
|
model2.delete
|
779
|
-
|
780
|
-
# one of the elements of the alerts array should include the word "falling"
|
781
|
-
expect( model2.alerts.map(&:message) ).to include(include "falling")
|
782
708
|
end
|
783
709
|
|
784
|
-
it
|
710
|
+
it "calls delete on the interface if the model status is good" do
|
785
711
|
expect( model3.interface ).
|
786
|
-
to receive(:delete)
|
787
|
-
and_return( model3.interface )
|
712
|
+
to receive(:delete)
|
788
713
|
|
789
714
|
model3.delete
|
790
715
|
end
|
791
716
|
|
792
|
-
it
|
717
|
+
it "calls delete on the interface if the model status is bad" do
|
793
718
|
expect( model3.interface ).
|
794
|
-
to receive(:delete)
|
795
|
-
and_return( model3.interface )
|
719
|
+
to receive(:delete)
|
796
720
|
|
797
|
-
model3.fake_an_alert(:error, :price,
|
721
|
+
model3.fake_an_alert(:error, :price, "qar")
|
798
722
|
model3.delete
|
799
723
|
end
|
800
724
|
|
801
|
-
it
|
725
|
+
it "still gives you full access to the data after a delete" do
|
802
726
|
model2.delete
|
803
727
|
|
804
728
|
expect( model2.id ).to eq records_as_ot[2].>>.id
|
@@ -806,17 +730,14 @@ describe 'CustomerModel' do
|
|
806
730
|
expect( model2.price ).to eq records_as_ot[2].>>.price
|
807
731
|
end
|
808
732
|
|
809
|
-
it
|
733
|
+
it "sets status to :deleted" do
|
810
734
|
model2.delete
|
811
735
|
expect( model2.model_status ).to eq :deleted
|
812
736
|
end
|
813
737
|
|
814
|
-
it
|
815
|
-
|
816
|
-
|
817
|
-
and_return( model4.interface )
|
818
|
-
|
819
|
-
model4.delete
|
738
|
+
it "doesn't freak out if the (non-autoincrementing) ID is non-integer" do
|
739
|
+
m = product_model_class.new("bar").read
|
740
|
+
expect{ m.delete }.not_to raise_error
|
820
741
|
end
|
821
742
|
|
822
743
|
it "creates an alert instead when the interface raises WeakError" do
|
@@ -826,8 +747,7 @@ describe 'CustomerModel' do
|
|
826
747
|
expect( model3.alerts.map(&:message) ).to include( include "foo" )
|
827
748
|
end
|
828
749
|
|
829
|
-
end
|
830
|
-
##
|
750
|
+
end # of #delete
|
831
751
|
|
832
752
|
end
|
833
753
|
|