much-rails 0.1.3 → 0.2.4
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/lib/much-rails.rb +9 -0
- data/lib/much-rails/abstract_class.rb +38 -0
- data/lib/much-rails/action/base_router.rb +10 -10
- data/lib/much-rails/action/router.rb +10 -4
- data/lib/much-rails/assets.rb +33 -18
- data/lib/much-rails/destroy_service.rb +42 -5
- data/lib/much-rails/records/validate_destroy.rb +23 -13
- data/lib/much-rails/save_service.rb +44 -7
- data/lib/much-rails/service_validation_errors.rb +41 -0
- data/lib/much-rails/version.rb +1 -1
- data/much-rails.gemspec +1 -1
- data/test/unit/abstract_class_tests.rb +45 -0
- data/test/unit/action/base_router_tests.rb +10 -10
- data/test/unit/action/router_tests.rb +4 -4
- data/test/unit/assets_tests.rb +46 -14
- data/test/unit/destroy_service_tests.rb +165 -21
- data/test/unit/much-rails_tests.rb +42 -1
- data/test/unit/records/always_destroyable_tests.rb +1 -1
- data/test/unit/records/not_destroyable_tests.rb +1 -1
- data/test/unit/records/validate_destroy_tests.rb +84 -4
- data/test/unit/save_service_tests.rb +180 -20
- data/test/unit/service_validation_errors_tests.rb +107 -0
- metadata +10 -4
@@ -34,7 +34,7 @@ module MuchRails::Records::AlwaysDestroyable
|
|
34
34
|
|
35
35
|
assert_that(subject.destroy).is_true
|
36
36
|
|
37
|
-
# won't raise MuchRails::Records::
|
37
|
+
# won't raise MuchRails::Records::DestructionInvalid
|
38
38
|
subject.destroy!
|
39
39
|
|
40
40
|
assert_that(subject.destroyable?).is_true
|
@@ -32,7 +32,7 @@ module MuchRails::Records::NotDestroyable
|
|
32
32
|
assert_that(subject.destroy).is_false
|
33
33
|
|
34
34
|
assert_that(->{ subject.destroy! })
|
35
|
-
.raises(MuchRails::Records::
|
35
|
+
.raises(MuchRails::Records::DestructionInvalid)
|
36
36
|
|
37
37
|
assert_that(subject.destroyable?).is_false
|
38
38
|
end
|
@@ -62,17 +62,17 @@ module MuchRails::Records::ValidateDestroy
|
|
62
62
|
|
63
63
|
exception =
|
64
64
|
assert_that(->{ subject.destroy! })
|
65
|
-
.raises(MuchRails::Records::
|
65
|
+
.raises(MuchRails::Records::DestructionInvalid)
|
66
66
|
assert_that(exception.message)
|
67
67
|
.equals("TEST DESTRUCTION ERROR1\nTEST DESTRUCTION ERROR2")
|
68
|
-
assert_that(exception.
|
68
|
+
assert_that(exception.errors)
|
69
69
|
.equals(base: subject.destruction_error_messages.to_a)
|
70
70
|
assert_that(subject.super_destroy_called).is_true
|
71
71
|
|
72
72
|
exception =
|
73
73
|
assert_that(->{ subject.destroy!(as: :thing) })
|
74
|
-
.raises(MuchRails::Records::
|
75
|
-
assert_that(exception.
|
74
|
+
.raises(MuchRails::Records::DestructionInvalid)
|
75
|
+
assert_that(exception.errors)
|
76
76
|
.equals(thing: subject.destruction_error_messages.to_a)
|
77
77
|
|
78
78
|
subject.super_destroy_called = nil
|
@@ -138,9 +138,85 @@ module MuchRails::Records::ValidateDestroy
|
|
138
138
|
end
|
139
139
|
end
|
140
140
|
|
141
|
+
class DestructionInvalidTests < UnitTests
|
142
|
+
desc "MuchRails::Records::DestructionInvalid"
|
143
|
+
subject{ exception_class }
|
144
|
+
|
145
|
+
let(:exception_class){ MuchRails::Records::DestructionInvalid }
|
146
|
+
|
147
|
+
let(:record){ FakeRecordClass.new }
|
148
|
+
|
149
|
+
should "be configured as expected" do
|
150
|
+
assert_that(subject < StandardError).is_true
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
class DestructionInvalidInitTests < DestructionInvalidTests
|
155
|
+
desc "when init"
|
156
|
+
subject{ exception_class.new(record) }
|
157
|
+
|
158
|
+
should have_readers :record, :errors, :error_full_messages
|
159
|
+
|
160
|
+
should "know its attributes when destruction errors exist" do
|
161
|
+
record.destruction_errors_exist = true
|
162
|
+
record.validate_destroy
|
163
|
+
|
164
|
+
assert_that(subject.message)
|
165
|
+
.equals(record.destruction_error_messages.to_a.join("\n"))
|
166
|
+
assert_that(subject.record).is(record)
|
167
|
+
assert_that(subject.errors).equals(
|
168
|
+
base: record.destruction_error_messages.to_a,
|
169
|
+
)
|
170
|
+
assert_that(subject.error_full_messages)
|
171
|
+
.equals(record.destruction_error_messages.to_a)
|
172
|
+
end
|
173
|
+
|
174
|
+
should "know its attributes when destruction errors don't exist" do
|
175
|
+
assert_that(subject.message).equals("")
|
176
|
+
assert_that(subject.record).is(record)
|
177
|
+
assert_that(subject.errors).equals({})
|
178
|
+
assert_that(subject.error_full_messages).equals([])
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
class DestructionInvalidInitWithFieldNameTests < DestructionInvalidTests
|
183
|
+
desc "when init with a field name"
|
184
|
+
subject{ exception_class.new(record, field_name: field_name) }
|
185
|
+
|
186
|
+
let(:field_name){ Factory.string }
|
187
|
+
|
188
|
+
should "know its attributes when destruction errors exist" do
|
189
|
+
record.destruction_errors_exist = true
|
190
|
+
record.validate_destroy
|
191
|
+
|
192
|
+
assert_that(subject.message)
|
193
|
+
.equals(record.destruction_error_messages.to_a.join("\n"))
|
194
|
+
assert_that(subject.record).is(record)
|
195
|
+
assert_that(subject.errors).equals(
|
196
|
+
field_name.to_sym => record.destruction_error_messages.to_a,
|
197
|
+
)
|
198
|
+
assert_that(subject.error_full_messages)
|
199
|
+
.equals(
|
200
|
+
record
|
201
|
+
.destruction_error_messages
|
202
|
+
.map do |m|
|
203
|
+
ActiveModel::Error.new(record, field_name, m).full_message
|
204
|
+
end,
|
205
|
+
)
|
206
|
+
end
|
207
|
+
|
208
|
+
should "know its attributes when destruction errors don't exist" do
|
209
|
+
assert_that(subject.message).equals("")
|
210
|
+
assert_that(subject.record).is(record)
|
211
|
+
assert_that(subject.errors).equals({})
|
212
|
+
assert_that(subject.error_full_messages).equals([])
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
141
216
|
require "active_record"
|
142
217
|
|
143
218
|
class FakeRecordBaseClass
|
219
|
+
extend ActiveRecord::Translation
|
144
220
|
# Include ActiveRecord::Persistence to test the `destroy!` logic
|
145
221
|
# (the `_raise_record_not_destroyed` method) that we had to re-implement in
|
146
222
|
# the MuchRails::Records::ValidateDestroy.
|
@@ -150,6 +226,10 @@ module MuchRails::Records::ValidateDestroy
|
|
150
226
|
|
151
227
|
attr_accessor :destruction_errors_exist
|
152
228
|
|
229
|
+
def self.base_class?
|
230
|
+
true
|
231
|
+
end
|
232
|
+
|
153
233
|
def destroy
|
154
234
|
@super_destroy_called = true
|
155
235
|
end
|
@@ -44,49 +44,209 @@ module MuchRails::SaveService
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
class
|
48
|
-
desc "
|
47
|
+
class ReceiverInitTests < ReceiverTests
|
48
|
+
desc "when init"
|
49
|
+
subject{ receiver_class.new(exception: exception) }
|
50
|
+
|
51
|
+
let(:exception){ nil }
|
52
|
+
end
|
53
|
+
|
54
|
+
class ReceiverInitAroundCallCallbackTests < ReceiverInitTests
|
55
|
+
desc "around_call callback"
|
49
56
|
setup do
|
50
|
-
Assert.stub(
|
57
|
+
Assert.stub(
|
58
|
+
MuchRails::SaveService::ValidationErrors,
|
59
|
+
:exception_classes,
|
60
|
+
){ exception_classes }
|
61
|
+
Assert.stub_on_call(
|
62
|
+
MuchRails::SaveService::ValidationErrors,
|
63
|
+
:result_for,
|
64
|
+
) do |call|
|
65
|
+
@result_for_call = call
|
66
|
+
validation_error_result
|
67
|
+
end
|
51
68
|
end
|
52
69
|
|
53
|
-
let(:exception){
|
70
|
+
let(:exception){ exceptions.sample }
|
71
|
+
let(:exceptions) do
|
72
|
+
[
|
73
|
+
RuntimeError.new(Factory.string),
|
74
|
+
ArgumentError.new(Factory.string),
|
75
|
+
ActiveRecord::RecordInvalid.new(FakeRecord.new),
|
76
|
+
]
|
77
|
+
end
|
78
|
+
let(:exception_classes){ exceptions.map(&:class) }
|
79
|
+
let(:validation_error_result) do
|
80
|
+
MuchResult.failure(error: result_error_message)
|
81
|
+
end
|
82
|
+
let(:result_error_message){ Factory.string }
|
83
|
+
|
84
|
+
should "rescue raised exceptions and "\
|
85
|
+
"use the ValidationErrors to build a result" do
|
86
|
+
result = subject.call
|
87
|
+
|
88
|
+
assert_that(result.failure?).is_true
|
89
|
+
assert_that(result.error).equals(result_error_message)
|
90
|
+
end
|
54
91
|
end
|
55
92
|
|
56
|
-
class
|
57
|
-
desc "
|
93
|
+
class ValidationErrorsTests < UnitTests
|
94
|
+
desc "ValidationErrors"
|
95
|
+
subject{ unit_class::ValidationErrors }
|
58
96
|
|
59
|
-
|
97
|
+
should have_imeths :add, :exception_classes, :result_for
|
98
|
+
should have_imeths :service_validation_errors
|
99
|
+
|
100
|
+
should "know its ServiceValidationErrors" do
|
101
|
+
assert_that(subject.service_validation_errors)
|
102
|
+
.is_an_instance_of(MuchRails::ServiceValidationErrors)
|
103
|
+
assert_that(subject.service_validation_errors.exception_classes)
|
104
|
+
.includes(ActiveRecord::RecordInvalid)
|
105
|
+
end
|
106
|
+
end
|
60
107
|
|
61
|
-
|
62
|
-
|
108
|
+
class ValidationErrorsAddTests < ValidationErrorsTests
|
109
|
+
desc ".add"
|
63
110
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
111
|
+
setup do
|
112
|
+
Assert.stub_on_call(subject.service_validation_errors, :add) do |call|
|
113
|
+
@add_call = call
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
let(:exception_class){ StandardError }
|
118
|
+
let(:block){ proc{ MuchResult.failure } }
|
119
|
+
|
120
|
+
should "call #add on its ServiceValidationErrors" do
|
121
|
+
subject.add(exception_class, &block)
|
122
|
+
assert_that(@add_call.args).equals([exception_class])
|
123
|
+
assert_that(@add_call.block).is(block)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
class ValidationErrorsExceptionClassesTests < ValidationErrorsTests
|
128
|
+
desc ".exception_classes"
|
129
|
+
|
130
|
+
setup do
|
131
|
+
Assert.stub(
|
132
|
+
subject.service_validation_errors,
|
133
|
+
:exception_classes,
|
134
|
+
){ exception_classes }
|
135
|
+
end
|
136
|
+
|
137
|
+
let(:exception_classes) do
|
138
|
+
[
|
139
|
+
StandardError,
|
140
|
+
ArgumentError,
|
141
|
+
]
|
142
|
+
end
|
143
|
+
|
144
|
+
should "call #exception_classes on its ServiceValidationErrors" do
|
145
|
+
assert_that(subject.exception_classes).is(exception_classes)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
class ValidationErrorsResultForTests < ValidationErrorsTests
|
150
|
+
desc ".result_for"
|
151
|
+
|
152
|
+
setup do
|
153
|
+
Assert.stub_on_call(
|
154
|
+
subject.service_validation_errors,
|
155
|
+
:result_for,
|
156
|
+
) do |call|
|
157
|
+
@result_for_call = call
|
158
|
+
result_for_result
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
let(:exception){ StandardError.new(Factory.string) }
|
163
|
+
let(:result_for_result){ MuchResult.failure }
|
164
|
+
|
165
|
+
should "call #result_for on its ServiceValidationErrors" do
|
166
|
+
assert_that(subject.result_for(exception)).is(result_for_result)
|
167
|
+
assert_that(@result_for_call.args).equals([exception])
|
70
168
|
end
|
71
169
|
end
|
72
170
|
|
73
|
-
class
|
74
|
-
desc "
|
171
|
+
class ValidationErrorsResultForRecordInvalidTests < ValidationErrorsTests
|
172
|
+
desc "when .result_for is passed an ActiveRecord::RecordInvalid"
|
173
|
+
|
174
|
+
let(:exception){ ActiveRecord::RecordInvalid.new(record) }
|
175
|
+
let(:record){ FakeRecord.new }
|
75
176
|
|
76
|
-
let(:
|
177
|
+
let(:no_record_exception){ ActiveRecord::RecordInvalid.new }
|
178
|
+
|
179
|
+
should "return a failure result with the record and validation errors" do
|
180
|
+
result = subject.result_for(exception)
|
181
|
+
|
182
|
+
assert_that(result.failure?).is_true
|
183
|
+
assert_that(result.exception).equals(exception)
|
184
|
+
assert_that(result.validation_errors).equals(record.errors.to_h)
|
185
|
+
assert_that(result.validation_error_messages)
|
186
|
+
.equals(record.errors.full_messages.to_a)
|
187
|
+
end
|
77
188
|
|
78
189
|
should "return a failure result with the exception and empty "\
|
79
190
|
"record errors" do
|
80
|
-
result = subject.
|
191
|
+
result = subject.result_for(no_record_exception)
|
81
192
|
|
82
193
|
assert_that(result.failure?).is_true
|
83
|
-
assert_that(result.exception).equals(
|
194
|
+
assert_that(result.exception).equals(no_record_exception)
|
84
195
|
assert_that(result.validation_errors).equals({})
|
85
196
|
assert_that(result.validation_error_messages).equals([])
|
86
197
|
end
|
87
198
|
end
|
88
199
|
|
200
|
+
class FailureResultTests < UnitTests
|
201
|
+
desc "FailureResult"
|
202
|
+
subject{ unit_class::FailureResult }
|
203
|
+
|
204
|
+
setup do
|
205
|
+
Assert.stub_tap_on_call(MuchResult, :failure) do |_, call|
|
206
|
+
@much_result_failure_call = call
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
let(:exception){ StandardError.new(Factory.string) }
|
211
|
+
let(:validation_errors){ { Factory.symbol => Factory.string } }
|
212
|
+
let(:custom_value){ Factory.string }
|
213
|
+
|
214
|
+
should have_imeths :new
|
215
|
+
|
216
|
+
should "use MuchResult.failure to build a result" do
|
217
|
+
result =
|
218
|
+
subject.new(
|
219
|
+
exception: exception,
|
220
|
+
validation_errors: validation_errors,
|
221
|
+
custom: custom_value,
|
222
|
+
)
|
223
|
+
assert_that(result.failure?).is_true
|
224
|
+
assert_that(@much_result_failure_call.kargs)
|
225
|
+
.equals(
|
226
|
+
exception: exception,
|
227
|
+
validation_errors: validation_errors,
|
228
|
+
custom: custom_value,
|
229
|
+
)
|
230
|
+
end
|
231
|
+
|
232
|
+
should "raise an error without an exception or validation errors" do
|
233
|
+
assert_that{
|
234
|
+
subject.new(validation_errors: validation_errors)
|
235
|
+
}.raises(ArgumentError)
|
236
|
+
end
|
237
|
+
|
238
|
+
should "raise an error without an exception or validation errors" do
|
239
|
+
assert_that{
|
240
|
+
subject.new(exception: exception)
|
241
|
+
}.raises(ArgumentError)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
89
245
|
class FakeRecord
|
246
|
+
def self.i18n_scope
|
247
|
+
"fake_record"
|
248
|
+
end
|
249
|
+
|
90
250
|
def errors
|
91
251
|
Errors.new
|
92
252
|
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MuchRails::ServiceValidationErrors
|
4
|
+
class UnitTests < Assert::Context
|
5
|
+
desc "MuchRails::ServiceValidationErrors"
|
6
|
+
subject{ unit_class }
|
7
|
+
|
8
|
+
let(:unit_class){ MuchRails::ServiceValidationErrors }
|
9
|
+
end
|
10
|
+
|
11
|
+
class InitTests < UnitTests
|
12
|
+
desc "when init"
|
13
|
+
subject{ unit_class.new }
|
14
|
+
|
15
|
+
should have_readers :hash
|
16
|
+
should have_imeths :add, :exception_classes, :result_for
|
17
|
+
end
|
18
|
+
|
19
|
+
class InitAddTests < InitTests
|
20
|
+
desc "#add"
|
21
|
+
|
22
|
+
let(:exception_class){ StandardError }
|
23
|
+
let(:block){ proc{ MuchResult.failure } }
|
24
|
+
|
25
|
+
let(:invalid_exception_class) do
|
26
|
+
[
|
27
|
+
Class.new,
|
28
|
+
Factory.string,
|
29
|
+
nil,
|
30
|
+
].sample
|
31
|
+
end
|
32
|
+
|
33
|
+
should "add an exception class and block" do
|
34
|
+
subject.add(exception_class, &block)
|
35
|
+
assert_that(subject.hash[exception_class]).is(block)
|
36
|
+
end
|
37
|
+
|
38
|
+
should "raise an error when it's not passed an Exception" do
|
39
|
+
assert_that{
|
40
|
+
subject.add(invalid_exception_class, &block)
|
41
|
+
}.raises(ArgumentError)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class InitExceptionClassesTests < InitTests
|
46
|
+
desc "#exception_classes"
|
47
|
+
|
48
|
+
setup do
|
49
|
+
exception_classes.each do |exception_class|
|
50
|
+
subject.add(exception_class, &block)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
let(:exception_classes) do
|
55
|
+
[
|
56
|
+
StandardError,
|
57
|
+
ArgumentError,
|
58
|
+
RuntimeError,
|
59
|
+
]
|
60
|
+
end
|
61
|
+
let(:block){ proc{ MuchResult.failure } }
|
62
|
+
|
63
|
+
should "return all the added exception classes" do
|
64
|
+
assert_that(subject.exception_classes).equals(exception_classes)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class InitResultForTests < InitTests
|
69
|
+
desc "#result_for"
|
70
|
+
|
71
|
+
setup do
|
72
|
+
subject.add(exception_class, &block)
|
73
|
+
end
|
74
|
+
|
75
|
+
let(:exception){ exception_class.new(Factory.string) }
|
76
|
+
let(:exception_class){ StandardError }
|
77
|
+
let(:block) do
|
78
|
+
proc{ MuchResult.failure(error_message: failure_result_error_message) }
|
79
|
+
end
|
80
|
+
let(:failure_result_error_message){ Factory.string }
|
81
|
+
|
82
|
+
let(:inherited_exception){ RuntimeError.new(Factory.string) }
|
83
|
+
|
84
|
+
let(:invalid_exception){ Exception.new(Factory.string) }
|
85
|
+
|
86
|
+
should "return the result of calling the added block "\
|
87
|
+
"for the exception class" do
|
88
|
+
result = subject.result_for(exception)
|
89
|
+
assert_that(result.failure?).is_true
|
90
|
+
assert_that(result.error_message).equals(failure_result_error_message)
|
91
|
+
end
|
92
|
+
|
93
|
+
should "return the result of calling the added block "\
|
94
|
+
"for an exception class ancestor" do
|
95
|
+
result = subject.result_for(exception)
|
96
|
+
assert_that(result.failure?).is_true
|
97
|
+
assert_that(result.error_message).equals(failure_result_error_message)
|
98
|
+
end
|
99
|
+
|
100
|
+
should "raise an error if a block hasn't been added "\
|
101
|
+
"for the exception class" do
|
102
|
+
assert_that{
|
103
|
+
subject.result_for(invalid_exception)
|
104
|
+
}.raises(ArgumentError)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|