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.
Files changed (48) hide show
  1. checksums.yaml +5 -5
  2. data/.bugs/bugs +2 -1
  3. data/.bugs/details/b5368c7ef19065fc597b5692314da71772660963.txt +53 -0
  4. data/.hgtags +1 -0
  5. data/Gemfile +5 -5
  6. data/README.md +157 -46
  7. data/lib/pod4/basic_model.rb +9 -22
  8. data/lib/pod4/connection.rb +67 -0
  9. data/lib/pod4/connection_pool.rb +154 -0
  10. data/lib/pod4/errors.rb +20 -0
  11. data/lib/pod4/interface.rb +34 -12
  12. data/lib/pod4/model.rb +32 -27
  13. data/lib/pod4/nebulous_interface.rb +25 -30
  14. data/lib/pod4/null_interface.rb +22 -16
  15. data/lib/pod4/pg_interface.rb +84 -104
  16. data/lib/pod4/sequel_interface.rb +138 -82
  17. data/lib/pod4/tds_interface.rb +83 -70
  18. data/lib/pod4/tweaking.rb +105 -0
  19. data/lib/pod4/version.rb +1 -1
  20. data/md/breaking_changes.md +80 -0
  21. data/spec/common/basic_model_spec.rb +67 -70
  22. data/spec/common/connection_pool_parallelism_spec.rb +154 -0
  23. data/spec/common/connection_pool_spec.rb +246 -0
  24. data/spec/common/connection_spec.rb +129 -0
  25. data/spec/common/model_ai_missing_id_spec.rb +256 -0
  26. data/spec/common/model_plus_encrypting_spec.rb +16 -4
  27. data/spec/common/model_plus_tweaking_spec.rb +128 -0
  28. data/spec/common/model_plus_typecasting_spec.rb +10 -4
  29. data/spec/common/model_spec.rb +283 -363
  30. data/spec/common/nebulous_interface_spec.rb +159 -108
  31. data/spec/common/null_interface_spec.rb +88 -65
  32. data/spec/common/sequel_interface_pg_spec.rb +217 -161
  33. data/spec/common/shared_examples_for_interface.rb +50 -50
  34. data/spec/jruby/sequel_encrypting_jdbc_pg_spec.rb +1 -1
  35. data/spec/jruby/sequel_interface_jdbc_ms_spec.rb +3 -3
  36. data/spec/jruby/sequel_interface_jdbc_pg_spec.rb +3 -23
  37. data/spec/mri/pg_encrypting_spec.rb +1 -1
  38. data/spec/mri/pg_interface_spec.rb +311 -223
  39. data/spec/mri/sequel_encrypting_spec.rb +1 -1
  40. data/spec/mri/sequel_interface_spec.rb +177 -180
  41. data/spec/mri/tds_encrypting_spec.rb +1 -1
  42. data/spec/mri/tds_interface_spec.rb +296 -212
  43. data/tags +340 -174
  44. metadata +19 -11
  45. data/md/fixme.md +0 -3
  46. data/md/roadmap.md +0 -125
  47. data/md/typecasting.md +0 -80
  48. 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
- set_interface NullInterface.new(:id, :nhs_no, :name, :ailment, :prescription, :nonce, [])
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
- set_interface NullInterface.new(:id, :nhs_no, :name, :ailment, :prescription, :nonce, [])
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
- set_interface NullInterface.new(:id, :nhs_no, :name, :ailment, :prescription, :nonce, [])
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
- set_interface NullInterface.new(:id, :date, :heading, :text, [])
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 "ProductModel" do
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
- set_interface NullInterface.new(:id, :code, :product, :price, [])
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
- set_interface NullInterface.new( :id, :code, :band, :sales, :created, :yrstart,
32
- :flag, :foo, :bar, [] )
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
@@ -1,39 +1,35 @@
1
- require 'octothorpe'
1
+ require "octothorpe"
2
2
 
3
- require 'pod4/model'
4
- require 'pod4/null_interface'
3
+ require "pod4/model"
4
+ require "pod4/null_interface"
5
5
 
6
6
 
7
- describe 'CustomerModel' do
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
- set_interface NullInterface.new(:id, :name, :price, :groups, [])
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: 10, name: 'Gomez', price: 1.23, groups: 'trains' },
54
- {id: 20, name: 'Morticia', price: 2.34, groups: 'spanish' },
55
- {id: 30, name: 'Wednesday', price: 3.45, groups: 'school' },
56
- {id: 40, name: 'Pugsley', price: 4.56, groups: 'trains,school'} ]
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 'allow' on one and not the other.
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(20) }
92
+ let(:model) { customer_model_class.new(2) }
76
93
 
77
94
  let(:model2) do
78
- m = customer_model_class.new(30)
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(40)
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
- let(:model4) do
95
- m = customer_model_class.new('eek')
109
+ describe "Model.attr_columns" do
96
110
 
97
- allow( m.interface ).to receive(:read).and_return(thing)
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 'exposes the columns just like attr_accessor' do
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 'Model.columns' do
127
- it 'lists the columns' do
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 'Model.set_interface' do
135
- it 'requires an Interface object' do
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 'sets interface' - covered by the interface test
145
+ # it "sets interface" - covered by the interface test
140
146
  end
141
- ##
142
147
 
143
148
 
144
- describe 'Model.interface' do
145
- it 'is the interface object' do
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
- def arr_without_groups(arr)
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: 'Betty') }.not_to raise_exception
164
+ expect{ customer_model_class.list(name: "Betty") }.not_to raise_exception
167
165
  end
168
166
 
169
- it 'returns an array of customer_model_class records' do
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 'returns the data from the interface' do
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 'honours passed selection criteria' do
188
- hash = {price: 2.22}
189
-
190
- expect( customer_model_class.interface ).
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 'returns an empty array if nothing matches' do
200
- hash = {price: 1.23}
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 'returns an empty array if there are no records' do
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 'calls map_to_model to set the record data' do
214
- allow( customer_model_class.interface ).
215
- to receive(:list).
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 '#new' do
200
+ describe "#new" do
226
201
 
227
- it 'takes an optional ID' do
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 'sets the ID attribute' do
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 'sets the status to empty' do
237
- expect( customer_model_class.new.model_status ).to eq :empty
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 'initializes the alerts attribute' do
215
+ it "initializes the alerts attribute" do
241
216
  expect( customer_model_class.new.alerts ).to eq([])
242
217
  end
243
218
 
244
- it 'doesn''t freak out if the ID is not an integer' do
245
- expect{ customer_model_class.new("france") }.not_to raise_exception
246
- expect( customer_model_class.new("france").model_id ).to eq "france"
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 '#interface' do
254
- it 'returns the interface set in the class definition, again' do
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
- end
259
- ##
233
+
234
+ end # of #interface
260
235
 
261
236
 
262
- describe '#columns' do
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
- describe '#alerts' do
274
- it 'returns the list of alerts against the model' do
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, 'one')
277
- cm.fake_an_alert(:error, :bar, 'two')
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
- it 'requires type, message or type, field, message' do
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('foo') }.to raise_exception ArgumentError
269
+ expect{ model.fake_an_alert("foo") }.to raise_exception ArgumentError
295
270
 
296
- expect{ model.fake_an_alert(:error, 'foo') }.not_to raise_exception
297
- expect{ model.fake_an_alert(:warning, :name, 'bar') }.
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 'only allows valid types' do
277
+ it "only allows valid types" do
303
278
  [:brian, :werning, nil, :alert, :danger].each do |l|
304
- expect{ model.fake_an_alert(l, 'foo') }.to raise_exception ArgumentError
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, 'foo') }.not_to raise_exception
283
+ expect{ model.fake_an_alert(l, "foo") }.not_to raise_exception
309
284
  end
310
285
 
311
286
  end
312
287
 
313
- it 'creates an Alert and adds it to @alerts' do
314
- lurch = 'Dnhhhhhh'
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 'sets @model_status if the type is worse than @model_status' do
323
- model.fake_an_alert(:warning, :price, 'xoo')
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, 'flom')
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, 'flom')
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, 'qar')
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, 'drazq')
310
+ model.fake_an_alert(:warning, :price, "drazq")
336
311
  expect( model.model_status ).to eq :error
337
312
  end
338
313
 
339
- it 'ignores a new alert if identical to an existing one' do
340
- lurch = 'Dnhhhhhh'
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 'takes an Octothorpe or a Hash' do
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 'returns self' do
335
+ it "returns self" do
363
336
  expect( model.set(ot) ).to eq model
364
337
  end
365
338
 
366
- it 'sets the attribute columns from the hash' do
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 'only sets the attributes on the model that it is given' do
375
- otx = Octothorpe.new(name: 'Piggy', price: 98.76, weapon: 'rake')
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 40
379
- expect( model3.name ).to eq 'Piggy'
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 '#to_ot' do
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
- expect( model.to_ot.to_h ).
393
- to eq( {id: nil, name: nil, price:nil, groups:nil} )
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
- model.map_to_model(records[1])
396
- expect( model.to_ot ).to be_a_kind_of Octothorpe
397
- expect( without_groups(model.to_ot) ).to eq recordsx[1]
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
- model.map_to_model(records_as_ot[2])
400
- expect( model.to_ot ).to be_a_kind_of Octothorpe
401
- expect( without_groups(model.to_ot) ).to eq recordsx[2]
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
- end
404
- ##
377
+
378
+ end # of #to_ot
405
379
 
406
380
 
407
- describe '#map_to_model' do
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( ['trains','school'] )
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
- describe '#map_to_interface' do
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.>>.groups ).to eq( 'trains,school' )
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 '#raise_exceptions' do
410
+ describe "#raise_exceptions" do
434
411
 
435
- it 'is also known as .or_die' do
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 'raises ValidationError if model status is :error' do
441
- model.fake_an_alert(:error, :price, 'qar')
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 'does nothing if model status is not :error' do
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, 'qar')
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, 'qar')
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, 'qar')
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 'takes no parameters' do
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 'returns self' do
446
+ it "returns self" do
472
447
  expect( new_model.create ).to eq new_model
473
448
  end
474
449
 
475
- it 'calls validate' do
476
- # validation tests arity of the validate method; rspec freaks out. So we can't
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
- m = customer_model_class.new
480
- m.name = "fall over"
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 'calls create on the interface if the record is good' do
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, 'foo')
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
- it 'doesnt call create on the interface if the record is bad' do
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 'sets the ID' do
502
- new_model.id = 50
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 'leaves the model status alone if it was not :empty' do
518
- new_model.id = 50
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, 'qar')
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 'calls map_to_interface to get record data' do
527
- allow( new_model.interface ).to receive(:create)
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
- new_model.id = 50
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
- it 'doesn\'t freak out if the model is not an integer' do
536
- expect( new_model.interface ).to receive(:create)
537
- new_model.id = "handy"
538
- new_model.name = "Thing"
494
+ m.id = 5
495
+ m.name = "Lurch"
496
+ m.create
497
+ end
539
498
 
540
- expect{ new_model.create }.not_to raise_error
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 = 50
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
- end
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 '#read' do
527
+ describe "#read" do
558
528
 
559
- it 'takes no parameters' do
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 'returns self ' do
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 'calls read on the interface' do
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 'calls validate' do
581
- # again, because rspec is a bit stupid, we can't just `expect(model).to receive(:validate)`
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 'sets the attribute columns using map_to_model' do
548
+ it "sets the attribute columns using map_to_model" do
592
549
  ot = records_as_ot.last
593
- allow( model.interface ).to receive(:read).and_return( ot )
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 'sets model status to :okay if it was :empty' do
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 'leaves the model status alone if it was not :empty' do
563
+ it "leaves the model status alone if it was not :unknown" do
612
564
  ot = records_as_ot.last
613
- allow( model.interface ).to receive(:read).and_return( ot )
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 'doesn\'t freak out if the model is non-integer' do
621
- allow( model.interface ).to receive(:read).and_return( thing )
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 'if the interface.read returns an empty Octothorpe' do
574
+ context "if the interface.read returns an empty Octothorpe" do
627
575
  let(:missing) { customer_model_class.new(99) }
628
576
 
629
- it 'doesn\'t throw an exception' do
577
+ it "doesn't throw an exception" do
630
578
  expect{ missing.read }.not_to raise_exception
631
579
  end
632
580
 
633
- it 'raises an error alert' do
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
- end
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
- describe '#update' do
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
- end
605
+ describe "#update" do
660
606
 
661
- it 'takes no parameters' do
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 'returns self' do
612
+ it "returns self" do
667
613
  expect( model2.update ).to eq model2
668
614
  end
669
615
 
670
- it 'raises a Pod4Error if model status is :empty' do
671
- allow( model.interface ).to receive(:update).and_return( model.interface )
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 'raises a Pod4Error if model status is :deleted' do
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 'calls validate' do
683
- # again, we can't `expect(model2).to receive(:validate)` because we're testing arity there
684
- model2.name = "fall over"
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 'calls update on the interface if the validation passes' do
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 'doesn\'t call update on the interface if the validation fails' do
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 'calls map_to_interface to get record data' do
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 'doesn\'t freak out if the model is non-integer' do
710
- expect( model4.interface ).
711
- to receive(:update).
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 'when the record already has error alerts' do
718
-
719
- it 'passes if there is no longer anything wrong' do
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
- end
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
- describe '#delete' do
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
- end
684
+ describe "#delete" do
750
685
 
751
- it 'takes no parameters' do
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 'returns self' do
691
+ it "returns self" do
757
692
  expect( model2.delete ).to eq model2
758
693
  end
759
694
 
760
- it 'raises a Pod4Error if model status is :empty' do
761
- allow( model.interface ).to receive(:delete).and_return( model.interface )
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 'raises a Pod4Error if model status is :deleted'do
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 'calls validate' do
773
- # again, because rspec can't cope with us testing arity in Pod4::Model, we can't say
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 'calls delete on the interface if the model status is good' do
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 'calls delete on the interface if the model status is bad' do
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, 'qar')
721
+ model3.fake_an_alert(:error, :price, "qar")
798
722
  model3.delete
799
723
  end
800
724
 
801
- it 'still gives you full access to the data after a delete' do
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 'sets status to :deleted' do
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 'doesn\'t freak out if the model is non-integer' do
815
- expect( model4.interface ).
816
- to receive(:delete).
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