pod4 0.8.3 → 0.9.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 +4 -4
- data/.hgignore +1 -0
- data/.hgtags +1 -0
- data/Gemfile +4 -4
- data/README.md +165 -161
- data/lib/pod4/basic_model.rb +1 -1
- data/lib/pod4/errors.rb +15 -0
- data/lib/pod4/interface.rb +4 -1
- data/lib/pod4/model.rb +34 -35
- data/lib/pod4/nebulous_interface.rb +2 -1
- data/lib/pod4/sequel_interface.rb +30 -6
- data/lib/pod4/version.rb +1 -1
- data/lib/pod4.rb +1 -0
- data/pod4.gemspec +1 -1
- data/spec/README.md +0 -5
- data/spec/common/basic_model_spec.rb +1 -1
- data/spec/common/model_new_validate_spec.rb +204 -0
- data/spec/common/model_spec.rb +53 -15
- data/spec/common/nebulous_interface_spec.rb +19 -4
- data/spec/jruby/sequel_interface_jdbc_ms_spec.rb +2 -1
- metadata +6 -5
- data/.ruby-version +0 -1
data/lib/pod4/model.rb
CHANGED
|
@@ -9,7 +9,7 @@ module Pod4
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
##
|
|
12
|
-
# The parent of all models.
|
|
12
|
+
# The parent of all CRUDL models.
|
|
13
13
|
#
|
|
14
14
|
# Models & Interfaces
|
|
15
15
|
# -------------------
|
|
@@ -21,7 +21,7 @@ module Pod4
|
|
|
21
21
|
# not. The model doesn't care about where the data comes from. Models are all subclasses of
|
|
22
22
|
# Pod4::Model.
|
|
23
23
|
#
|
|
24
|
-
# An interface encapsulates the connection to whatever is providing the data
|
|
24
|
+
# An interface encapsulates the connection to whatever is providing the data. It might be a
|
|
25
25
|
# wrapper for calls to the Sequel ORM, for example. Or it could be a making a series of calls to
|
|
26
26
|
# a set of Nebulous verbs. It only cares about dealing with the data source, and it is only
|
|
27
27
|
# called by the model.
|
|
@@ -91,7 +91,6 @@ module Pod4
|
|
|
91
91
|
attr_accessor *cols
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
-
|
|
95
94
|
##
|
|
96
95
|
# Returns the list of columns from attr_columns
|
|
97
96
|
#
|
|
@@ -99,7 +98,6 @@ module Pod4
|
|
|
99
98
|
[]
|
|
100
99
|
end
|
|
101
100
|
|
|
102
|
-
|
|
103
101
|
##
|
|
104
102
|
# Call this to return an array of record information.
|
|
105
103
|
#
|
|
@@ -107,12 +105,17 @@ module Pod4
|
|
|
107
105
|
# ID in each array element.
|
|
108
106
|
#
|
|
109
107
|
# For the purposes of Model we assume that we can make an instance out of each array element,
|
|
110
|
-
# and we return an array of instances of the model
|
|
108
|
+
# and we return an array of instances of the model. Override this method if that is not true
|
|
111
109
|
# for your Interface.
|
|
112
110
|
#
|
|
113
111
|
# Note that list should ALWAYS return an array, and array elements should always respond to
|
|
114
112
|
# :id -- otherwise we raise a Pod4Error.
|
|
115
113
|
#
|
|
114
|
+
# Note also that while list returns an array of model objects, `read` has _not_ been run
|
|
115
|
+
# against each object. The data is there, but @model_status == :empty, and validation has not
|
|
116
|
+
# been run. This is partly for the sake of efficiency, partly to help avoid recursive loops
|
|
117
|
+
# in validation.
|
|
118
|
+
#
|
|
116
119
|
def list(params=nil)
|
|
117
120
|
fail_no_id_fld unless interface.id_fld
|
|
118
121
|
|
|
@@ -123,37 +126,29 @@ module Pod4
|
|
|
123
126
|
rec.map_to_model(ot) # seperately, in case model forgot to return self
|
|
124
127
|
rec
|
|
125
128
|
end
|
|
126
|
-
|
|
127
129
|
end
|
|
128
130
|
|
|
129
|
-
|
|
130
131
|
def test_for_octo(param)
|
|
131
132
|
raise( ArgumentError, 'Parameter must be a Hash or Octothorpe', caller ) \
|
|
132
133
|
unless param.kind_of?(Hash) || param.kind_of?(Octothorpe)
|
|
133
134
|
|
|
134
135
|
end
|
|
135
136
|
|
|
136
|
-
|
|
137
137
|
def test_for_invalid_status(action, status)
|
|
138
138
|
raise( Pod4Error, "Invalid model status for an action of #{action}", caller ) \
|
|
139
139
|
if [:empty, :deleted].include? status
|
|
140
140
|
|
|
141
141
|
end
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
|
|
145
143
|
def fail_no_id_fld
|
|
146
144
|
raise Pod4Error, "No ID field defined in interface", caller
|
|
147
145
|
end
|
|
148
146
|
|
|
149
|
-
|
|
150
147
|
def fail_no_id
|
|
151
148
|
raise Pod4Error, "ID field missing from record", caller
|
|
152
149
|
end
|
|
153
150
|
|
|
154
|
-
end
|
|
155
|
-
##
|
|
156
|
-
|
|
151
|
+
end # of class << self
|
|
157
152
|
|
|
158
153
|
##
|
|
159
154
|
# Syntactic sugar; pretty much the same as self.class.columns, which returns the `attr_columns`
|
|
@@ -161,21 +156,21 @@ module Pod4
|
|
|
161
156
|
#
|
|
162
157
|
def columns; self.class.columns.dup; end
|
|
163
158
|
|
|
164
|
-
|
|
165
159
|
##
|
|
166
160
|
# Call this to write a new record to the data source.
|
|
167
161
|
#
|
|
168
162
|
# Note: create needs to set @id. But interface.create should return it, so that's okay.
|
|
169
163
|
#
|
|
170
164
|
def create
|
|
171
|
-
|
|
165
|
+
run_validation(:create)
|
|
172
166
|
@model_id = interface.create(map_to_interface) unless @model_status == :error
|
|
173
167
|
|
|
174
168
|
@model_status = :okay if @model_status == :empty
|
|
175
169
|
self
|
|
170
|
+
rescue Pod4::WeakError
|
|
171
|
+
add_alert(:error, $!)
|
|
176
172
|
end
|
|
177
173
|
|
|
178
|
-
|
|
179
174
|
##
|
|
180
175
|
# Call this to fetch the data for this instance from the data source
|
|
181
176
|
#
|
|
@@ -186,26 +181,29 @@ module Pod4
|
|
|
186
181
|
add_alert(:error, "Record ID '#@model_id' not found on the data source")
|
|
187
182
|
else
|
|
188
183
|
map_to_model(r)
|
|
184
|
+
run_validation(:read)
|
|
189
185
|
@model_status = :okay if @model_status == :empty
|
|
190
186
|
end
|
|
191
187
|
|
|
192
188
|
self
|
|
189
|
+
rescue Pod4::WeakError
|
|
190
|
+
add_alert(:error, $!)
|
|
193
191
|
end
|
|
194
192
|
|
|
195
|
-
|
|
196
193
|
##
|
|
197
194
|
# Call this to update the data source with the current attribute values
|
|
198
195
|
#
|
|
199
196
|
def update
|
|
200
197
|
Model.test_for_invalid_status(:update, @model_status)
|
|
201
198
|
|
|
202
|
-
clear_alerts;
|
|
199
|
+
clear_alerts; run_validation(:update)
|
|
203
200
|
interface.update(@model_id, map_to_interface) unless @model_status == :error
|
|
204
201
|
|
|
205
202
|
self
|
|
203
|
+
rescue Pod4::WeakError
|
|
204
|
+
add_alert(:error, $!)
|
|
206
205
|
end
|
|
207
206
|
|
|
208
|
-
|
|
209
207
|
##
|
|
210
208
|
# Call this to delete the record on the data source.
|
|
211
209
|
#
|
|
@@ -213,13 +211,14 @@ module Pod4
|
|
|
213
211
|
#
|
|
214
212
|
def delete
|
|
215
213
|
Model.test_for_invalid_status(:delete, @model_status)
|
|
216
|
-
clear_alerts;
|
|
214
|
+
clear_alerts; run_validation(:delete)
|
|
217
215
|
interface.delete(@model_id)
|
|
218
216
|
@model_status = :deleted
|
|
219
217
|
self
|
|
218
|
+
rescue Pod4::WeakError
|
|
219
|
+
add_alert(:error, $!)
|
|
220
220
|
end
|
|
221
221
|
|
|
222
|
-
|
|
223
222
|
##
|
|
224
223
|
# Call this to validate the model.
|
|
225
224
|
#
|
|
@@ -228,14 +227,14 @@ module Pod4
|
|
|
228
227
|
# Note that you can only validate what is actually stored on the model. If you want to check
|
|
229
228
|
# the data being passed to the model in `set`, you need to override that routine.
|
|
230
229
|
#
|
|
231
|
-
#
|
|
232
|
-
#
|
|
230
|
+
# You may optionally catch the vmode parameter, which is one of :create,
|
|
231
|
+
# :read, :update, :delete, to have different validation under these circumstances; or you may
|
|
232
|
+
# safely ignore it and override `create`, `read`, `update` or `delete` as you wish.
|
|
233
233
|
#
|
|
234
|
-
def validate
|
|
234
|
+
def validate(vmode=nil)
|
|
235
235
|
# Holding pattern. All models should use super, in principal
|
|
236
236
|
end
|
|
237
237
|
|
|
238
|
-
|
|
239
238
|
##
|
|
240
239
|
# Set instance values on the model from a Hash or Octothorpe.
|
|
241
240
|
#
|
|
@@ -252,7 +251,6 @@ module Pod4
|
|
|
252
251
|
self
|
|
253
252
|
end
|
|
254
253
|
|
|
255
|
-
|
|
256
254
|
##
|
|
257
255
|
# Return an Octothorpe of all the attr_columns attributes.
|
|
258
256
|
#
|
|
@@ -264,7 +262,6 @@ module Pod4
|
|
|
264
262
|
Octothorpe.new(to_h)
|
|
265
263
|
end
|
|
266
264
|
|
|
267
|
-
|
|
268
265
|
##
|
|
269
266
|
# Used by the interface to set the column values on the model.
|
|
270
267
|
#
|
|
@@ -278,11 +275,9 @@ module Pod4
|
|
|
278
275
|
#
|
|
279
276
|
def map_to_model(ot)
|
|
280
277
|
merge(ot)
|
|
281
|
-
validate
|
|
282
278
|
self
|
|
283
279
|
end
|
|
284
280
|
|
|
285
|
-
|
|
286
281
|
##
|
|
287
282
|
# used by the model to get an OT of column values for the interface.
|
|
288
283
|
#
|
|
@@ -302,10 +297,8 @@ module Pod4
|
|
|
302
297
|
Octothorpe.new(to_h)
|
|
303
298
|
end
|
|
304
299
|
|
|
305
|
-
|
|
306
300
|
private
|
|
307
301
|
|
|
308
|
-
|
|
309
302
|
##
|
|
310
303
|
# Output a hash of the columns
|
|
311
304
|
#
|
|
@@ -315,7 +308,6 @@ module Pod4
|
|
|
315
308
|
end
|
|
316
309
|
end
|
|
317
310
|
|
|
318
|
-
|
|
319
311
|
##
|
|
320
312
|
# Merge an OT with our columns
|
|
321
313
|
#
|
|
@@ -327,8 +319,15 @@ module Pod4
|
|
|
327
319
|
end
|
|
328
320
|
end
|
|
329
321
|
|
|
330
|
-
|
|
331
|
-
|
|
322
|
+
##
|
|
323
|
+
# Call the validate method on the model. Allow the user to override the method with or without
|
|
324
|
+
# the vmode paramter, as they choose.
|
|
325
|
+
#
|
|
326
|
+
def run_validation(vmode)
|
|
327
|
+
method(:validate).arity == 0 ? validate : validate(vmode)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
end # of Model
|
|
332
331
|
|
|
333
332
|
|
|
334
333
|
end
|
|
@@ -291,7 +291,8 @@ module Pod4
|
|
|
291
291
|
else :response
|
|
292
292
|
end
|
|
293
293
|
|
|
294
|
-
raise Pod4::
|
|
294
|
+
raise Pod4::WeakError, "Nebulous returned an error verb: #{@response.description}" \
|
|
295
|
+
if @response_status == :verberror
|
|
295
296
|
|
|
296
297
|
self
|
|
297
298
|
|
|
@@ -79,16 +79,37 @@ module Pod4
|
|
|
79
79
|
raise(Pod4Error, 'no call to set_table in the interface definition') if self.class.table.nil?
|
|
80
80
|
raise(Pod4Error, 'no call to set_id_fld in the interface definition') if self.class.id_fld.nil?
|
|
81
81
|
|
|
82
|
-
@
|
|
83
|
-
@
|
|
84
|
-
@id_fld
|
|
85
|
-
|
|
82
|
+
@sequel_version = Sequel.respond_to?(:qualify) ? 5 : 4
|
|
83
|
+
@db = db # reference to the db object
|
|
84
|
+
@id_fld = self.class.id_fld
|
|
85
|
+
|
|
86
|
+
@table =
|
|
87
|
+
if schema
|
|
88
|
+
if @sequel_version == 5
|
|
89
|
+
db[ Sequel[schema][table] ]
|
|
90
|
+
else
|
|
91
|
+
db[ "#{schema}__#{table}".to_sym ]
|
|
92
|
+
end
|
|
93
|
+
else
|
|
94
|
+
db[table]
|
|
95
|
+
end
|
|
96
|
+
|
|
86
97
|
# Work around a problem with jdbc-postgresql where it throws an exception whenever it sees
|
|
87
98
|
# the money type. This workaround actually allows us to return a BigDecimal, so it's better
|
|
88
99
|
# than using postgres_pr when under jRuby!
|
|
89
100
|
if @db.uri =~ /jdbc:postgresql/
|
|
90
101
|
@db.conversion_procs[790] = ->(s){BigDecimal.new s[1..-1] rescue nil}
|
|
91
|
-
Sequel::JDBC::Postgres::Dataset
|
|
102
|
+
c = Sequel::JDBC::Postgres::Dataset
|
|
103
|
+
|
|
104
|
+
if @sequel_version >= 5
|
|
105
|
+
# In Sequel 5 everything is frozen, so some hacking is required.
|
|
106
|
+
# See https://github.com/jeremyevans/sequel/issues/1458
|
|
107
|
+
vals = c::PG_SPECIFIC_TYPES + [Java::JavaSQL::Types::DOUBLE]
|
|
108
|
+
c.send(:remove_const, :PG_SPECIFIC_TYPES) # We can probably get away with just const_set, but.
|
|
109
|
+
c.send(:const_set, :PG_SPECIFIC_TYPES, vals.freeze)
|
|
110
|
+
else
|
|
111
|
+
c::PG_SPECIFIC_TYPES << Java::JavaSQL::Types::DOUBLE
|
|
112
|
+
end
|
|
92
113
|
end
|
|
93
114
|
|
|
94
115
|
rescue => e
|
|
@@ -323,8 +344,11 @@ module Pod4
|
|
|
323
344
|
m[k] = v.kind_of?(Symbol) ? v.to_s : v
|
|
324
345
|
end
|
|
325
346
|
|
|
347
|
+
when nil
|
|
348
|
+
nil
|
|
349
|
+
|
|
326
350
|
else
|
|
327
|
-
sel
|
|
351
|
+
fail Pod4::DatabaseError, "Expected a selection hash, got: #{sel.inspect}"
|
|
328
352
|
|
|
329
353
|
end
|
|
330
354
|
|
data/lib/pod4/version.rb
CHANGED
data/lib/pod4.rb
CHANGED
data/pod4.gemspec
CHANGED
data/spec/README.md
CHANGED
|
@@ -12,8 +12,3 @@ assumes that the schema names can be hardcoded, and since we create and wipe
|
|
|
12
12
|
tables in these tests, you should really have a seperate database for them
|
|
13
13
|
anyway.)
|
|
14
14
|
|
|
15
|
-
Sequel
|
|
16
|
-
------
|
|
17
|
-
Note that the Sequel ORM adapter does **not** require a database -- we
|
|
18
|
-
currently use an in-memory sqlite database instead.
|
|
19
|
-
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
require 'octothorpe'
|
|
2
|
+
|
|
3
|
+
require 'pod4/model'
|
|
4
|
+
require 'pod4/null_interface'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
##
|
|
8
|
+
# This is purely here to test that model works when you have a validate that accepts the new
|
|
9
|
+
# vmode parameter
|
|
10
|
+
#
|
|
11
|
+
describe 'Customer Model with new validate' do
|
|
12
|
+
|
|
13
|
+
let(:customer_model_class) do
|
|
14
|
+
Class.new Pod4::Model do
|
|
15
|
+
attr_columns :id, :name, :groups
|
|
16
|
+
attr_columns :price # specifically testing multiple calls to attr_columns
|
|
17
|
+
set_interface NullInterface.new(:id, :name, :price, :groups, [])
|
|
18
|
+
|
|
19
|
+
def map_to_model(ot)
|
|
20
|
+
super
|
|
21
|
+
@groups = @groups ? @groups.split(',') : []
|
|
22
|
+
self
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def map_to_interface
|
|
26
|
+
x = super
|
|
27
|
+
g = (x.>>.groups || []).join(',')
|
|
28
|
+
x.merge(groups: g)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def fake_an_alert(*args)
|
|
32
|
+
add_alert(*args) #private method
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def validate(vmode)
|
|
36
|
+
add_alert(:error, "falling over for mode #{vmode}") if name == "fall over"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def reset_alerts; @alerts = []; end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
let(:records) do
|
|
44
|
+
[ {id: 10, name: 'Gomez', price: 1.23, groups: 'trains' },
|
|
45
|
+
{id: 20, name: 'Morticia', price: 2.34, groups: 'spanish' },
|
|
46
|
+
{id: 30, name: 'Wednesday', price: 3.45, groups: 'school' },
|
|
47
|
+
{id: 40, name: 'Pugsley', price: 4.56, groups: 'trains,school'} ]
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
let(:records_as_ot) { records.map{|r| Octothorpe.new(r) } }
|
|
52
|
+
|
|
53
|
+
# model is just a plain newly created object that you can call read on.
|
|
54
|
+
# model2 and model3 are in an identical state - they have been filled with a
|
|
55
|
+
# read(). We have two so that we can RSpec 'allow' on one and not the other.
|
|
56
|
+
|
|
57
|
+
let(:model) { customer_model_class.new(20) }
|
|
58
|
+
|
|
59
|
+
let(:model2) do
|
|
60
|
+
m = customer_model_class.new(30)
|
|
61
|
+
|
|
62
|
+
allow( m.interface ).to receive(:read).and_return( Octothorpe.new(records[2]) )
|
|
63
|
+
m.read.or_die
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
let(:model3) do
|
|
67
|
+
m = customer_model_class.new(40)
|
|
68
|
+
|
|
69
|
+
allow( m.interface ).to receive(:read).and_return( Octothorpe.new(records[3]) )
|
|
70
|
+
m.read.or_die
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
##
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
describe '#create' do
|
|
77
|
+
|
|
78
|
+
let (:new_model) { customer_model_class.new }
|
|
79
|
+
|
|
80
|
+
it 'calls validate and passes the parameter' do
|
|
81
|
+
# validation tests arity of the validate method; rspec freaks out. So we can't
|
|
82
|
+
# `expect( new_model ).to receive(:validate)`
|
|
83
|
+
|
|
84
|
+
m = customer_model_class.new
|
|
85
|
+
m.name = "fall over"
|
|
86
|
+
m.create
|
|
87
|
+
expect( m.model_status ).to eq :error
|
|
88
|
+
expect( m.alerts.map(&:message) ).to include( include "create" )
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it 'calls create on the interface if the record is good' do
|
|
92
|
+
expect( customer_model_class.interface ).to receive(:create)
|
|
93
|
+
customer_model_class.new.create
|
|
94
|
+
|
|
95
|
+
new_model.fake_an_alert(:warning, :name, 'foo')
|
|
96
|
+
expect( new_model.interface ).to receive(:create)
|
|
97
|
+
new_model.create
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it 'doesnt call create on the interface if the record is bad' do
|
|
101
|
+
new_model.fake_an_alert(:error, :name, 'foo')
|
|
102
|
+
expect( new_model.interface ).not_to receive(:create)
|
|
103
|
+
new_model.create
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
end
|
|
107
|
+
##
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
describe '#read' do
|
|
111
|
+
|
|
112
|
+
it 'calls validate and passes the parameter' do
|
|
113
|
+
# again, because rspec is a bit stupid, we can't just `expect(model).to receive(:validate)`
|
|
114
|
+
|
|
115
|
+
allow( model.interface ).
|
|
116
|
+
to receive(:read).
|
|
117
|
+
and_return( records_as_ot.first.merge(name: "fall over") )
|
|
118
|
+
|
|
119
|
+
model.read
|
|
120
|
+
expect( model.model_status ).to eq :error
|
|
121
|
+
expect( model.alerts.map(&:message) ).to include( include "mode read" )
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
end
|
|
125
|
+
##
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
describe '#update' do
|
|
129
|
+
|
|
130
|
+
before do
|
|
131
|
+
allow( model2.interface ).
|
|
132
|
+
to receive(:update).
|
|
133
|
+
and_return( model2.interface )
|
|
134
|
+
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it 'calls validate and passes the parameter' do
|
|
138
|
+
# again, we can't `expect(model2).to receive(:validate)` because we're testing arity there
|
|
139
|
+
model2.name = "fall over"
|
|
140
|
+
model2.update
|
|
141
|
+
expect( model2.model_status ).to eq :error
|
|
142
|
+
expect( model2.alerts.map(&:message) ).to include( include "mode update" )
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it 'calls update on the interface if the validation passes' do
|
|
146
|
+
expect( model3.interface ).
|
|
147
|
+
to receive(:update).
|
|
148
|
+
and_return( model3.interface )
|
|
149
|
+
|
|
150
|
+
model3.update
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it 'doesn\'t call update on the interface if the validation fails' do
|
|
154
|
+
expect( model3.interface ).not_to receive(:update)
|
|
155
|
+
|
|
156
|
+
model3.name = "fall over" # triggers validation
|
|
157
|
+
model3.update
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
end
|
|
161
|
+
##
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
describe '#delete' do
|
|
165
|
+
|
|
166
|
+
before do
|
|
167
|
+
allow( model2.interface ).
|
|
168
|
+
to receive(:delete).
|
|
169
|
+
and_return( model2.interface )
|
|
170
|
+
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
it 'calls validate and passes the parameter' do
|
|
174
|
+
# again, because rspec can't cope with us testing arity in Pod4::Model, we can't say
|
|
175
|
+
# `expect(model2).to receive(:validate)`. But for delete we are only running validation as a
|
|
176
|
+
# courtesy -- a validation fail does not stop the delete, it just sets alerts. So the model
|
|
177
|
+
# status should be :deleted and not :error
|
|
178
|
+
model2.name = "fall over"
|
|
179
|
+
model2.delete
|
|
180
|
+
expect( model2.alerts.map(&:message) ).to include(include "mode delete")
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
it 'calls delete on the interface if the model status is good' do
|
|
184
|
+
expect( model3.interface ).
|
|
185
|
+
to receive(:delete).
|
|
186
|
+
and_return( model3.interface )
|
|
187
|
+
|
|
188
|
+
model3.delete
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
it 'calls delete on the interface if the model status is bad' do
|
|
192
|
+
expect( model3.interface ).
|
|
193
|
+
to receive(:delete).
|
|
194
|
+
and_return( model3.interface )
|
|
195
|
+
|
|
196
|
+
model3.fake_an_alert(:error, :price, 'qar')
|
|
197
|
+
model3.delete
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
end
|
|
201
|
+
##
|
|
202
|
+
|
|
203
|
+
end
|
|
204
|
+
|
data/spec/common/model_spec.rb
CHANGED
|
@@ -347,15 +347,6 @@ describe 'CustomerModel' do
|
|
|
347
347
|
##
|
|
348
348
|
|
|
349
349
|
|
|
350
|
-
describe '#validate' do
|
|
351
|
-
it 'takes no parameters' do
|
|
352
|
-
expect{ customer_model_class.new.validate(12) }.to raise_exception ArgumentError
|
|
353
|
-
expect{ customer_model_class.new.validate }.not_to raise_exception
|
|
354
|
-
end
|
|
355
|
-
end
|
|
356
|
-
##
|
|
357
|
-
|
|
358
|
-
|
|
359
350
|
describe '#set' do
|
|
360
351
|
|
|
361
352
|
let (:ot) { records_as_ot[3] }
|
|
@@ -482,8 +473,13 @@ describe 'CustomerModel' do
|
|
|
482
473
|
end
|
|
483
474
|
|
|
484
475
|
it 'calls validate' do
|
|
485
|
-
|
|
486
|
-
new_model.
|
|
476
|
+
# validation tests arity of the validate method; rspec freaks out. So we can't
|
|
477
|
+
# `expect( new_model ).to receive(:validate)`
|
|
478
|
+
|
|
479
|
+
m = customer_model_class.new
|
|
480
|
+
m.name = "fall over"
|
|
481
|
+
m.create
|
|
482
|
+
expect( m.model_status ).to eq :error
|
|
487
483
|
end
|
|
488
484
|
|
|
489
485
|
it 'calls create on the interface if the record is good' do
|
|
@@ -544,6 +540,16 @@ describe 'CustomerModel' do
|
|
|
544
540
|
expect{ new_model.create }.not_to raise_error
|
|
545
541
|
end
|
|
546
542
|
|
|
543
|
+
it "creates an alert instead when the interface raises WeakError" do
|
|
544
|
+
allow( new_model.interface ).to receive(:create).and_raise Pod4::WeakError, "foo"
|
|
545
|
+
|
|
546
|
+
new_model.id = 50
|
|
547
|
+
new_model.name = "Lurch"
|
|
548
|
+
expect{ new_model.create }.not_to raise_exception
|
|
549
|
+
expect( new_model.model_status ).to eq :error
|
|
550
|
+
expect( new_model.alerts.map(&:message) ).to include( include "foo" )
|
|
551
|
+
end
|
|
552
|
+
|
|
547
553
|
end
|
|
548
554
|
##
|
|
549
555
|
|
|
@@ -572,12 +578,14 @@ describe 'CustomerModel' do
|
|
|
572
578
|
end
|
|
573
579
|
|
|
574
580
|
it 'calls validate' do
|
|
581
|
+
# again, because rspec is a bit stupid, we can't just `expect(model).to receive(:validate)`
|
|
582
|
+
|
|
575
583
|
allow( model.interface ).
|
|
576
584
|
to receive(:read).
|
|
577
|
-
and_return( records_as_ot.first )
|
|
585
|
+
and_return( records_as_ot.first.merge(name: "fall over") )
|
|
578
586
|
|
|
579
|
-
expect( model ).to receive(:validate)
|
|
580
587
|
model.read
|
|
588
|
+
expect( model.model_status ).to eq :error
|
|
581
589
|
end
|
|
582
590
|
|
|
583
591
|
it 'sets the attribute columns using map_to_model' do
|
|
@@ -629,6 +637,14 @@ describe 'CustomerModel' do
|
|
|
629
637
|
|
|
630
638
|
end
|
|
631
639
|
|
|
640
|
+
it "creates an alert instead when the interface raises WeakError" do
|
|
641
|
+
allow( model.interface ).to receive(:read).and_raise Pod4::WeakError, "foo"
|
|
642
|
+
|
|
643
|
+
expect{ model.read }.not_to raise_exception
|
|
644
|
+
expect( model.model_status ).to eq :error
|
|
645
|
+
expect( model.alerts.map(&:message) ).to include( include "foo" )
|
|
646
|
+
end
|
|
647
|
+
|
|
632
648
|
end
|
|
633
649
|
##
|
|
634
650
|
|
|
@@ -664,8 +680,10 @@ describe 'CustomerModel' do
|
|
|
664
680
|
end
|
|
665
681
|
|
|
666
682
|
it 'calls validate' do
|
|
667
|
-
expect(
|
|
683
|
+
# again, we can't `expect(model2).to receive(:validate)` because we're testing arity there
|
|
684
|
+
model2.name = "fall over"
|
|
668
685
|
model2.update
|
|
686
|
+
expect( model2.model_status ).to eq :error
|
|
669
687
|
end
|
|
670
688
|
|
|
671
689
|
it 'calls update on the interface if the validation passes' do
|
|
@@ -709,6 +727,13 @@ describe 'CustomerModel' do
|
|
|
709
727
|
|
|
710
728
|
end
|
|
711
729
|
|
|
730
|
+
it "creates an alert instead when the interface raises WeakError" do
|
|
731
|
+
allow( model3.interface ).to receive(:update).and_raise Pod4::WeakError, "foo"
|
|
732
|
+
|
|
733
|
+
expect{ model3.update }.not_to raise_exception
|
|
734
|
+
expect( model3.model_status ).to eq :error
|
|
735
|
+
expect( model3.alerts.map(&:message) ).to include( include "foo" )
|
|
736
|
+
end
|
|
712
737
|
|
|
713
738
|
end
|
|
714
739
|
##
|
|
@@ -745,8 +770,15 @@ describe 'CustomerModel' do
|
|
|
745
770
|
end
|
|
746
771
|
|
|
747
772
|
it 'calls validate' do
|
|
748
|
-
|
|
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"
|
|
749
778
|
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")
|
|
750
782
|
end
|
|
751
783
|
|
|
752
784
|
it 'calls delete on the interface if the model status is good' do
|
|
@@ -787,6 +819,12 @@ describe 'CustomerModel' do
|
|
|
787
819
|
model4.delete
|
|
788
820
|
end
|
|
789
821
|
|
|
822
|
+
it "creates an alert instead when the interface raises WeakError" do
|
|
823
|
+
allow( model3.interface ).to receive(:delete).and_raise Pod4::WeakError, "foo"
|
|
824
|
+
|
|
825
|
+
expect{ model3.delete }.not_to raise_exception
|
|
826
|
+
expect( model3.alerts.map(&:message) ).to include( include "foo" )
|
|
827
|
+
end
|
|
790
828
|
|
|
791
829
|
end
|
|
792
830
|
##
|