cuprum-rails 0.1.0 → 0.2.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/CHANGELOG.md +145 -0
- data/DEVELOPMENT.md +20 -0
- data/README.md +356 -63
- data/lib/cuprum/rails/action.rb +32 -16
- data/lib/cuprum/rails/actions/create.rb +62 -15
- data/lib/cuprum/rails/actions/destroy.rb +23 -7
- data/lib/cuprum/rails/actions/edit.rb +23 -7
- data/lib/cuprum/rails/actions/index.rb +30 -10
- data/lib/cuprum/rails/actions/middleware/associations/cache.rb +112 -0
- data/lib/cuprum/rails/actions/middleware/associations/find.rb +23 -0
- data/lib/cuprum/rails/actions/middleware/associations/parent.rb +70 -0
- data/lib/cuprum/rails/actions/middleware/associations/query.rb +140 -0
- data/lib/cuprum/rails/actions/middleware/associations.rb +12 -0
- data/lib/cuprum/rails/actions/middleware/log_request.rb +126 -0
- data/lib/cuprum/rails/actions/middleware/log_result.rb +51 -0
- data/lib/cuprum/rails/actions/middleware/resources/find.rb +44 -0
- data/lib/cuprum/rails/actions/middleware/resources/query.rb +91 -0
- data/lib/cuprum/rails/actions/middleware/resources.rb +11 -0
- data/lib/cuprum/rails/actions/middleware.rb +13 -0
- data/lib/cuprum/rails/actions/new.rb +16 -4
- data/lib/cuprum/rails/actions/parameter_validation.rb +60 -0
- data/lib/cuprum/rails/actions/resource_action.rb +119 -42
- data/lib/cuprum/rails/actions/show.rb +23 -7
- data/lib/cuprum/rails/actions/update.rb +70 -22
- data/lib/cuprum/rails/actions.rb +11 -7
- data/lib/cuprum/rails/collection.rb +27 -47
- data/lib/cuprum/rails/command.rb +3 -1
- data/lib/cuprum/rails/commands/destroy_one.rb +10 -6
- data/lib/cuprum/rails/commands/find_many.rb +8 -1
- data/lib/cuprum/rails/commands/find_matching.rb +1 -1
- data/lib/cuprum/rails/commands/find_one.rb +8 -0
- data/lib/cuprum/rails/commands/insert_one.rb +17 -6
- data/lib/cuprum/rails/commands/update_one.rb +16 -5
- data/lib/cuprum/rails/constraints/parameters_contract.rb +14 -0
- data/lib/cuprum/rails/constraints.rb +10 -0
- data/lib/cuprum/rails/controller.rb +12 -2
- data/lib/cuprum/rails/controllers/action.rb +100 -0
- data/lib/cuprum/rails/controllers/class_methods/actions.rb +33 -7
- data/lib/cuprum/rails/controllers/class_methods/configuration.rb +36 -0
- data/lib/cuprum/rails/controllers/class_methods/middleware.rb +88 -0
- data/lib/cuprum/rails/controllers/class_methods/validations.rb +2 -2
- data/lib/cuprum/rails/controllers/configuration.rb +41 -1
- data/lib/cuprum/rails/controllers/middleware.rb +59 -0
- data/lib/cuprum/rails/controllers.rb +2 -0
- data/lib/cuprum/rails/errors/invalid_parameters.rb +55 -0
- data/lib/cuprum/rails/errors/invalid_statement.rb +11 -0
- data/lib/cuprum/rails/errors/missing_parameter.rb +42 -0
- data/lib/cuprum/rails/errors/resource_error.rb +46 -0
- data/lib/cuprum/rails/errors.rb +6 -1
- data/lib/cuprum/rails/map_errors.rb +29 -1
- data/lib/cuprum/rails/query.rb +1 -1
- data/lib/cuprum/rails/repository.rb +12 -25
- data/lib/cuprum/rails/request.rb +149 -60
- data/lib/cuprum/rails/resource.rb +119 -85
- data/lib/cuprum/rails/responders/base_responder.rb +78 -0
- data/lib/cuprum/rails/responders/html/plural_resource.rb +9 -39
- data/lib/cuprum/rails/responders/html/rendering.rb +81 -0
- data/lib/cuprum/rails/responders/html/resource.rb +107 -0
- data/lib/cuprum/rails/responders/html/singular_resource.rb +9 -38
- data/lib/cuprum/rails/responders/html.rb +2 -0
- data/lib/cuprum/rails/responders/html_responder.rb +8 -52
- data/lib/cuprum/rails/responders/json/resource.rb +3 -3
- data/lib/cuprum/rails/responders/json_responder.rb +31 -16
- data/lib/cuprum/rails/responders/matching.rb +29 -27
- data/lib/cuprum/rails/responders/serialization.rb +11 -9
- data/lib/cuprum/rails/responders.rb +1 -0
- data/lib/cuprum/rails/responses/head_response.rb +24 -0
- data/lib/cuprum/rails/responses/html/redirect_back_response.rb +55 -0
- data/lib/cuprum/rails/responses/html/redirect_response.rb +19 -4
- data/lib/cuprum/rails/responses/html/render_response.rb +17 -5
- data/lib/cuprum/rails/responses/html.rb +6 -2
- data/lib/cuprum/rails/responses.rb +1 -0
- data/lib/cuprum/rails/result.rb +36 -0
- data/lib/cuprum/rails/routes.rb +36 -23
- data/lib/cuprum/rails/rspec/contract_helpers.rb +57 -0
- data/lib/cuprum/rails/rspec/contracts/action_contracts.rb +754 -0
- data/lib/cuprum/rails/rspec/contracts/actions/create_contracts.rb +289 -0
- data/lib/cuprum/rails/rspec/contracts/actions/destroy_contracts.rb +164 -0
- data/lib/cuprum/rails/rspec/contracts/actions/edit_contracts.rb +73 -0
- data/lib/cuprum/rails/rspec/contracts/actions/index_contracts.rb +108 -0
- data/lib/cuprum/rails/rspec/contracts/actions/new_contracts.rb +111 -0
- data/lib/cuprum/rails/rspec/contracts/actions/show_contracts.rb +72 -0
- data/lib/cuprum/rails/rspec/contracts/actions/update_contracts.rb +263 -0
- data/lib/cuprum/rails/rspec/contracts/actions.rb +8 -0
- data/lib/cuprum/rails/rspec/contracts/command_contracts.rb +479 -0
- data/lib/cuprum/rails/rspec/contracts/responder_contracts.rb +232 -0
- data/lib/cuprum/rails/rspec/contracts/routes_contracts.rb +363 -0
- data/lib/cuprum/rails/rspec/contracts/serializers_contracts.rb +70 -0
- data/lib/cuprum/rails/rspec/contracts.rb +8 -0
- data/lib/cuprum/rails/rspec/matchers/be_a_result_matcher.rb +64 -0
- data/lib/cuprum/rails/rspec/matchers.rb +41 -0
- data/lib/cuprum/rails/serializers/base_serializer.rb +60 -0
- data/lib/cuprum/rails/serializers/context.rb +84 -0
- data/lib/cuprum/rails/serializers/json/active_record_serializer.rb +2 -2
- data/lib/cuprum/rails/serializers/json/array_serializer.rb +9 -8
- data/lib/cuprum/rails/serializers/json/attributes_serializer.rb +95 -172
- data/lib/cuprum/rails/serializers/json/error_serializer.rb +2 -2
- data/lib/cuprum/rails/serializers/json/hash_serializer.rb +9 -8
- data/lib/cuprum/rails/serializers/json/identity_serializer.rb +3 -3
- data/lib/cuprum/rails/serializers/json/properties_serializer.rb +252 -0
- data/lib/cuprum/rails/serializers/json.rb +2 -1
- data/lib/cuprum/rails/serializers.rb +3 -1
- data/lib/cuprum/rails/version.rb +1 -1
- data/lib/cuprum/rails.rb +19 -16
- metadata +73 -131
- data/lib/cuprum/rails/controller_action.rb +0 -121
- data/lib/cuprum/rails/errors/missing_parameters.rb +0 -33
- data/lib/cuprum/rails/errors/missing_primary_key.rb +0 -46
- data/lib/cuprum/rails/errors/undefined_permitted_attributes.rb +0 -34
- data/lib/cuprum/rails/rspec/command_contract.rb +0 -460
- data/lib/cuprum/rails/rspec/define_route_contract.rb +0 -84
- data/lib/cuprum/rails/serializers/json/serializer.rb +0 -66
@@ -0,0 +1,479 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rspec/sleeping_king_studios/contract'
|
4
|
+
|
5
|
+
require 'cuprum/rails/rspec/contracts'
|
6
|
+
|
7
|
+
require 'support/book'
|
8
|
+
require 'support/tome'
|
9
|
+
|
10
|
+
module Cuprum::Rails::RSpec::Contracts
|
11
|
+
# Namespace for RSpec command contracts.
|
12
|
+
module CommandContracts
|
13
|
+
# Contract validating the behavior of a Rails command implementation.
|
14
|
+
module ShouldBeARailsCommandContract
|
15
|
+
extend RSpec::SleepingKingStudios::Contract
|
16
|
+
|
17
|
+
# @!method apply(example_group)
|
18
|
+
# Adds the contract to the example group.
|
19
|
+
#
|
20
|
+
# @param example_group [RSpec::Core::ExampleGroup] the example group to
|
21
|
+
# which the contract is applied.
|
22
|
+
contract do
|
23
|
+
describe '.subclass' do
|
24
|
+
let(:subclass) { described_class.subclass }
|
25
|
+
let(:constructor_options) do
|
26
|
+
{
|
27
|
+
record_class: Book,
|
28
|
+
optional_key: 'optional value'
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should define the class method' do
|
33
|
+
expect(described_class)
|
34
|
+
.to respond_to(:subclass)
|
35
|
+
.with(0).arguments
|
36
|
+
.and_any_keywords
|
37
|
+
end
|
38
|
+
|
39
|
+
it { expect(subclass).to be_a Class }
|
40
|
+
|
41
|
+
it { expect(subclass).to be < described_class }
|
42
|
+
|
43
|
+
it 'should define the constructor' do
|
44
|
+
expect(subclass)
|
45
|
+
.to respond_to(:new)
|
46
|
+
.with(0).arguments
|
47
|
+
.and_any_keywords
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should return the record class' do
|
51
|
+
expect(subclass.new(**constructor_options).record_class)
|
52
|
+
.to be record_class
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should return the options' do
|
56
|
+
expect(subclass.new(**constructor_options).options)
|
57
|
+
.to be == { optional_key: 'optional value' }
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'with options' do
|
61
|
+
let(:default_options) do
|
62
|
+
{
|
63
|
+
record_class: Book,
|
64
|
+
custom_key: 'custom value'
|
65
|
+
}
|
66
|
+
end
|
67
|
+
let(:constructor_options) do
|
68
|
+
{
|
69
|
+
optional_key: 'optional value'
|
70
|
+
}
|
71
|
+
end
|
72
|
+
let(:subclass) { described_class.subclass(**default_options) }
|
73
|
+
|
74
|
+
it { expect(subclass).to be_a Class }
|
75
|
+
|
76
|
+
it { expect(subclass).to be < described_class }
|
77
|
+
|
78
|
+
it 'should define the constructor' do
|
79
|
+
expect(subclass)
|
80
|
+
.to respond_to(:new)
|
81
|
+
.with(0).arguments
|
82
|
+
.and_any_keywords
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should return the record class' do
|
86
|
+
expect(subclass.new(**constructor_options).record_class)
|
87
|
+
.to be record_class
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'should return the options' do
|
91
|
+
expect(subclass.new(**constructor_options).options)
|
92
|
+
.to be == {
|
93
|
+
custom_key: 'custom value',
|
94
|
+
optional_key: 'optional value'
|
95
|
+
}
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe '#collection_name' do
|
101
|
+
let(:expected) { record_class.name.underscore.pluralize }
|
102
|
+
|
103
|
+
include_examples 'should define reader',
|
104
|
+
:collection_name,
|
105
|
+
-> { be == expected }
|
106
|
+
|
107
|
+
context 'when initialized with collection_name: string' do
|
108
|
+
let(:collection_name) { 'books' }
|
109
|
+
let(:constructor_options) do
|
110
|
+
super().merge(collection_name: collection_name)
|
111
|
+
end
|
112
|
+
|
113
|
+
it { expect(command.collection_name).to be == collection_name }
|
114
|
+
end
|
115
|
+
|
116
|
+
context 'when initialized with collection_name: symbol' do
|
117
|
+
let(:collection_name) { :books }
|
118
|
+
let(:constructor_options) do
|
119
|
+
super().merge(collection_name: collection_name)
|
120
|
+
end
|
121
|
+
|
122
|
+
it { expect(command.collection_name).to be == collection_name.to_s }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe '#member_name' do
|
127
|
+
def tools
|
128
|
+
SleepingKingStudios::Tools::Toolbelt.instance
|
129
|
+
end
|
130
|
+
|
131
|
+
include_examples 'should have reader',
|
132
|
+
:member_name,
|
133
|
+
-> { record_class.name.underscore }
|
134
|
+
|
135
|
+
context 'when initialized with collection_name: value' do
|
136
|
+
let(:collection_name) { :books }
|
137
|
+
|
138
|
+
it 'should return the singular collection name' do
|
139
|
+
expect(command.member_name)
|
140
|
+
.to be == tools.str.singularize(collection_name.to_s)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context 'when initialized with member_name: string' do
|
145
|
+
let(:member_name) { 'tome' }
|
146
|
+
let(:constructor_options) do
|
147
|
+
super().merge(member_name: member_name)
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'should return the singular collection name' do
|
151
|
+
expect(command.member_name).to be member_name
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
context 'when initialized with member_name: symbol' do
|
156
|
+
let(:member_name) { :tome }
|
157
|
+
let(:constructor_options) do
|
158
|
+
super().merge(member_name: member_name)
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'should return the singular collection name' do
|
162
|
+
expect(command.member_name).to be == member_name.to_s
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe '#options' do
|
168
|
+
let(:expected_options) do
|
169
|
+
defined?(super()) ? super() : constructor_options
|
170
|
+
end
|
171
|
+
|
172
|
+
include_examples 'should define reader',
|
173
|
+
:options,
|
174
|
+
-> { be == expected_options }
|
175
|
+
|
176
|
+
context 'when initialized with options' do
|
177
|
+
let(:constructor_options) { super().merge({ key: 'value' }) }
|
178
|
+
let(:expected_options) { super().merge({ key: 'value' }) }
|
179
|
+
|
180
|
+
it { expect(command.options).to be == expected_options }
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe '#primary_key_name' do
|
185
|
+
include_examples 'should define reader', :primary_key_name, 'id'
|
186
|
+
|
187
|
+
context 'with a record class with custom primary key' do
|
188
|
+
let(:record_class) { Tome }
|
189
|
+
|
190
|
+
include_examples 'should define reader', :primary_key_name, 'uuid'
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe '#primary_key_type' do
|
195
|
+
include_examples 'should define reader', :primary_key_type, Integer
|
196
|
+
|
197
|
+
context 'with a record class with custom primary key' do
|
198
|
+
let(:record_class) { Tome }
|
199
|
+
|
200
|
+
include_examples 'should define reader', :primary_key_type, String
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
describe '#record_class' do
|
205
|
+
include_examples 'should define reader',
|
206
|
+
:record_class,
|
207
|
+
-> { record_class }
|
208
|
+
end
|
209
|
+
|
210
|
+
describe '#validate_entity' do
|
211
|
+
let(:expected_error) do
|
212
|
+
type = record_class
|
213
|
+
contract = Stannum::Contracts::ParametersContract.new do
|
214
|
+
keyword :entity, type
|
215
|
+
end
|
216
|
+
errors = contract.errors_for(
|
217
|
+
{
|
218
|
+
arguments: [],
|
219
|
+
block: nil,
|
220
|
+
keywords: { entity: nil }
|
221
|
+
}
|
222
|
+
)
|
223
|
+
|
224
|
+
Cuprum::Collections::Errors::InvalidParameters.new(
|
225
|
+
command: command,
|
226
|
+
errors: errors
|
227
|
+
)
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'should define the private method' do
|
231
|
+
expect(command)
|
232
|
+
.to respond_to(:validate_entity, true)
|
233
|
+
.with(1).argument
|
234
|
+
end
|
235
|
+
|
236
|
+
describe 'with nil' do
|
237
|
+
it 'should return a failing result' do
|
238
|
+
expect(command.send(:validate_entity, nil))
|
239
|
+
.to be_a_failing_result
|
240
|
+
.with_error(expected_error)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
describe 'with an Object' do
|
245
|
+
it 'should return a failing result' do
|
246
|
+
expect(command.send(:validate_entity, Object.new.freeze))
|
247
|
+
.to be_a_failing_result
|
248
|
+
.with_error(expected_error)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
describe 'with an invalid record instance' do
|
253
|
+
it 'should return a failing result' do
|
254
|
+
expect(command.send(:validate_entity, Tome.new))
|
255
|
+
.to be_a_failing_result
|
256
|
+
.with_error(expected_error)
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
describe 'with a valid record instance' do
|
261
|
+
it 'should not return a result' do
|
262
|
+
expect(command.send(:validate_entity, Book.new))
|
263
|
+
.not_to be_a_result
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
describe '#validate_primary_key' do
|
269
|
+
let(:primary_key_type) { Integer }
|
270
|
+
let(:expected_error) do
|
271
|
+
type = primary_key_type
|
272
|
+
contract = Stannum::Contracts::ParametersContract.new do
|
273
|
+
keyword :primary_key, type
|
274
|
+
end
|
275
|
+
errors = contract.errors_for(
|
276
|
+
{
|
277
|
+
arguments: [],
|
278
|
+
block: nil,
|
279
|
+
keywords: { primary_key: nil }
|
280
|
+
}
|
281
|
+
)
|
282
|
+
|
283
|
+
Cuprum::Collections::Errors::InvalidParameters.new(
|
284
|
+
command: command,
|
285
|
+
errors: errors
|
286
|
+
)
|
287
|
+
end
|
288
|
+
|
289
|
+
it 'should define the private method' do
|
290
|
+
expect(command)
|
291
|
+
.to respond_to(:validate_primary_key, true)
|
292
|
+
.with(1).argument
|
293
|
+
end
|
294
|
+
|
295
|
+
describe 'with nil' do
|
296
|
+
it 'should return a failing result' do
|
297
|
+
expect(command.send(:validate_primary_key, nil))
|
298
|
+
.to be_a_failing_result
|
299
|
+
.with_error(expected_error)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
describe 'with an Object' do
|
304
|
+
it 'should return a failing result' do
|
305
|
+
expect(command.send(:validate_primary_key, Object.new.freeze))
|
306
|
+
.to be_a_failing_result
|
307
|
+
.with_error(expected_error)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
describe 'with a String' do
|
312
|
+
it 'should return a failing result' do
|
313
|
+
expect(command.send(:validate_primary_key, '12345'))
|
314
|
+
.to be_a_failing_result
|
315
|
+
.with_error(expected_error)
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
describe 'with an Integer' do
|
320
|
+
it 'should not return a result' do
|
321
|
+
expect(command.send(:validate_primary_key, 12_345))
|
322
|
+
.not_to be_a_result
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
context 'with a record class with custom primary key' do
|
327
|
+
let(:record_class) { Tome }
|
328
|
+
let(:primary_key_type) { String }
|
329
|
+
|
330
|
+
describe 'with an Integer' do
|
331
|
+
it 'should return a failing result' do
|
332
|
+
expect(command.send(:validate_primary_key, 12_345))
|
333
|
+
.to be_a_failing_result
|
334
|
+
.with_error(expected_error)
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
describe 'with a String' do
|
339
|
+
it 'should not return a result' do
|
340
|
+
expect(command.send(:validate_primary_key, '12345'))
|
341
|
+
.not_to be_a_result
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
describe '#validate_primary_keys' do
|
348
|
+
let(:primary_keys) { nil }
|
349
|
+
let(:primary_key_type) { Integer }
|
350
|
+
let(:expected_error) do
|
351
|
+
type = primary_key_type
|
352
|
+
contract = Stannum::Contracts::ParametersContract.new do
|
353
|
+
keyword :primary_keys,
|
354
|
+
Stannum::Constraints::Types::ArrayType.new(item_type: type)
|
355
|
+
end
|
356
|
+
errors = contract.errors_for(
|
357
|
+
{
|
358
|
+
arguments: [],
|
359
|
+
block: nil,
|
360
|
+
keywords: { primary_keys: primary_keys }
|
361
|
+
}
|
362
|
+
)
|
363
|
+
|
364
|
+
Cuprum::Collections::Errors::InvalidParameters.new(
|
365
|
+
command: command,
|
366
|
+
errors: errors
|
367
|
+
)
|
368
|
+
end
|
369
|
+
|
370
|
+
it 'should define the private method' do
|
371
|
+
expect(command)
|
372
|
+
.to respond_to(:validate_primary_keys, true)
|
373
|
+
.with(1).argument
|
374
|
+
end
|
375
|
+
|
376
|
+
describe 'with nil' do
|
377
|
+
it 'should return a failing result' do
|
378
|
+
expect(command.send(:validate_primary_keys, nil))
|
379
|
+
.to be_a_failing_result
|
380
|
+
.with_error(expected_error)
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
describe 'with an Object' do
|
385
|
+
it 'should return a failing result' do
|
386
|
+
expect(command.send(:validate_primary_keys, Object.new.freeze))
|
387
|
+
.to be_a_failing_result
|
388
|
+
.with_error(expected_error)
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
describe 'with a String' do
|
393
|
+
it 'should return a failing result' do
|
394
|
+
expect(command.send(:validate_primary_keys, '12345'))
|
395
|
+
.to be_a_failing_result
|
396
|
+
.with_error(expected_error)
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
describe 'with an Integer' do
|
401
|
+
it 'should return a failing result' do
|
402
|
+
expect(command.send(:validate_primary_keys, 12_345))
|
403
|
+
.to be_a_failing_result
|
404
|
+
.with_error(expected_error)
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
describe 'with an empty Array' do
|
409
|
+
it 'should not return a result' do
|
410
|
+
expect(command.send(:validate_primary_keys, []))
|
411
|
+
.not_to be_a_result
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
describe 'with an Array with nil values' do
|
416
|
+
let(:primary_keys) { Array.new(3, nil) }
|
417
|
+
|
418
|
+
it 'should return a failing result' do
|
419
|
+
expect(command.send(:validate_primary_keys, primary_keys))
|
420
|
+
.to be_a_failing_result
|
421
|
+
.with_error(expected_error)
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
describe 'with an Array with Object values' do
|
426
|
+
let(:primary_keys) { Array.new(3) { Object.new.freeze } }
|
427
|
+
|
428
|
+
it 'should return a failing result' do
|
429
|
+
expect(command.send(:validate_primary_keys, primary_keys))
|
430
|
+
.to be_a_failing_result
|
431
|
+
.with_error(expected_error)
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
describe 'with an Array with String values' do
|
436
|
+
let(:primary_keys) { %w[ichi ni san] }
|
437
|
+
|
438
|
+
it 'should return a failing result' do
|
439
|
+
expect(command.send(:validate_primary_keys, primary_keys))
|
440
|
+
.to be_a_failing_result
|
441
|
+
.with_error(expected_error)
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
describe 'with an Array with Integer values' do
|
446
|
+
it 'should not return a result' do
|
447
|
+
expect(command.send(:validate_primary_keys, [0, 1, 2]))
|
448
|
+
.not_to be_a_result
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
context 'with a record class with custom primary key' do
|
453
|
+
let(:record_class) { Tome }
|
454
|
+
let(:primary_key_type) { String }
|
455
|
+
|
456
|
+
describe 'with an Array with String values' do
|
457
|
+
let(:primary_keys) { %w[ichi ni san] }
|
458
|
+
|
459
|
+
it 'should not return a result' do
|
460
|
+
expect(command.send(:validate_primary_keys, primary_keys))
|
461
|
+
.not_to be_a_result
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
describe 'with an Array with Integer values' do
|
466
|
+
let(:primary_keys) { [0, 1, 2] }
|
467
|
+
|
468
|
+
it 'should return a failing result' do
|
469
|
+
expect(command.send(:validate_primary_keys, primary_keys))
|
470
|
+
.to be_a_failing_result
|
471
|
+
.with_error(expected_error)
|
472
|
+
end
|
473
|
+
end
|
474
|
+
end
|
475
|
+
end
|
476
|
+
end
|
477
|
+
end
|
478
|
+
end
|
479
|
+
end
|
@@ -0,0 +1,232 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rspec/sleeping_king_studios/contract'
|
4
|
+
|
5
|
+
require 'cuprum/rails/rspec/contracts'
|
6
|
+
|
7
|
+
module Cuprum::Rails::RSpec::Contracts
|
8
|
+
# Namespace for RSpec responder contracts.
|
9
|
+
module ResponderContracts
|
10
|
+
# Contract validating the interface for a responder.
|
11
|
+
module ShouldImplementTheResponderMethodsContract
|
12
|
+
extend RSpec::SleepingKingStudios::Contract
|
13
|
+
|
14
|
+
# @!method apply(example_group, constructor_keywords: [])
|
15
|
+
# Adds the contract to the example group.
|
16
|
+
#
|
17
|
+
# @param example_group [RSpec::Core::ExampleGroup] the example group to
|
18
|
+
# which the contract is applied.
|
19
|
+
# @param constructor_keywords [Array<Symbol>] additional keywords that
|
20
|
+
# are required by the constructor.
|
21
|
+
# @param controller_name [String] the name of the test controller.
|
22
|
+
contract do |
|
23
|
+
constructor_keywords: [],
|
24
|
+
controller_name: 'Spec::CustomController'
|
25
|
+
|
|
26
|
+
def self.let?(method_name, &block)
|
27
|
+
let(method_name, &block) unless method_defined?(method_name)
|
28
|
+
end
|
29
|
+
|
30
|
+
let?(:resource_options) { { name: 'books' } }
|
31
|
+
let?(:resource) do
|
32
|
+
Cuprum::Rails::Resource.new(**resource_options)
|
33
|
+
end
|
34
|
+
|
35
|
+
example_class(controller_name) do |klass|
|
36
|
+
configured_resource = resource
|
37
|
+
|
38
|
+
klass.define_singleton_method(:resource) do
|
39
|
+
@resource ||= configured_resource
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '.new' do
|
44
|
+
let(:expected_keywords) do
|
45
|
+
%i[
|
46
|
+
action_name
|
47
|
+
controller
|
48
|
+
member_action
|
49
|
+
request
|
50
|
+
resource
|
51
|
+
]
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should define the constructor' do
|
55
|
+
expect(described_class)
|
56
|
+
.to respond_to(:new)
|
57
|
+
.with(0).arguments
|
58
|
+
.and_keywords(*expected_keywords, *constructor_keywords)
|
59
|
+
.and_any_keywords
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '#action_name' do
|
64
|
+
include_examples 'should define reader',
|
65
|
+
:action_name,
|
66
|
+
-> { action_name }
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#call' do
|
70
|
+
let(:result) { Cuprum::Result.new(value: :ok) }
|
71
|
+
|
72
|
+
def ignore_exceptions
|
73
|
+
yield
|
74
|
+
rescue StandardError
|
75
|
+
# Do nothing
|
76
|
+
end
|
77
|
+
|
78
|
+
it { expect(responder).to respond_to(:call).with(1).argument }
|
79
|
+
|
80
|
+
it 'should set the result' do
|
81
|
+
expect { ignore_exceptions { responder.call(result) } }
|
82
|
+
.to change(responder, :result)
|
83
|
+
.to be == result
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe '#controller' do
|
88
|
+
include_examples 'should define reader',
|
89
|
+
:controller,
|
90
|
+
-> { controller }
|
91
|
+
end
|
92
|
+
|
93
|
+
describe '#controller_name' do
|
94
|
+
include_examples 'should define reader',
|
95
|
+
:controller_name,
|
96
|
+
controller_name
|
97
|
+
end
|
98
|
+
|
99
|
+
describe '#member_action?' do
|
100
|
+
include_examples 'should define predicate',
|
101
|
+
:member_action?,
|
102
|
+
-> { !!constructor_options[:member_action] } # rubocop:disable Style/DoubleNegation
|
103
|
+
|
104
|
+
context 'when initialized with member_action: false' do
|
105
|
+
let(:constructor_options) { super().merge(member_action: false) }
|
106
|
+
|
107
|
+
it { expect(responder.member_action?).to be false }
|
108
|
+
end
|
109
|
+
|
110
|
+
context 'when initialized with member_action: true' do
|
111
|
+
let(:constructor_options) { super().merge(member_action: true) }
|
112
|
+
|
113
|
+
it { expect(responder.member_action?).to be true }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe '#request' do
|
118
|
+
include_examples 'should define reader', :request, -> { request }
|
119
|
+
end
|
120
|
+
|
121
|
+
describe '#resource' do
|
122
|
+
include_examples 'should define reader',
|
123
|
+
:resource,
|
124
|
+
-> { controller.class.resource }
|
125
|
+
end
|
126
|
+
|
127
|
+
describe '#result' do
|
128
|
+
include_examples 'should define reader', :result, nil
|
129
|
+
end
|
130
|
+
|
131
|
+
describe '#routes' do
|
132
|
+
include_examples 'should define reader', :routes
|
133
|
+
|
134
|
+
let(:resource_options) { super().merge(name: 'books') }
|
135
|
+
|
136
|
+
it 'should return the resource routes' do
|
137
|
+
expect(responder.routes)
|
138
|
+
.to be_a Cuprum::Rails::Routing::PluralRoutes
|
139
|
+
end
|
140
|
+
|
141
|
+
it { expect(responder.routes.base_path).to be == '/books' }
|
142
|
+
|
143
|
+
it { expect(responder.routes.index_path).to be == '/books' }
|
144
|
+
|
145
|
+
it { expect(responder.routes.parent_path).to be == '/' }
|
146
|
+
|
147
|
+
it { expect(responder.routes.show_path(0)).to be == '/books/0' }
|
148
|
+
|
149
|
+
it { expect(responder.routes.wildcards).to be == {} }
|
150
|
+
|
151
|
+
context 'when the request has path params' do
|
152
|
+
let(:path_params) { { 'author_id' => 0 } }
|
153
|
+
let(:request) do
|
154
|
+
Cuprum::Rails::Request.new(path_params: path_params)
|
155
|
+
end
|
156
|
+
|
157
|
+
it { expect(responder.routes.wildcards).to be == path_params }
|
158
|
+
end
|
159
|
+
|
160
|
+
context 'when initialized with a singular resource' do
|
161
|
+
let(:resource_options) do
|
162
|
+
super().merge(name: 'book', singular: true)
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'should return the resource routes' do
|
166
|
+
expect(responder.routes)
|
167
|
+
.to be_a Cuprum::Rails::Routing::SingularRoutes
|
168
|
+
end
|
169
|
+
|
170
|
+
it { expect(responder.routes.base_path).to be == '/book' }
|
171
|
+
|
172
|
+
it { expect(responder.routes.show_path).to be == '/book' }
|
173
|
+
end
|
174
|
+
|
175
|
+
context 'when initialized with a resource with custom routes' do
|
176
|
+
let(:resource_options) do
|
177
|
+
super().merge(
|
178
|
+
routes: Cuprum::Rails::Routes.new(base_path: '/path/to/books')
|
179
|
+
)
|
180
|
+
end
|
181
|
+
|
182
|
+
it { expect(responder.routes.base_path).to be == '/path/to/books' }
|
183
|
+
end
|
184
|
+
|
185
|
+
context 'when initialized with a resource with ancestors' do
|
186
|
+
let(:base_path) { '/authors/:author_id/series/:series_id' }
|
187
|
+
let(:authors_resource) do
|
188
|
+
Cuprum::Rails::Resource.new(name: 'authors')
|
189
|
+
end
|
190
|
+
let(:series_resource) do
|
191
|
+
Cuprum::Rails::Resource.new(
|
192
|
+
name: 'series',
|
193
|
+
singular_name: 'series',
|
194
|
+
parent: authors_resource
|
195
|
+
)
|
196
|
+
end
|
197
|
+
let(:resource_options) { super().merge(parent: series_resource) }
|
198
|
+
|
199
|
+
it 'should set the scoped base path' do
|
200
|
+
expect(responder.routes.base_path).to be == "#{base_path}/books"
|
201
|
+
end
|
202
|
+
|
203
|
+
context 'when the request has path params' do
|
204
|
+
let(:path_params) { { 'author_id' => 0, 'series_id' => 1 } }
|
205
|
+
let(:parent_path) { '/authors/0/series/1' }
|
206
|
+
let(:request) do
|
207
|
+
Cuprum::Rails::Request.new(path_params: path_params)
|
208
|
+
end
|
209
|
+
|
210
|
+
it { expect(responder.routes.wildcards).to be == path_params }
|
211
|
+
|
212
|
+
it 'should set the scoped index path' do
|
213
|
+
expect(responder.routes.index_path)
|
214
|
+
.to be == "#{parent_path}/books"
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'should set the scoped parent path' do
|
218
|
+
expect(responder.routes.parent_path)
|
219
|
+
.to be == parent_path
|
220
|
+
end
|
221
|
+
|
222
|
+
it 'should set the scoped show path' do
|
223
|
+
expect(responder.routes.show_path(2))
|
224
|
+
.to be == "#{parent_path}/books/2"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|