much-rails 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,9 +6,9 @@ require "much-rails/layout"
6
6
  module MuchRails::Layout
7
7
  class UnitTests < Assert::Context
8
8
  desc "MuchRails::Layout"
9
- subject{ unit_class }
9
+ subject{ unit_module }
10
10
 
11
- let(:unit_class){ MuchRails::Layout }
11
+ let(:unit_module){ MuchRails::Layout }
12
12
 
13
13
  should "include MuchRails::Mixin" do
14
14
  assert_that(subject).includes(MuchRails::Mixin)
@@ -30,15 +30,16 @@ module MuchRails::Layout
30
30
  end
31
31
 
32
32
  should have_imeths :page_title, :page_titles, :application_page_title
33
- should have_imeths :breadcrumb, :breadcrumbs, :stylesheet, :stylesheets
34
- should have_imeths :javascript, :javascripts, :layout, :layouts
33
+ should have_imeths :breadcrumb, :breadcrumbs
34
+ should have_imeths :stylesheet, :stylesheets, :javascript, :javascripts
35
+ should have_imeths :head_link, :head_links, :layout, :layouts
35
36
  end
36
37
 
37
38
  class InitTests < ReceiverTests
38
39
  desc "when init"
39
40
  subject{ receiver_class.new }
40
41
 
41
- should "know its #page_title" do
42
+ should "know its page title" do
42
43
  assert_that(subject.page_title).is_nil
43
44
 
44
45
  receiver_class.page_title{ "Some Portal" }
@@ -47,7 +48,7 @@ module MuchRails::Layout
47
48
  assert_that(subject.page_title).equals("Some Resource attribute1")
48
49
  end
49
50
 
50
- should "know its #application_page_title" do
51
+ should "know its application page title" do
51
52
  assert_that(subject.application_page_title).is_nil
52
53
 
53
54
  receiver_class.application_page_title{ "Some App" }
@@ -56,12 +57,12 @@ module MuchRails::Layout
56
57
  assert_that(subject.application_page_title).equals("Some App attribute1")
57
58
  end
58
59
 
59
- should "know its #full_page_title "\
60
+ should "know its full page title "\
60
61
  "given no application page title or page title segments" do
61
62
  assert_that(subject.full_page_title).is_nil
62
63
  end
63
64
 
64
- should "know its #full_page_title "\
65
+ should "know its full page title "\
65
66
  "given an application page title but no page title segments" do
66
67
  receiver_class.application_page_title{ "Some App" }
67
68
 
@@ -69,7 +70,7 @@ module MuchRails::Layout
69
70
  .equals(subject.application_page_title)
70
71
  end
71
72
 
72
- should "know its #full_page_title "\
73
+ should "know its full page title "\
73
74
  "given no application page title but page title segments" do
74
75
  receiver_class.page_title{ "Some Portal" }
75
76
  receiver_class.page_title{ "Some Resource #{attribute1}" }
@@ -78,7 +79,7 @@ module MuchRails::Layout
78
79
  .equals("Some Resource attribute1 - Some Portal")
79
80
  end
80
81
 
81
- should "know its #full_page_title "\
82
+ should "know its full page title "\
82
83
  "given both an application page title and page title segments" do
83
84
  receiver_class.application_page_title{ "Some App" }
84
85
  receiver_class.page_title{ "Some Portal" }
@@ -88,13 +89,14 @@ module MuchRails::Layout
88
89
  .equals("Some Resource attribute1 - Some Portal | Some App")
89
90
  end
90
91
 
91
- should "know its #breadcrumbs" do
92
+ should "know its breadcrumbs" do
92
93
  receiver = receiver_class.new
94
+ assert_that(receiver.any_breadcrumbs?).is_false
93
95
  assert_that(receiver.breadcrumbs).is_empty
94
96
 
95
97
  receiver_class.breadcrumb{ ["Item1", "item1-url"] }
96
98
  receiver_class.breadcrumb{ "Item2" }
97
-
99
+ assert_that(subject.any_breadcrumbs?).is_true
98
100
  assert_that(subject.breadcrumbs)
99
101
  .equals([
100
102
  MuchRails::ViewModels::Breadcrumb.new("Item1", "item1-url"),
@@ -102,7 +104,7 @@ module MuchRails::Layout
102
104
  ])
103
105
  end
104
106
 
105
- should "know its #stylesheets" do
107
+ should "know its stylesheets" do
106
108
  receiver = receiver_class.new
107
109
  assert_that(receiver.stylesheets).is_empty
108
110
 
@@ -116,7 +118,7 @@ module MuchRails::Layout
116
118
  ])
117
119
  end
118
120
 
119
- should "know its #javascripts" do
121
+ should "know its javascripts" do
120
122
  receiver = receiver_class.new
121
123
  assert_that(receiver.javascripts).is_empty
122
124
 
@@ -134,12 +136,16 @@ module MuchRails::Layout
134
136
  ])
135
137
  end
136
138
 
137
- should "know its #any_breadcrumbs?" do
139
+ should "know its head links" do
138
140
  receiver = receiver_class.new
139
- assert_that(receiver.any_breadcrumbs?).is_false
141
+ assert_that(receiver.head_links).is_empty
140
142
 
141
- receiver_class.breadcrumb{ "Item2" }
142
- assert_that(subject.any_breadcrumbs?).is_true
143
+ url1 = Factory.url
144
+ attributes1 = { some_attribute: Factory.string }
145
+ receiver_class.head_link(url1, **attributes1)
146
+
147
+ assert_that(subject.head_links)
148
+ .equals([unit_module::HeadLink.new(url1, **attributes1)])
143
149
  end
144
150
 
145
151
  should "know its #layouts" do
@@ -23,10 +23,51 @@ module MuchRails
23
23
  desc ".config"
24
24
  subject{ unit_class.config }
25
25
 
26
- should have_imeths :action
26
+ should have_imeths :action, :layout
27
+ should have_imeths :add_save_service_validation_error
28
+ should have_imeths :add_destroy_service_validation_error
27
29
 
28
30
  should "be configured as expected" do
29
31
  assert_that(subject.action).is_not_nil
32
+ assert_that(subject.layout).is_not_nil
33
+ end
34
+ end
35
+
36
+ class ConfigServiceValidationErrorTests < ConfigTests
37
+ setup do
38
+ Assert.stub_on_call(
39
+ MuchRails::SaveService::ValidationErrors,
40
+ :add,
41
+ ) do |call|
42
+ @save_service_validation_errors_add_call = call
43
+ end
44
+ Assert.stub_on_call(
45
+ MuchRails::DestroyService::ValidationErrors,
46
+ :add,
47
+ ) do |call|
48
+ @destroy_service_validation_errors_add_call = call
49
+ end
50
+ end
51
+
52
+ let(:exception_class){ StandardError }
53
+ let(:block){ proc{ MuchResult.failure } }
54
+
55
+ should "know how to add an exception class "\
56
+ "to the save service validation errors" do
57
+ subject.add_save_service_validation_error(exception_class, &block)
58
+ assert_that(@save_service_validation_errors_add_call.args)
59
+ .equals([exception_class])
60
+ assert_that(@save_service_validation_errors_add_call.block)
61
+ .is(block)
62
+ end
63
+
64
+ should "know how to add an exception class "\
65
+ "to the destroy service validation errors" do
66
+ subject.add_destroy_service_validation_error(exception_class, &block)
67
+ assert_that(@destroy_service_validation_errors_add_call.args)
68
+ .equals([exception_class])
69
+ assert_that(@destroy_service_validation_errors_add_call.block)
70
+ .is(block)
30
71
  end
31
72
  end
32
73
 
@@ -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::ValidateDestroy::DestructionInvalid
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::ValidateDestroy::DestructionInvalid)
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::ValidateDestroy::DestructionInvalid)
65
+ .raises(MuchRails::Records::DestructionInvalid)
66
66
  assert_that(exception.message)
67
67
  .equals("TEST DESTRUCTION ERROR1\nTEST DESTRUCTION ERROR2")
68
- assert_that(exception.destruction_errors)
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::ValidateDestroy::DestructionInvalid)
75
- assert_that(exception.destruction_errors)
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 RecordInvalidErrorSetupTests < ReceiverTests
48
- desc "with an ActiveRecord::RecordInvalid error"
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(exception, :record){ record }
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){ ActiveRecord::RecordInvalid.new }
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 ExceptionWithRecordErrorsTests < RecordInvalidErrorSetupTests
57
- desc "with an exception that has record errors"
93
+ class ValidationErrorsTests < UnitTests
94
+ desc "ValidationErrors"
95
+ subject{ unit_class::ValidationErrors }
58
96
 
59
- let(:record){ @fake_record ||= FakeRecord.new }
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
- should "return a failure result with the exception and record errors" do
62
- result = subject.call(exception: exception)
108
+ class ValidationErrorsAddTests < ValidationErrorsTests
109
+ desc ".add"
63
110
 
64
- assert_that(result.failure?).is_true
65
- assert_that(result.exception).equals(exception)
66
- assert_that(result.validation_errors)
67
- .equals(some_field: %w[ERROR1 ERROR2])
68
- assert_that(result.validation_error_messages)
69
- .equals(["some_field ERROR1", "some_field ERROR2"])
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 ExceptionWithoutRecordErrorsTests < RecordInvalidErrorSetupTests
74
- desc "with an exception that has no record errors"
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(:record){ nil }
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.call(exception: exception)
191
+ result = subject.result_for(no_record_exception)
81
192
 
82
193
  assert_that(result.failure?).is_true
83
- assert_that(result.exception).equals(exception)
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