pod4 0.8.0 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- def validate
49
- add_alert(:error, "falling over now") if name == "fall over"
50
- end
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
- def reset_alerts; @alerts = []; end
53
- end
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
- describe 'CustomerModel' do
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) { CustomerModel.new(20) }
75
+ let(:model) { customer_model_class.new(20) }
83
76
 
84
77
  let(:model2) do
85
- m = CustomerModel.new(30)
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 = CustomerModel.new(40)
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 = CustomerModel.new('eek')
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( CustomerModel ).to respond_to(:attr_columns).with(1).argument
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( CustomerModel.new ).to respond_to(:id)
119
- expect( CustomerModel.new ).to respond_to(:name)
120
- expect( CustomerModel.new ).to respond_to(:price)
121
- expect( CustomerModel.new ).to respond_to(:groups)
122
- expect( CustomerModel.new ).to respond_to(:id=)
123
- expect( CustomerModel.new ).to respond_to(:name=)
124
- expect( CustomerModel.new ).to respond_to(:price=)
125
- expect( CustomerModel.new ).to respond_to(:groups=)
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( CustomerModel.columns ).to match_array( [:id,:name,:price,:groups] )
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( CustomerModel ).to respond_to(:set_interface).with(1).argument
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( CustomerModel.interface ).to be_a_kind_of NullInterface
154
- expect( CustomerModel.interface.id_fld ).to eq :id
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) { CustomerModel.list }
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{ CustomerModel.list }.not_to raise_exception
173
- expect{ CustomerModel.list(name: 'Betty') }.not_to raise_exception
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 CustomerModel records' do
177
- expect( CustomerModel.interface ).
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 CustomerModel)
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( CustomerModel.interface ).
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( CustomerModel.interface ).
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 = CustomerModel.list(hash)
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( CustomerModel.interface ).
202
+ expect( customer_model_class.interface ).
210
203
  to receive(:list).with(hash).
211
204
  and_return([])
212
205
 
213
- expect( CustomerModel.list(hash) ).to eq []
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( CustomerModel.list ).to eq []
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( CustomerModel.interface ).
214
+ allow( customer_model_class.interface ).
222
215
  to receive(:list).
223
216
  and_return(records_as_ot)
224
217
 
225
- expect( CustomerModel.list.last.groups ).to eq(['trains', 'school'])
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{ CustomerModel.new }.not_to raise_exception
236
- expect{ CustomerModel.new(1) }.not_to raise_exception
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( CustomerModel.new(23).model_id ).to eq 23
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( CustomerModel.new.model_status ).to eq :empty
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( CustomerModel.new.alerts ).to eq([])
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{ CustomerModel.new("france") }.not_to raise_exception
253
- expect( CustomerModel.new("france").model_id ).to eq "france"
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( CustomerModel.new.interface ).to be_a_kind_of NullInterface
263
- expect( CustomerModel.new.interface.id_fld ).to eq :id
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( CustomerModel.new.columns ).
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 = CustomerModel.new
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{ CustomerModel.new.validate(12) }.to raise_exception ArgumentError
360
- expect{ CustomerModel.new.validate }.not_to raise_exception
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 = CustomerModel.new
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 = CustomerModel.new
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 = CustomerModel.new
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) { CustomerModel.new }
473
+ let (:new_model) { customer_model_class.new }
481
474
 
482
475
  it 'takes no parameters' do
483
- expect{ CustomerModel.new.create(12) }.to raise_exception ArgumentError
484
- expect{ CustomerModel.new.create }.not_to raise_exception
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( CustomerModel.interface ).to receive(:create)
498
- CustomerModel.new.create
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{ CustomerModel.new.create(12) }.to raise_exception ArgumentError
562
- expect{ CustomerModel.new.create }.not_to raise_exception
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 = CustomerModel.new(10).read
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{ CustomerModel.new('eek').read }.not_to raise_error
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) { CustomerModel.new(99) }
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/nebrequest_null'
4
+ require 'nebulous_stomp/stomp_handler_null'
5
5
 
6
6
  require_relative 'shared_examples_for_interface'
7
7
 
8
8
 
9
9
  ##
10
- # In order to conform with 'acts_like an interface', our CRUDL routines must
11
- # pass a single value as the record, which we are making an array. When you
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 TestNebulousInterface < NebulousInterface
17
- set_target 'faketarget'
18
- set_id_fld :id
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
- # responder. We can also check the behaviour of NebulousInterface by using
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 send(verb, paramStr, withCache)
49
- array = (paramStr || '').split(',')
58
+ def response_message_hash(requestmsg)
59
+ hash1 = { contentType: 'application/json' }
50
60
 
51
- hash1 = { stompHeaders: nil,
52
- stompBody: '',
53
- verb: '',
54
- params: '',
55
- desc: '',
56
- replyTo: nil,
57
- replyId: nil,
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[paramStr.to_i]
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 paramStr && !array[0].empty?
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
- req = NebulousStomp::NebRequestNull.new('faketarget', verb, paramStr)
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 TestNebulousInterface do
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
- TestNebulousInterface.new( FakeRequester.new )
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
- TestNebulousInterface.new( FakeRequester.new(data) )
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{ TestNebulousInterface.new }.not_to raise_exception
171
+ expect{ nebulous_interface_class.new }.not_to raise_exception
168
172
  end
169
173
 
170
174
  end