pod4 0.8.0 → 0.8.1

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