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
@@ -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
|
|