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.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +145 -0
  3. data/DEVELOPMENT.md +20 -0
  4. data/README.md +356 -63
  5. data/lib/cuprum/rails/action.rb +32 -16
  6. data/lib/cuprum/rails/actions/create.rb +62 -15
  7. data/lib/cuprum/rails/actions/destroy.rb +23 -7
  8. data/lib/cuprum/rails/actions/edit.rb +23 -7
  9. data/lib/cuprum/rails/actions/index.rb +30 -10
  10. data/lib/cuprum/rails/actions/middleware/associations/cache.rb +112 -0
  11. data/lib/cuprum/rails/actions/middleware/associations/find.rb +23 -0
  12. data/lib/cuprum/rails/actions/middleware/associations/parent.rb +70 -0
  13. data/lib/cuprum/rails/actions/middleware/associations/query.rb +140 -0
  14. data/lib/cuprum/rails/actions/middleware/associations.rb +12 -0
  15. data/lib/cuprum/rails/actions/middleware/log_request.rb +126 -0
  16. data/lib/cuprum/rails/actions/middleware/log_result.rb +51 -0
  17. data/lib/cuprum/rails/actions/middleware/resources/find.rb +44 -0
  18. data/lib/cuprum/rails/actions/middleware/resources/query.rb +91 -0
  19. data/lib/cuprum/rails/actions/middleware/resources.rb +11 -0
  20. data/lib/cuprum/rails/actions/middleware.rb +13 -0
  21. data/lib/cuprum/rails/actions/new.rb +16 -4
  22. data/lib/cuprum/rails/actions/parameter_validation.rb +60 -0
  23. data/lib/cuprum/rails/actions/resource_action.rb +119 -42
  24. data/lib/cuprum/rails/actions/show.rb +23 -7
  25. data/lib/cuprum/rails/actions/update.rb +70 -22
  26. data/lib/cuprum/rails/actions.rb +11 -7
  27. data/lib/cuprum/rails/collection.rb +27 -47
  28. data/lib/cuprum/rails/command.rb +3 -1
  29. data/lib/cuprum/rails/commands/destroy_one.rb +10 -6
  30. data/lib/cuprum/rails/commands/find_many.rb +8 -1
  31. data/lib/cuprum/rails/commands/find_matching.rb +1 -1
  32. data/lib/cuprum/rails/commands/find_one.rb +8 -0
  33. data/lib/cuprum/rails/commands/insert_one.rb +17 -6
  34. data/lib/cuprum/rails/commands/update_one.rb +16 -5
  35. data/lib/cuprum/rails/constraints/parameters_contract.rb +14 -0
  36. data/lib/cuprum/rails/constraints.rb +10 -0
  37. data/lib/cuprum/rails/controller.rb +12 -2
  38. data/lib/cuprum/rails/controllers/action.rb +100 -0
  39. data/lib/cuprum/rails/controllers/class_methods/actions.rb +33 -7
  40. data/lib/cuprum/rails/controllers/class_methods/configuration.rb +36 -0
  41. data/lib/cuprum/rails/controllers/class_methods/middleware.rb +88 -0
  42. data/lib/cuprum/rails/controllers/class_methods/validations.rb +2 -2
  43. data/lib/cuprum/rails/controllers/configuration.rb +41 -1
  44. data/lib/cuprum/rails/controllers/middleware.rb +59 -0
  45. data/lib/cuprum/rails/controllers.rb +2 -0
  46. data/lib/cuprum/rails/errors/invalid_parameters.rb +55 -0
  47. data/lib/cuprum/rails/errors/invalid_statement.rb +11 -0
  48. data/lib/cuprum/rails/errors/missing_parameter.rb +42 -0
  49. data/lib/cuprum/rails/errors/resource_error.rb +46 -0
  50. data/lib/cuprum/rails/errors.rb +6 -1
  51. data/lib/cuprum/rails/map_errors.rb +29 -1
  52. data/lib/cuprum/rails/query.rb +1 -1
  53. data/lib/cuprum/rails/repository.rb +12 -25
  54. data/lib/cuprum/rails/request.rb +149 -60
  55. data/lib/cuprum/rails/resource.rb +119 -85
  56. data/lib/cuprum/rails/responders/base_responder.rb +78 -0
  57. data/lib/cuprum/rails/responders/html/plural_resource.rb +9 -39
  58. data/lib/cuprum/rails/responders/html/rendering.rb +81 -0
  59. data/lib/cuprum/rails/responders/html/resource.rb +107 -0
  60. data/lib/cuprum/rails/responders/html/singular_resource.rb +9 -38
  61. data/lib/cuprum/rails/responders/html.rb +2 -0
  62. data/lib/cuprum/rails/responders/html_responder.rb +8 -52
  63. data/lib/cuprum/rails/responders/json/resource.rb +3 -3
  64. data/lib/cuprum/rails/responders/json_responder.rb +31 -16
  65. data/lib/cuprum/rails/responders/matching.rb +29 -27
  66. data/lib/cuprum/rails/responders/serialization.rb +11 -9
  67. data/lib/cuprum/rails/responders.rb +1 -0
  68. data/lib/cuprum/rails/responses/head_response.rb +24 -0
  69. data/lib/cuprum/rails/responses/html/redirect_back_response.rb +55 -0
  70. data/lib/cuprum/rails/responses/html/redirect_response.rb +19 -4
  71. data/lib/cuprum/rails/responses/html/render_response.rb +17 -5
  72. data/lib/cuprum/rails/responses/html.rb +6 -2
  73. data/lib/cuprum/rails/responses.rb +1 -0
  74. data/lib/cuprum/rails/result.rb +36 -0
  75. data/lib/cuprum/rails/routes.rb +36 -23
  76. data/lib/cuprum/rails/rspec/contract_helpers.rb +57 -0
  77. data/lib/cuprum/rails/rspec/contracts/action_contracts.rb +754 -0
  78. data/lib/cuprum/rails/rspec/contracts/actions/create_contracts.rb +289 -0
  79. data/lib/cuprum/rails/rspec/contracts/actions/destroy_contracts.rb +164 -0
  80. data/lib/cuprum/rails/rspec/contracts/actions/edit_contracts.rb +73 -0
  81. data/lib/cuprum/rails/rspec/contracts/actions/index_contracts.rb +108 -0
  82. data/lib/cuprum/rails/rspec/contracts/actions/new_contracts.rb +111 -0
  83. data/lib/cuprum/rails/rspec/contracts/actions/show_contracts.rb +72 -0
  84. data/lib/cuprum/rails/rspec/contracts/actions/update_contracts.rb +263 -0
  85. data/lib/cuprum/rails/rspec/contracts/actions.rb +8 -0
  86. data/lib/cuprum/rails/rspec/contracts/command_contracts.rb +479 -0
  87. data/lib/cuprum/rails/rspec/contracts/responder_contracts.rb +232 -0
  88. data/lib/cuprum/rails/rspec/contracts/routes_contracts.rb +363 -0
  89. data/lib/cuprum/rails/rspec/contracts/serializers_contracts.rb +70 -0
  90. data/lib/cuprum/rails/rspec/contracts.rb +8 -0
  91. data/lib/cuprum/rails/rspec/matchers/be_a_result_matcher.rb +64 -0
  92. data/lib/cuprum/rails/rspec/matchers.rb +41 -0
  93. data/lib/cuprum/rails/serializers/base_serializer.rb +60 -0
  94. data/lib/cuprum/rails/serializers/context.rb +84 -0
  95. data/lib/cuprum/rails/serializers/json/active_record_serializer.rb +2 -2
  96. data/lib/cuprum/rails/serializers/json/array_serializer.rb +9 -8
  97. data/lib/cuprum/rails/serializers/json/attributes_serializer.rb +95 -172
  98. data/lib/cuprum/rails/serializers/json/error_serializer.rb +2 -2
  99. data/lib/cuprum/rails/serializers/json/hash_serializer.rb +9 -8
  100. data/lib/cuprum/rails/serializers/json/identity_serializer.rb +3 -3
  101. data/lib/cuprum/rails/serializers/json/properties_serializer.rb +252 -0
  102. data/lib/cuprum/rails/serializers/json.rb +2 -1
  103. data/lib/cuprum/rails/serializers.rb +3 -1
  104. data/lib/cuprum/rails/version.rb +1 -1
  105. data/lib/cuprum/rails.rb +19 -16
  106. metadata +73 -131
  107. data/lib/cuprum/rails/controller_action.rb +0 -121
  108. data/lib/cuprum/rails/errors/missing_parameters.rb +0 -33
  109. data/lib/cuprum/rails/errors/missing_primary_key.rb +0 -46
  110. data/lib/cuprum/rails/errors/undefined_permitted_attributes.rb +0 -34
  111. data/lib/cuprum/rails/rspec/command_contract.rb +0 -460
  112. data/lib/cuprum/rails/rspec/define_route_contract.rb +0 -84
  113. data/lib/cuprum/rails/serializers/json/serializer.rb +0 -66
@@ -0,0 +1,289 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec/sleeping_king_studios/contract'
4
+
5
+ require 'cuprum/rails/rspec/contract_helpers'
6
+ require 'cuprum/rails/rspec/contracts/action_contracts'
7
+ require 'cuprum/rails/rspec/contracts/actions'
8
+
9
+ module Cuprum::Rails::RSpec::Contracts::Actions
10
+ # Namespace for RSpec create contracts, which validate create implementations.
11
+ module CreateContracts
12
+ # Contract asserting the action implements the create action interface.
13
+ module ShouldBeACreateActionContract
14
+ extend RSpec::SleepingKingStudios::Contract
15
+
16
+ # @method apply(example_group, invalid_attributes:, valid_attributes:, **options)
17
+ # Adds the contract to the example group.
18
+ #
19
+ # @param example_group [RSpec::Core::ExampleGroup] The example group to
20
+ # which the contract is applied.
21
+ # @param invalid_attributes [Hash<String>] A set of attributes that will
22
+ # fail validation.
23
+ # @param valid_attributes [Hash<String>] A set of attributes that will
24
+ # pass validation.
25
+ #
26
+ # @option options [Hash<String>] duplicate_attributes A set of
27
+ # attributes for a duplicate entity.
28
+ # @option options [#to_proc] examples_on_failure Extra examples to run
29
+ # for the failing cases.
30
+ # @option options [#to_proc] examples_on_success Extra examples to run
31
+ # for the passing case.
32
+ # @option options [Hash<String>] expected_attributes The expected
33
+ # attributes for both a failed validation and a returned entity.
34
+ # @option options [Hash<String>] expected_attributes_on_failure The
35
+ # expected attributes for a failed validation. Defaults to the value
36
+ # of invalid_attributes.
37
+ # @option options [Hash<String>] expected_attributes_on_success The
38
+ # expected attributes for the returned object. Defaults to the value
39
+ # of valid_attributes.
40
+ # @option options [Hash<String>] expected_value_on_success The expected
41
+ # value for the passing result. Defaults to a Hash with the created
42
+ # entity.
43
+ # @option options [Hash<String>] params The parameters used to build the
44
+ # request. Defaults to the given attributes.
45
+ #
46
+ # @yield Additional examples to run for the passing case.
47
+
48
+ contract do |invalid_attributes:, valid_attributes:, **options, &block|
49
+ include Cuprum::Rails::RSpec::Contracts::ActionContracts
50
+ include Cuprum::Rails::RSpec::Contracts::Actions::CreateContracts
51
+
52
+ options = options.merge(valid_attributes: valid_attributes)
53
+ configured_params = lambda do
54
+ attributes =
55
+ Cuprum::Rails::RSpec::ContractHelpers.option_with_default(
56
+ valid_attributes,
57
+ context: self
58
+ )
59
+
60
+ Cuprum::Rails::RSpec::ContractHelpers.option_with_default(
61
+ options[:params],
62
+ context: self,
63
+ default: {
64
+ configured_resource.singular_name => attributes
65
+ }
66
+ )
67
+ end
68
+
69
+ # :nocov:
70
+ if options[:examples_on_success] && block
71
+ raise ArgumentError, 'provide either :examples_on_success or a block'
72
+ elsif block
73
+ options[:examples_on_success] = block
74
+ end
75
+ # :nocov:
76
+
77
+ should_not_create_an_entity = lambda do
78
+ it 'should not create an entity' do
79
+ expect { call_action }
80
+ .not_to change(configured_resource.entity_class, :count)
81
+ end
82
+
83
+ # :nocov:
84
+ if options[:examples_on_failure]
85
+ instance_exec(&options[:examples_on_failure])
86
+ end
87
+ # :nocov:
88
+ end
89
+
90
+ include_contract 'should be a resource action',
91
+ require_permitted_attributes: true
92
+
93
+ include_contract(
94
+ 'should require parameters',
95
+ params: configured_params,
96
+ &should_not_create_an_entity
97
+ )
98
+
99
+ include_contract(
100
+ 'should validate attributes',
101
+ expected_attributes: options.fetch(
102
+ :expected_attributes,
103
+ options[:expected_attributes_on_failure]
104
+ ),
105
+ invalid_attributes: invalid_attributes,
106
+ params: configured_params,
107
+ &should_not_create_an_entity
108
+ )
109
+
110
+ if options[:duplicate_attributes]
111
+ include_contract(
112
+ 'should not create a duplicate entity',
113
+ params: configured_params,
114
+ valid_attributes: options[:duplicate_attributes],
115
+ &should_not_create_an_entity
116
+ )
117
+ end
118
+
119
+ include_contract 'should create the entity',
120
+ expected_attributes: options.fetch(
121
+ :expected_attributes,
122
+ options[:expected_attributes_on_success]
123
+ ),
124
+ expected_value: options[:expected_value_on_success],
125
+ params: configured_params,
126
+ valid_attributes: valid_attributes,
127
+ &options[:examples_on_success]
128
+ end
129
+ end
130
+
131
+ # Contract asserting the action creates a new entity.
132
+ module ShouldCreateTheEntityContract
133
+ extend RSpec::SleepingKingStudios::Contract
134
+
135
+ # @!method apply(example_group, valid_attributes:, **options)
136
+ # Adds the contract to the example group.
137
+ #
138
+ # @param example_group [RSpec::Core::ExampleGroup] The example group to
139
+ # which the contract is applied.
140
+ #
141
+ # @option options [Hash<String>] expected_attributes The expected
142
+ # attributes for the returned object. Defaults to the value of
143
+ # valid_attributes.
144
+ # @option options [Hash<String>] expected_value The expected value for
145
+ # the passing result. Defaults to a Hash with the created entity.
146
+ # @option options [Hash<String>] params The parameters used to build the
147
+ # request. Defaults to the given attributes.
148
+ # @option options [Hash<String>] valid_attributes A set of attributes
149
+ # that will pass validation.
150
+ #
151
+ # @yield Additional examples.
152
+
153
+ contract do |valid_attributes:, **options, &block|
154
+ describe '#call' do
155
+ include Cuprum::Rails::RSpec::ContractHelpers
156
+
157
+ context 'with valid parameters' do
158
+ let(:request) do
159
+ Cuprum::Rails::Request.new(params: configured_params)
160
+ end
161
+ let(:configured_valid_attributes) do
162
+ option_with_default(valid_attributes)
163
+ end
164
+ let(:configured_params) do
165
+ resource_name = resource.singular_name
166
+
167
+ option_with_default(
168
+ options[:params],
169
+ default: {}
170
+ )
171
+ .merge({ resource_name => configured_valid_attributes })
172
+ end
173
+ let(:configured_expected_attributes) do
174
+ option_with_default(
175
+ options[:expected_attributes],
176
+ default: configured_valid_attributes
177
+ )
178
+ end
179
+ let(:configured_expected_entity) do
180
+ action
181
+ .resource
182
+ .entity_class
183
+ .where(configured_expected_attributes)
184
+ .first
185
+ end
186
+ let(:configured_expected_value) do
187
+ resource_name = resource.singular_name
188
+
189
+ option_with_default(
190
+ options[:expected_value],
191
+ default: {
192
+ resource_name => configured_expected_entity
193
+ }
194
+ )
195
+ end
196
+
197
+ it 'should return a passing result' do
198
+ expect(call_action)
199
+ .to be_a_passing_result
200
+ .with_value(configured_expected_value)
201
+ end
202
+
203
+ it 'should create the entity', :aggregate_failures do
204
+ expect { call_action }
205
+ .to change(configured_resource.entity_class, :count)
206
+ .by(1)
207
+
208
+ expect(
209
+ action
210
+ .resource
211
+ .entity_class
212
+ .where(configured_expected_attributes)
213
+ .exists?
214
+ ).to be true
215
+ end
216
+
217
+ instance_exec(&block) if block.is_a?(Proc)
218
+ end
219
+ end
220
+ end
221
+ end
222
+
223
+ # Contract asserting the action does not create a duplicate entity.
224
+ module ShouldNotCreateADuplicateEntityContract
225
+ extend RSpec::SleepingKingStudios::Contract
226
+
227
+ # @!method apply(example_group, valid_attributes:, primary_key: "id")
228
+ # Adds the contract to the example group.
229
+ #
230
+ # @param example_group [RSpec::Core::ExampleGroup] The example group to
231
+ # which the contract is applied.
232
+ # @param valid_attributes [Hash<String>] A set of attributes that will
233
+ # pass validation.
234
+ #
235
+ # @option options [Hash<String>] params The parameters used to build the
236
+ # request. Defaults to the given attributes.
237
+ #
238
+ # @yield Additional examples.
239
+
240
+ contract do |valid_attributes:, **options, &block|
241
+ describe '#call' do
242
+ include Cuprum::Rails::RSpec::ContractHelpers
243
+
244
+ context 'with duplicate parameters' do
245
+ let(:request) do
246
+ Cuprum::Rails::Request.new(params: configured_params)
247
+ end
248
+ let(:configured_valid_attributes) do
249
+ option_with_default(valid_attributes)
250
+ end
251
+ let(:configured_duplicate_entity) do
252
+ configured_resource.entity_class.new(valid_attributes)
253
+ end
254
+ let(:configured_params) do
255
+ resource_name = configured_resource.singular_name
256
+
257
+ option_with_default(
258
+ options[:params],
259
+ default: {}
260
+ )
261
+ .merge({ resource_name => configured_valid_attributes })
262
+ end
263
+ let(:configured_expected_error) do
264
+ primary_key_name = configured_resource.primary_key.to_s
265
+ primary_key_value = configured_duplicate_entity[primary_key_name]
266
+
267
+ Cuprum::Collections::Errors::AlreadyExists.new(
268
+ attribute_name: primary_key_name,
269
+ attribute_value: primary_key_value,
270
+ collection_name: configured_resource.name,
271
+ primary_key: true
272
+ )
273
+ end
274
+
275
+ before(:example) { configured_duplicate_entity.save! }
276
+
277
+ it 'should return a failing result' do
278
+ expect(call_action)
279
+ .to be_a_failing_result
280
+ .with_error(configured_expected_error)
281
+ end
282
+
283
+ instance_exec(&block) if block.is_a?(Proc)
284
+ end
285
+ end
286
+ end
287
+ end
288
+ end
289
+ end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec/sleeping_king_studios/contract'
4
+
5
+ require 'cuprum/rails/rspec/contract_helpers'
6
+ require 'cuprum/rails/rspec/contracts/action_contracts'
7
+ require 'cuprum/rails/rspec/contracts/actions'
8
+
9
+ module Cuprum::Rails::RSpec::Contracts::Actions
10
+ # Namespace for RSpec destroy contracts, which validate destroy
11
+ # implementations.
12
+ module DestroyContracts
13
+ # Contract asserting the action implements the destroy action interface.
14
+ module ShouldBeADestroyActionContract
15
+ extend RSpec::SleepingKingStudios::Contract
16
+
17
+ # @method apply(example_group, existing_entity:, **options)
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
+ # @param existing_entity [Object] The existing entity to destroy.
23
+ #
24
+ # @option options [#to_proc] examples_on_failure Extra examples to run
25
+ # for the failing cases.
26
+ # @option options [#to_proc] examples_on_success Extra examples to run
27
+ # for the passing case.
28
+ # @option options [Hash<String>] expected_value_on_success The expected
29
+ # value for the passing result. Defaults to a Hash with the destroyed
30
+ # entity.
31
+ # @option options [Hash<String>] params The parameters used to build the
32
+ # request. Defaults to the id of the entity.
33
+ # @option options [Object] primary_key_value The value of the primary
34
+ # key for the missing entity.
35
+ #
36
+ # @yield Additional examples to run for the passing case.
37
+
38
+ contract do |existing_entity:, **options, &block|
39
+ include Cuprum::Rails::RSpec::Contracts::ActionContracts
40
+ include Cuprum::Rails::RSpec::Contracts::Actions::DestroyContracts
41
+
42
+ # :nocov:
43
+ if options[:examples_on_success] && block
44
+ raise ArgumentError, 'provide either :examples_on_success or a block'
45
+ elsif block
46
+ options[:examples_on_success] = block
47
+ end
48
+ # :nocov:
49
+
50
+ should_not_destroy_the_entity = lambda do
51
+ it 'should not destroy the entity' do
52
+ expect { call_action }
53
+ .not_to change(configured_resource.entity_class, :count)
54
+ end
55
+
56
+ # :nocov:
57
+ if options[:examples_on_failure]
58
+ instance_exec(&options[:examples_on_failure])
59
+ end
60
+ # :nocov:
61
+ end
62
+
63
+ include_contract 'should be a resource action'
64
+
65
+ include_contract(
66
+ 'should require primary key',
67
+ params: options[:params],
68
+ &should_not_destroy_the_entity
69
+ )
70
+
71
+ include_contract(
72
+ 'should require existing entity',
73
+ params: options[:params],
74
+ primary_key_value: options[:primary_key_value],
75
+ &should_not_destroy_the_entity
76
+ )
77
+
78
+ include_contract(
79
+ 'should destroy the entity',
80
+ existing_entity: existing_entity,
81
+ expected_value: options[:expected_value_on_success],
82
+ params: options[:params],
83
+ &options[:examples_on_success]
84
+ )
85
+ end
86
+ end
87
+
88
+ # Contract asserting the action destroys the specified entity.
89
+ module ShouldDestroyTheEntityContract
90
+ extend RSpec::SleepingKingStudios::Contract
91
+
92
+ # @method apply(example_group, existing_entity:, **options, &block)
93
+ # Adds the contract to the example group.
94
+ #
95
+ # @param example_group [RSpec::Core::ExampleGroup] The example group to
96
+ # which the contract is applied.
97
+ # @param existing_entity [Object] The existing entity to destroy.
98
+ #
99
+ # @option options [Hash<String>] expected_value The expected value for
100
+ # the passing result. Defaults to a Hash with the destroyed entity.
101
+ # @option options [Hash<String>] params The parameters used to build the
102
+ # request. Defaults to the id of the entity.
103
+ #
104
+ # @yield Additional configuration or examples.
105
+
106
+ contract do |existing_entity:, **options, &block|
107
+ describe '#call' do
108
+ include Cuprum::Rails::RSpec::ContractHelpers
109
+
110
+ context 'when the entity exists' do
111
+ let(:request) do
112
+ Cuprum::Rails::Request.new(params: configured_params)
113
+ end
114
+ let(:configured_existing_entity) do
115
+ option_with_default(existing_entity)
116
+ end
117
+ let(:configured_params) do
118
+ resource_id =
119
+ configured_existing_entity[configured_resource.primary_key]
120
+
121
+ option_with_default(
122
+ options[:params],
123
+ default: { 'id' => resource_id }
124
+ )
125
+ end
126
+ let(:configured_expected_value) do
127
+ resource_name = configured_resource.singular_name
128
+
129
+ option_with_default(
130
+ options[:expected_value],
131
+ default: {
132
+ resource_name => configured_existing_entity
133
+ }
134
+ )
135
+ end
136
+
137
+ it 'should return a passing result' do
138
+ expect(call_action)
139
+ .to be_a_passing_result
140
+ .with_value(configured_expected_value)
141
+ end
142
+
143
+ it 'should destroy the entity', :aggregate_failures do
144
+ expect { call_action }
145
+ .to change(configured_resource.entity_class, :count)
146
+ .by(-1)
147
+
148
+ primary_key_name = configured_resource.primary_key
149
+ primary_key_value = configured_existing_entity[primary_key_name]
150
+ expect(
151
+ action
152
+ .resource
153
+ .entity_class
154
+ .exists?(primary_key_name => primary_key_value)
155
+ ).to be false
156
+ end
157
+
158
+ instance_exec(&block) if block
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec/sleeping_king_studios/contract'
4
+
5
+ require 'cuprum/rails/rspec/contract_helpers'
6
+ require 'cuprum/rails/rspec/contracts/action_contracts'
7
+ require 'cuprum/rails/rspec/contracts/actions'
8
+
9
+ module Cuprum::Rails::RSpec::Contracts::Actions
10
+ # Namespace for RSpec edit contracts, which validate edit implementations.
11
+ module EditContracts
12
+ # Contract asserting the action implements the edit action interface.
13
+ module ShouldBeAnEditActionContract
14
+ extend RSpec::SleepingKingStudios::Contract
15
+
16
+ # @method apply(example_group, existing_entity:, **options)
17
+ # Adds the contract to the example group.
18
+ #
19
+ # @param example_group [RSpec::Core::ExampleGroup] The example group to
20
+ # which the contract is applied.
21
+ # @param existing_entity [Object] The existing entity to find.
22
+ #
23
+ # @option options [#to_proc] examples_on_failure Extra examples to run
24
+ # for the failing cases.
25
+ # @option options [#to_proc] examples_on_success Extra examples to run
26
+ # for the passing case.
27
+ # @option options [Hash<String>] expected_value_on_success The expected
28
+ # value for the passing result. Defaults to a Hash with the found
29
+ # entity.
30
+ # @option options [Hash<String>] params The parameters used to build the
31
+ # request. Defaults to the id of the entity.
32
+ # @option options [Object] primary_key_value The value of the primary
33
+ # key for the missing entity.
34
+ #
35
+ # @yield Additional examples to run for the passing case.
36
+
37
+ contract do |existing_entity:, **options, &block|
38
+ include Cuprum::Rails::RSpec::Contracts::ActionContracts
39
+
40
+ # :nocov:
41
+ if options[:examples_on_success] && block
42
+ raise ArgumentError, 'provide either :examples_on_success or a block'
43
+ elsif block
44
+ options[:examples_on_success] = block
45
+ end
46
+ # :nocov:
47
+
48
+ include_contract 'should be a resource action'
49
+
50
+ include_contract(
51
+ 'should require primary key',
52
+ params: options[:params],
53
+ &options[:examples_on_failure]
54
+ )
55
+
56
+ include_contract(
57
+ 'should require existing entity',
58
+ params: options[:params],
59
+ primary_key_value: options[:primary_key_value],
60
+ &options[:examples_on_failure]
61
+ )
62
+
63
+ include_contract(
64
+ 'should find the entity',
65
+ existing_entity: existing_entity,
66
+ expected_value: options[:expected_value_on_success],
67
+ params: options[:params],
68
+ &options[:examples_on_success]
69
+ )
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/rails/rspec/contracts/action_contracts'
4
+ require 'cuprum/rails/rspec/contracts/actions'
5
+
6
+ module Cuprum::Rails::RSpec::Contracts::Actions
7
+ # Namespace for RSpec index contracts, which validate index implementations.
8
+ module IndexContracts
9
+ # Contract asserting the action implements the index action interface.
10
+ module ShouldBeAnIndexActionContract
11
+ extend RSpec::SleepingKingStudios::Contract
12
+
13
+ # @method apply(example_group, existing_entities:, **options)
14
+ # Adds the contract to the example group.
15
+ #
16
+ # @param example_group [RSpec::Core::ExampleGroup] The example group to
17
+ # which the contract is applied.
18
+ # @param existing_entities [Object] The existing entities to find.
19
+ #
20
+ # @option options [#to_proc] examples_on_success Extra examples to run
21
+ # for the passing case.
22
+ # @option options [Hash<String>] expected_value_on_success The expected
23
+ # value for the passing result. Defaults to a Hash with the found
24
+ # entity.
25
+ # @option options [Hash<String>] params The parameters used to build the
26
+ # request. Defaults to an empty hash.
27
+ #
28
+ # @yield Additional examples to run for the passing case.
29
+
30
+ contract do |existing_entities:, **options, &block|
31
+ include Cuprum::Rails::RSpec::Contracts::ActionContracts
32
+ include Cuprum::Rails::RSpec::Contracts::Actions::IndexContracts
33
+
34
+ # :nocov:
35
+ if options[:examples_on_success] && block
36
+ raise ArgumentError, 'provide either :examples_on_success or a block'
37
+ elsif block
38
+ options[:examples_on_success] = block
39
+ end
40
+ # :nocov:
41
+
42
+ include_contract 'should be a resource action'
43
+
44
+ include_contract 'should find the entities',
45
+ existing_entities: existing_entities,
46
+ expected_value: options[:expected_value_on_success],
47
+ params: options[:params],
48
+ &options[:examples_on_success]
49
+ end
50
+ end
51
+
52
+ # Contract asserting the action queries the repository for the entities.
53
+ module ShouldFindTheEntitiesContract
54
+ extend RSpec::SleepingKingStudios::Contract
55
+
56
+ # @method apply(example_group, existing_entities:, **options)
57
+ # Adds the contract to the example group.
58
+ #
59
+ # @param example_group [RSpec::Core::ExampleGroup] The example group to
60
+ # which the contract is applied.
61
+ # @param existing_entities [Object] The existing entities to find.
62
+ #
63
+ # @option options [Hash<String>] expected_value The expected
64
+ # value for the passing result. Defaults to a Hash with the found
65
+ # entity.
66
+ # @option options [Hash<String>] params The parameters used to build the
67
+ # request. Defaults to an empty hash.
68
+ #
69
+ # @yield Additional examples.
70
+
71
+ contract do |existing_entities:, **options, &block|
72
+ include Cuprum::Rails::RSpec::Contracts::ActionContracts
73
+
74
+ describe '#call' do
75
+ include Cuprum::Rails::RSpec::ContractHelpers
76
+
77
+ let(:request) do
78
+ Cuprum::Rails::Request.new(params: configured_params)
79
+ end
80
+ let(:configured_params) do
81
+ option_with_default(options[:params], default: {})
82
+ end
83
+ let(:configured_existing_entities) do
84
+ option_with_default(existing_entities)
85
+ end
86
+ let(:configured_expected_value) do
87
+ resource_name = configured_resource.name
88
+
89
+ option_with_default(
90
+ options[:expected_value],
91
+ default: {
92
+ resource_name => configured_existing_entities
93
+ }
94
+ )
95
+ end
96
+
97
+ it 'should return a passing result' do
98
+ expect(call_action)
99
+ .to be_a_passing_result
100
+ .with_value(configured_expected_value)
101
+ end
102
+
103
+ instance_exec(&block) if block
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end