pod4 0.8.0 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.hgtags +1 -0
- data/.ruby-version +1 -1
- data/Gemfile +5 -5
- data/lib/pod4/errors.rb +5 -0
- data/lib/pod4/nebulous_interface.rb +10 -11
- data/lib/pod4/pg_interface.rb +2 -2
- data/lib/pod4/tds_interface.rb +4 -2
- data/lib/pod4/typecasting.rb +1 -1
- data/lib/pod4/version.rb +1 -1
- data/pod4.gemspec +1 -21
- data/spec/common/basic_model_spec.rb +37 -45
- data/spec/common/model_plus_typecasting_spec.rb +16 -16
- data/spec/common/model_spec.rb +96 -103
- data/spec/common/nebulous_interface_spec.rb +78 -74
- data/spec/common/sequel_interface_pg_spec.rb +23 -19
- data/spec/common/sql_helper_spec.rb +24 -14
- data/spec/jruby/sequel_interface_jdbc_ms_spec.rb +24 -17
- data/spec/jruby/sequel_interface_jdbc_pg_spec.rb +30 -4
- data/spec/mri/pg_interface_spec.rb +50 -41
- data/spec/mri/sequel_interface_spec.rb +48 -53
- data/spec/mri/tds_interface_spec.rb +51 -42
- data/tags +186 -47
- metadata +3 -3
data/spec/common/model_spec.rb
CHANGED
@@ -4,57 +4,50 @@ require 'pod4/model'
|
|
4
4
|
require 'pod4/null_interface'
|
5
5
|
|
6
6
|
|
7
|
-
|
8
|
-
# We define a model class to test, since in normal operation we would never use
|
9
|
-
# Model directly, and since it needs an inner Interface.
|
10
|
-
#
|
11
|
-
# We can't use a mock for the interface -- class definitions fall outside the
|
12
|
-
# RSpec DSL as far as I can tell, so I can neither create a mock here or inject
|
13
|
-
# it. Which means we can't mock the interface in the rest of the test either;
|
14
|
-
# any mock we created would not get called.
|
15
|
-
#
|
16
|
-
# But: we want to test that Model calls Interface correctly.
|
17
|
-
#
|
18
|
-
# We do have what appears to be a perfectly sane way of testing. We can define
|
19
|
-
# an inner class based on the genuinely existing, non-mock NullInterface class;
|
20
|
-
# and then define expectations on it. When we do this, Rspec fails to pass the
|
21
|
-
# call on to the object, unless we specifically say `.and_call_original`
|
22
|
-
# instead of `.and_return`.
|
23
|
-
#
|
24
|
-
# This is actually quite nice, but more than a little confusing when you see it
|
25
|
-
# for the first time. Its use isn't spelled out in the RSpec docs AFAICS.
|
26
|
-
#
|
27
|
-
class CustomerModel < Pod4::Model
|
28
|
-
attr_columns :id, :name, :groups
|
29
|
-
attr_columns :price # specifically testing multiple calls to attr_columns
|
30
|
-
set_interface NullInterface.new(:id, :name, :price, :groups, [])
|
31
|
-
|
32
|
-
def map_to_model(ot)
|
33
|
-
super
|
34
|
-
@groups = @groups ? @groups.split(',') : []
|
35
|
-
self
|
36
|
-
end
|
37
|
-
|
38
|
-
def map_to_interface
|
39
|
-
x = super
|
40
|
-
g = (x.>>.groups || []).join(',')
|
41
|
-
x.merge(groups: g)
|
42
|
-
end
|
43
|
-
|
44
|
-
def fake_an_alert(*args)
|
45
|
-
add_alert(*args) #private method
|
46
|
-
end
|
7
|
+
describe 'CustomerModel' do
|
47
8
|
|
48
|
-
|
49
|
-
|
50
|
-
|
9
|
+
##
|
10
|
+
# We define a model class to test, since in normal operation we would never use Model directly,
|
11
|
+
# and since it needs an inner Interface.
|
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
|
+
let(:customer_model_class) do
|
23
|
+
Class.new Pod4::Model do
|
24
|
+
attr_columns :id, :name, :groups
|
25
|
+
attr_columns :price # specifically testing multiple calls to attr_columns
|
26
|
+
set_interface NullInterface.new(:id, :name, :price, :groups, [])
|
27
|
+
|
28
|
+
def map_to_model(ot)
|
29
|
+
super
|
30
|
+
@groups = @groups ? @groups.split(',') : []
|
31
|
+
self
|
32
|
+
end
|
51
33
|
|
52
|
-
|
53
|
-
|
34
|
+
def map_to_interface
|
35
|
+
x = super
|
36
|
+
g = (x.>>.groups || []).join(',')
|
37
|
+
x.merge(groups: g)
|
38
|
+
end
|
54
39
|
|
40
|
+
def fake_an_alert(*args)
|
41
|
+
add_alert(*args) #private method
|
42
|
+
end
|
55
43
|
|
44
|
+
def validate
|
45
|
+
add_alert(:error, "falling over now") if name == "fall over"
|
46
|
+
end
|
56
47
|
|
57
|
-
|
48
|
+
def reset_alerts; @alerts = []; end
|
49
|
+
end
|
50
|
+
end
|
58
51
|
|
59
52
|
let(:records) do
|
60
53
|
[ {id: 10, name: 'Gomez', price: 1.23, groups: 'trains' },
|
@@ -79,17 +72,17 @@ describe 'CustomerModel' do
|
|
79
72
|
# model2 and model3 are in an identical state - they have been filled with a
|
80
73
|
# read(). We have two so that we can RSpec 'allow' on one and not the other.
|
81
74
|
|
82
|
-
let(:model) {
|
75
|
+
let(:model) { customer_model_class.new(20) }
|
83
76
|
|
84
77
|
let(:model2) do
|
85
|
-
m =
|
78
|
+
m = customer_model_class.new(30)
|
86
79
|
|
87
80
|
allow( m.interface ).to receive(:read).and_return( Octothorpe.new(records[2]) )
|
88
81
|
m.read.or_die
|
89
82
|
end
|
90
83
|
|
91
84
|
let(:model3) do
|
92
|
-
m =
|
85
|
+
m = customer_model_class.new(40)
|
93
86
|
|
94
87
|
allow( m.interface ).to receive(:read).and_return( Octothorpe.new(records[3]) )
|
95
88
|
m.read.or_die
|
@@ -99,7 +92,7 @@ describe 'CustomerModel' do
|
|
99
92
|
let(:thing) { Octothorpe.new(id: 'eek', name: 'thing', price: 9.99, groups: 'scuttering') }
|
100
93
|
|
101
94
|
let(:model4) do
|
102
|
-
m =
|
95
|
+
m = customer_model_class.new('eek')
|
103
96
|
|
104
97
|
allow( m.interface ).to receive(:read).and_return(thing)
|
105
98
|
m.read.or_die
|
@@ -111,18 +104,18 @@ describe 'CustomerModel' do
|
|
111
104
|
describe 'Model.attr_columns' do
|
112
105
|
|
113
106
|
it 'requires a list of columns' do
|
114
|
-
expect(
|
107
|
+
expect( customer_model_class ).to respond_to(:attr_columns).with(1).argument
|
115
108
|
end
|
116
109
|
|
117
110
|
it 'exposes the columns just like attr_accessor' do
|
118
|
-
expect(
|
119
|
-
expect(
|
120
|
-
expect(
|
121
|
-
expect(
|
122
|
-
expect(
|
123
|
-
expect(
|
124
|
-
expect(
|
125
|
-
expect(
|
111
|
+
expect( customer_model_class.new ).to respond_to(:id)
|
112
|
+
expect( customer_model_class.new ).to respond_to(:name)
|
113
|
+
expect( customer_model_class.new ).to respond_to(:price)
|
114
|
+
expect( customer_model_class.new ).to respond_to(:groups)
|
115
|
+
expect( customer_model_class.new ).to respond_to(:id=)
|
116
|
+
expect( customer_model_class.new ).to respond_to(:name=)
|
117
|
+
expect( customer_model_class.new ).to respond_to(:price=)
|
118
|
+
expect( customer_model_class.new ).to respond_to(:groups=)
|
126
119
|
end
|
127
120
|
|
128
121
|
# it adds the columns to Model.columns -- covered by the columns test
|
@@ -132,7 +125,7 @@ describe 'CustomerModel' do
|
|
132
125
|
|
133
126
|
describe 'Model.columns' do
|
134
127
|
it 'lists the columns' do
|
135
|
-
expect(
|
128
|
+
expect( customer_model_class.columns ).to match_array( [:id,:name,:price,:groups] )
|
136
129
|
end
|
137
130
|
end
|
138
131
|
##
|
@@ -140,7 +133,7 @@ describe 'CustomerModel' do
|
|
140
133
|
|
141
134
|
describe 'Model.set_interface' do
|
142
135
|
it 'requires an Interface object' do
|
143
|
-
expect(
|
136
|
+
expect( customer_model_class ).to respond_to(:set_interface).with(1).argument
|
144
137
|
end
|
145
138
|
|
146
139
|
# it 'sets interface' - covered by the interface test
|
@@ -150,8 +143,8 @@ describe 'CustomerModel' do
|
|
150
143
|
|
151
144
|
describe 'Model.interface' do
|
152
145
|
it 'is the interface object' do
|
153
|
-
expect(
|
154
|
-
expect(
|
146
|
+
expect( customer_model_class.interface ).to be_a_kind_of NullInterface
|
147
|
+
expect( customer_model_class.interface.id_fld ).to eq :id
|
155
148
|
end
|
156
149
|
end
|
157
150
|
##
|
@@ -159,7 +152,7 @@ describe 'CustomerModel' do
|
|
159
152
|
|
160
153
|
describe 'Model.list' do
|
161
154
|
|
162
|
-
let(:list1) {
|
155
|
+
let(:list1) { customer_model_class.list }
|
163
156
|
|
164
157
|
def arr_without_groups(arr)
|
165
158
|
arr
|
@@ -169,21 +162,21 @@ describe 'CustomerModel' do
|
|
169
162
|
end
|
170
163
|
|
171
164
|
it 'allows an optional selection parameter' do
|
172
|
-
expect{
|
173
|
-
expect{
|
165
|
+
expect{ customer_model_class.list }.not_to raise_exception
|
166
|
+
expect{ customer_model_class.list(name: 'Betty') }.not_to raise_exception
|
174
167
|
end
|
175
168
|
|
176
|
-
it 'returns an array of
|
177
|
-
expect(
|
169
|
+
it 'returns an array of customer_model_class records' do
|
170
|
+
expect( customer_model_class.interface ).
|
178
171
|
to receive(:list).with(nil).
|
179
172
|
and_return( records_as_ot )
|
180
173
|
|
181
174
|
expect( list1 ).to be_a_kind_of Array
|
182
|
-
expect( list1 ).to all(be_a_kind_of
|
175
|
+
expect( list1 ).to all(be_a_kind_of customer_model_class)
|
183
176
|
end
|
184
177
|
|
185
178
|
it 'returns the data from the interface' do
|
186
|
-
expect(
|
179
|
+
expect( customer_model_class.interface ).
|
187
180
|
to receive(:list).with(nil).
|
188
181
|
and_return(records_as_ot)
|
189
182
|
|
@@ -194,11 +187,11 @@ describe 'CustomerModel' do
|
|
194
187
|
it 'honours passed selection criteria' do
|
195
188
|
hash = {price: 2.22}
|
196
189
|
|
197
|
-
expect(
|
190
|
+
expect( customer_model_class.interface ).
|
198
191
|
to receive(:list).with(hash).
|
199
192
|
and_return( [Octothorpe.new(records[1])] )
|
200
193
|
|
201
|
-
list2 =
|
194
|
+
list2 = customer_model_class.list(hash)
|
202
195
|
expect( list2.size ).to eq 1
|
203
196
|
expect( arr_without_groups(list2).first ).to eq( recordsx[1] )
|
204
197
|
end
|
@@ -206,23 +199,23 @@ describe 'CustomerModel' do
|
|
206
199
|
it 'returns an empty array if nothing matches' do
|
207
200
|
hash = {price: 1.23}
|
208
201
|
|
209
|
-
expect(
|
202
|
+
expect( customer_model_class.interface ).
|
210
203
|
to receive(:list).with(hash).
|
211
204
|
and_return([])
|
212
205
|
|
213
|
-
expect(
|
206
|
+
expect( customer_model_class.list(hash) ).to eq []
|
214
207
|
end
|
215
208
|
|
216
209
|
it 'returns an empty array if there are no records' do
|
217
|
-
expect(
|
210
|
+
expect( customer_model_class.list ).to eq []
|
218
211
|
end
|
219
212
|
|
220
213
|
it 'calls map_to_model to set the record data' do
|
221
|
-
allow(
|
214
|
+
allow( customer_model_class.interface ).
|
222
215
|
to receive(:list).
|
223
216
|
and_return(records_as_ot)
|
224
217
|
|
225
|
-
expect(
|
218
|
+
expect( customer_model_class.list.last.groups ).to eq(['trains', 'school'])
|
226
219
|
end
|
227
220
|
|
228
221
|
end
|
@@ -232,25 +225,25 @@ describe 'CustomerModel' do
|
|
232
225
|
describe '#new' do
|
233
226
|
|
234
227
|
it 'takes an optional ID' do
|
235
|
-
expect{
|
236
|
-
expect{
|
228
|
+
expect{ customer_model_class.new }.not_to raise_exception
|
229
|
+
expect{ customer_model_class.new(1) }.not_to raise_exception
|
237
230
|
end
|
238
231
|
|
239
232
|
it 'sets the ID attribute' do
|
240
|
-
expect(
|
233
|
+
expect( customer_model_class.new(23).model_id ).to eq 23
|
241
234
|
end
|
242
235
|
|
243
236
|
it 'sets the status to empty' do
|
244
|
-
expect(
|
237
|
+
expect( customer_model_class.new.model_status ).to eq :empty
|
245
238
|
end
|
246
239
|
|
247
240
|
it 'initializes the alerts attribute' do
|
248
|
-
expect(
|
241
|
+
expect( customer_model_class.new.alerts ).to eq([])
|
249
242
|
end
|
250
243
|
|
251
244
|
it 'doesn''t freak out if the ID is not an integer' do
|
252
|
-
expect{
|
253
|
-
expect(
|
245
|
+
expect{ customer_model_class.new("france") }.not_to raise_exception
|
246
|
+
expect( customer_model_class.new("france").model_id ).to eq "france"
|
254
247
|
end
|
255
248
|
|
256
249
|
end
|
@@ -259,8 +252,8 @@ describe 'CustomerModel' do
|
|
259
252
|
|
260
253
|
describe '#interface' do
|
261
254
|
it 'returns the interface set in the class definition, again' do
|
262
|
-
expect(
|
263
|
-
expect(
|
255
|
+
expect( customer_model_class.new.interface ).to be_a_kind_of NullInterface
|
256
|
+
expect( customer_model_class.new.interface.id_fld ).to eq :id
|
264
257
|
end
|
265
258
|
end
|
266
259
|
##
|
@@ -269,7 +262,7 @@ describe 'CustomerModel' do
|
|
269
262
|
describe '#columns' do
|
270
263
|
it 'returns the attr_columns list from the class definition' do
|
271
264
|
|
272
|
-
expect(
|
265
|
+
expect( customer_model_class.new.columns ).
|
273
266
|
to match_array( [:id,:name,:price,:groups] )
|
274
267
|
|
275
268
|
end
|
@@ -279,7 +272,7 @@ describe 'CustomerModel' do
|
|
279
272
|
|
280
273
|
describe '#alerts' do
|
281
274
|
it 'returns the list of alerts against the model' do
|
282
|
-
cm =
|
275
|
+
cm = customer_model_class.new
|
283
276
|
cm.fake_an_alert(:warning, :foo, 'one')
|
284
277
|
cm.fake_an_alert(:error, :bar, 'two')
|
285
278
|
|
@@ -356,8 +349,8 @@ describe 'CustomerModel' do
|
|
356
349
|
|
357
350
|
describe '#validate' do
|
358
351
|
it 'takes no parameters' do
|
359
|
-
expect{
|
360
|
-
expect{
|
352
|
+
expect{ customer_model_class.new.validate(12) }.to raise_exception ArgumentError
|
353
|
+
expect{ customer_model_class.new.validate }.not_to raise_exception
|
361
354
|
end
|
362
355
|
end
|
363
356
|
##
|
@@ -423,7 +416,7 @@ describe 'CustomerModel' do
|
|
423
416
|
describe '#map_to_model' do
|
424
417
|
|
425
418
|
it 'sets the columns, with groups as an array' do
|
426
|
-
cm =
|
419
|
+
cm = customer_model_class.new
|
427
420
|
cm.map_to_model(records.last)
|
428
421
|
|
429
422
|
expect( cm.groups ).to eq( ['trains','school'] )
|
@@ -436,7 +429,7 @@ describe 'CustomerModel' do
|
|
436
429
|
describe '#map_to_interface' do
|
437
430
|
|
438
431
|
it 'returns the columns, with groups as a list' do
|
439
|
-
cm =
|
432
|
+
cm = customer_model_class.new
|
440
433
|
cm.map_to_model(records.last)
|
441
434
|
|
442
435
|
expect( cm.map_to_interface.>>.groups ).to eq( 'trains,school' )
|
@@ -449,7 +442,7 @@ describe 'CustomerModel' do
|
|
449
442
|
describe '#raise_exceptions' do
|
450
443
|
|
451
444
|
it 'is also known as .or_die' do
|
452
|
-
cm =
|
445
|
+
cm = customer_model_class.new
|
453
446
|
expect( cm.method(:raise_exceptions) ).to eq( cm.method(:or_die) )
|
454
447
|
end
|
455
448
|
|
@@ -477,11 +470,11 @@ describe 'CustomerModel' do
|
|
477
470
|
|
478
471
|
describe '#create' do
|
479
472
|
|
480
|
-
let (:new_model) {
|
473
|
+
let (:new_model) { customer_model_class.new }
|
481
474
|
|
482
475
|
it 'takes no parameters' do
|
483
|
-
expect{
|
484
|
-
expect{
|
476
|
+
expect{ customer_model_class.new.create(12) }.to raise_exception ArgumentError
|
477
|
+
expect{ customer_model_class.new.create }.not_to raise_exception
|
485
478
|
end
|
486
479
|
|
487
480
|
it 'returns self' do
|
@@ -494,8 +487,8 @@ describe 'CustomerModel' do
|
|
494
487
|
end
|
495
488
|
|
496
489
|
it 'calls create on the interface if the record is good' do
|
497
|
-
expect(
|
498
|
-
|
490
|
+
expect( customer_model_class.interface ).to receive(:create)
|
491
|
+
customer_model_class.new.create
|
499
492
|
|
500
493
|
new_model.fake_an_alert(:warning, :name, 'foo')
|
501
494
|
expect( new_model.interface ).to receive(:create)
|
@@ -558,8 +551,8 @@ describe 'CustomerModel' do
|
|
558
551
|
describe '#read' do
|
559
552
|
|
560
553
|
it 'takes no parameters' do
|
561
|
-
expect{
|
562
|
-
expect{
|
554
|
+
expect{ customer_model_class.new.create(12) }.to raise_exception ArgumentError
|
555
|
+
expect{ customer_model_class.new.create }.not_to raise_exception
|
563
556
|
end
|
564
557
|
|
565
558
|
it 'returns self ' do
|
@@ -591,7 +584,7 @@ describe 'CustomerModel' do
|
|
591
584
|
ot = records_as_ot.last
|
592
585
|
allow( model.interface ).to receive(:read).and_return( ot )
|
593
586
|
|
594
|
-
cm =
|
587
|
+
cm = customer_model_class.new(10).read
|
595
588
|
expect( cm.id ).to eq ot.>>.id
|
596
589
|
expect( cm.name ).to eq ot.>>.name
|
597
590
|
expect( cm.price ).to eq ot.>>.price
|
@@ -619,11 +612,11 @@ describe 'CustomerModel' do
|
|
619
612
|
it 'doesn\'t freak out if the model is non-integer' do
|
620
613
|
allow( model.interface ).to receive(:read).and_return( thing )
|
621
614
|
|
622
|
-
expect{
|
615
|
+
expect{ customer_model_class.new('eek').read }.not_to raise_error
|
623
616
|
end
|
624
617
|
|
625
618
|
context 'if the interface.read returns an empty Octothorpe' do
|
626
|
-
let(:missing) {
|
619
|
+
let(:missing) { customer_model_class.new(99) }
|
627
620
|
|
628
621
|
it 'doesn\'t throw an exception' do
|
629
622
|
expect{ missing.read }.not_to raise_exception
|
@@ -1,42 +1,21 @@
|
|
1
1
|
require 'pod4/nebulous_interface'
|
2
2
|
|
3
3
|
require 'nebulous_stomp'
|
4
|
-
require 'nebulous_stomp/
|
4
|
+
require 'nebulous_stomp/stomp_handler_null'
|
5
5
|
|
6
6
|
require_relative 'shared_examples_for_interface'
|
7
7
|
|
8
8
|
|
9
9
|
##
|
10
|
-
#
|
11
|
-
#
|
12
|
-
# subclass NebulousInterface for your model, you don't have to follow that; you
|
13
|
-
# can have a parameter per nebulous parameter, if you want, buy overriding the
|
14
|
-
# CRUDL methods.
|
10
|
+
# This is the class we will pass an instance of to NebulousInterface to use as a cut-out for
|
11
|
+
# creating Nebulous::NebRequest objects.
|
15
12
|
#
|
16
|
-
class
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
set_verb :create, 'custcreate', :name, :price
|
21
|
-
set_verb :read, 'custread', :id
|
22
|
-
set_verb :update, 'custupdate', :id, :name, :price
|
23
|
-
set_verb :delete, 'custdelete', :id
|
24
|
-
set_verb :list, 'custlist', :name
|
25
|
-
end
|
26
|
-
##
|
27
|
-
|
28
|
-
|
29
|
-
##
|
30
|
-
# This is the class we will pass an instance of to NebulousInterface to use as
|
31
|
-
# a cut-out for creating Nebulous::NebRequest objects.
|
32
|
-
#
|
33
|
-
# If we pass an instance of this class to NebulousInterface it will call our
|
34
|
-
# send method instead of creating a NebRequest instance by itself. (It expects
|
35
|
-
# send to return a Nebrequest instance, or something that behaves like one.)
|
13
|
+
# If we pass an instance of this class to NebulousInterface it will call our send method instead of
|
14
|
+
# creating a Request instance by itself. (It expects send to return a Request instance, or
|
15
|
+
# something that behaves like one.)
|
36
16
|
#
|
37
|
-
# This means we can cut Nebulous out of the loop and don't need a real
|
38
|
-
#
|
39
|
-
# RSpec 'inspect' syntax on our cutout object.
|
17
|
+
# This means we can cut Nebulous out of the loop and don't need a real responder. We can also check
|
18
|
+
# the behaviour of NebulousInterface by using RSpec 'inspect' syntax on our cutout object.
|
40
19
|
#
|
41
20
|
# We're basically emulating both a responder and a data source here (!)
|
42
21
|
#
|
@@ -44,68 +23,72 @@ class FakeRequester
|
|
44
23
|
|
45
24
|
def initialize(data={}); @data = data; end
|
46
25
|
|
26
|
+
##
|
27
|
+
# NebulousInterface will call this to return a NebulousStomp::Request object, or something that
|
28
|
+
# behaves like one.
|
29
|
+
#
|
30
|
+
def send(target, requestmsg)
|
31
|
+
hash = response_message_hash(requestmsg)
|
32
|
+
|
33
|
+
stomphandler = NebulousStomp::StompHandlerNull.new
|
34
|
+
|
35
|
+
request = NebulousStomp::Request.new(target, requestmsg)
|
36
|
+
request.stomp_handler = stomphandler
|
37
|
+
|
38
|
+
hash[:inReplyTo] = request.message.reply_id
|
39
|
+
responsemsg = NebulousStomp::Message.new hash
|
40
|
+
stomphandler.insert_fake(responsemsg)
|
41
|
+
|
42
|
+
request
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def create(name, price)
|
48
|
+
id = @data.keys.sort.last.to_i + 1
|
49
|
+
@data[id] = {id: id, name: name, price: price.to_f}
|
50
|
+
id
|
51
|
+
end
|
52
|
+
|
53
|
+
def update(id, name, price)
|
54
|
+
return nil unless @data[id.to_i]
|
55
|
+
@data[id.to_i] = {id: id.to_i, name: name, price: price.to_f}
|
56
|
+
end
|
47
57
|
|
48
|
-
def
|
49
|
-
|
58
|
+
def response_message_hash(requestmsg)
|
59
|
+
hash1 = { contentType: 'application/json' }
|
50
60
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
inReplyTo: nil,
|
59
|
-
contentType: 'application/json' }
|
61
|
+
if requestmsg.params.is_a?(Array)
|
62
|
+
array = requestmsg.params
|
63
|
+
paramstr = array.join(',')
|
64
|
+
else
|
65
|
+
paramstr = requestmsg.params.to_s
|
66
|
+
array = paramstr.split(',')
|
67
|
+
end
|
60
68
|
|
61
|
-
case verb
|
69
|
+
case requestmsg.verb
|
62
70
|
when 'custcreate'
|
63
71
|
id = create(*array)
|
64
72
|
hash2 = {verb: 'success', params: id.to_s}
|
65
73
|
|
66
74
|
when 'custread'
|
67
|
-
record = @data[
|
75
|
+
record = @data[paramstr.to_i]
|
68
76
|
hash2 = { stompBody: (record ? record.to_json : ''.to_json) }
|
69
77
|
|
70
78
|
when 'custupdate'
|
71
79
|
hash2 = update(*array) ? {verb: 'success'} : {verb: 'error' }
|
72
80
|
|
73
81
|
when 'custdelete'
|
74
|
-
hash2 =
|
75
|
-
if @data.delete(paramStr.to_i)
|
76
|
-
{verb: 'success'}
|
77
|
-
else
|
78
|
-
{verb: 'error'}
|
79
|
-
end
|
82
|
+
hash2 = @data.delete(paramstr.to_i) ? {verb: 'success'} : {verb: 'error'}
|
80
83
|
|
81
84
|
when 'custlist'
|
82
85
|
subset = @data.values
|
83
|
-
if
|
84
|
-
subset.select!{|x| x[:name] == array[0] }
|
85
|
-
end
|
86
|
+
subset.select!{|x| x[:name] == array[0] } if (!paramstr.empty? && !array[0].empty?)
|
86
87
|
hash2 = { stompBody: subset.to_json }
|
87
88
|
|
88
89
|
end
|
89
90
|
|
90
|
-
|
91
|
-
hash2[:inReplyTo] = req.replyID
|
92
|
-
|
93
|
-
mess = NebulousStomp::Message.from_cache( hash1.merge(hash2).to_json )
|
94
|
-
req.insert_fake_stomp(mess)
|
95
|
-
req
|
96
|
-
end
|
97
|
-
|
98
|
-
|
99
|
-
def create(name, price)
|
100
|
-
id = @data.keys.sort.last.to_i + 1
|
101
|
-
@data[id] = {id: id, name: name, price: price.to_f}
|
102
|
-
id
|
103
|
-
end
|
104
|
-
|
105
|
-
|
106
|
-
def update(id, name, price)
|
107
|
-
return nil unless @data[id.to_i]
|
108
|
-
@data[id.to_i] = {id: id.to_i, name: name, price: price.to_f}
|
91
|
+
hash1.merge(hash2)
|
109
92
|
end
|
110
93
|
|
111
94
|
end
|
@@ -113,7 +96,28 @@ end
|
|
113
96
|
|
114
97
|
|
115
98
|
|
116
|
-
describe
|
99
|
+
describe "NebulousInterface" do
|
100
|
+
|
101
|
+
##
|
102
|
+
# In order to conform with 'acts_like an interface', our CRUDL routines must
|
103
|
+
# pass a single value as the record, which we are making an array. When you
|
104
|
+
# subclass NebulousInterface for your model, you don't have to follow that; you
|
105
|
+
# can have a parameter per nebulous parameter, if you want, buy overriding the
|
106
|
+
# CRUDL methods.
|
107
|
+
#
|
108
|
+
let(:nebulous_interface_class) do
|
109
|
+
Class.new NebulousInterface do
|
110
|
+
set_target 'faketarget'
|
111
|
+
set_id_fld :id
|
112
|
+
|
113
|
+
set_verb :create, 'custcreate', :name, :price
|
114
|
+
set_verb :read, 'custread', :id
|
115
|
+
set_verb :update, 'custupdate', :id, :name, :price
|
116
|
+
set_verb :delete, 'custdelete', :id
|
117
|
+
set_verb :list, 'custlist', :name
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
117
121
|
|
118
122
|
def init_nebulous
|
119
123
|
stomp_hash = { hosts: [{ login: 'guest',
|
@@ -142,7 +146,7 @@ describe TestNebulousInterface do
|
|
142
146
|
|
143
147
|
let(:interface) do
|
144
148
|
init_nebulous
|
145
|
-
|
149
|
+
nebulous_interface_class.new( FakeRequester.new )
|
146
150
|
end
|
147
151
|
|
148
152
|
end
|
@@ -157,14 +161,14 @@ describe TestNebulousInterface do
|
|
157
161
|
|
158
162
|
let(:interface) do
|
159
163
|
init_nebulous
|
160
|
-
|
164
|
+
nebulous_interface_class.new( FakeRequester.new(data) )
|
161
165
|
end
|
162
166
|
|
163
167
|
|
164
168
|
describe '#new' do
|
165
169
|
|
166
170
|
it 'requires no parameters' do
|
167
|
-
expect{
|
171
|
+
expect{ nebulous_interface_class.new }.not_to raise_exception
|
168
172
|
end
|
169
173
|
|
170
174
|
end
|