cuprum-rails 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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