much-rails 0.0.1 → 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 (101) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -1
  3. data/lib/much-rails.rb +85 -0
  4. data/lib/much-rails/action.rb +415 -0
  5. data/lib/much-rails/action/base_command_result.rb +22 -0
  6. data/lib/much-rails/action/base_result.rb +14 -0
  7. data/lib/much-rails/action/base_router.rb +474 -0
  8. data/lib/much-rails/action/controller.rb +100 -0
  9. data/lib/much-rails/action/head_result.rb +18 -0
  10. data/lib/much-rails/action/redirect_to_result.rb +18 -0
  11. data/lib/much-rails/action/render_result.rb +25 -0
  12. data/lib/much-rails/action/router.rb +101 -0
  13. data/lib/much-rails/action/send_data_result.rb +18 -0
  14. data/lib/much-rails/action/send_file_result.rb +18 -0
  15. data/lib/much-rails/action/unprocessable_entity_result.rb +37 -0
  16. data/lib/much-rails/assets.rb +54 -0
  17. data/lib/much-rails/boolean.rb +7 -0
  18. data/lib/much-rails/call_method.rb +27 -0
  19. data/lib/much-rails/call_method_callbacks.rb +122 -0
  20. data/lib/much-rails/change_action.rb +83 -0
  21. data/lib/much-rails/change_action_result.rb +59 -0
  22. data/lib/much-rails/config.rb +55 -0
  23. data/lib/much-rails/date.rb +50 -0
  24. data/lib/much-rails/decimal.rb +7 -0
  25. data/lib/much-rails/destroy_action.rb +32 -0
  26. data/lib/much-rails/destroy_service.rb +67 -0
  27. data/lib/much-rails/has_slug.rb +7 -0
  28. data/lib/much-rails/input_value.rb +19 -0
  29. data/lib/much-rails/json.rb +29 -0
  30. data/lib/much-rails/layout.rb +142 -0
  31. data/lib/much-rails/layout/helper.rb +25 -0
  32. data/lib/much-rails/mixin.rb +7 -0
  33. data/lib/much-rails/not_given.rb +7 -0
  34. data/lib/much-rails/rails_routes.rb +29 -0
  35. data/lib/much-rails/railtie.rb +39 -0
  36. data/lib/much-rails/records.rb +5 -0
  37. data/lib/much-rails/records/always_destroyable.rb +30 -0
  38. data/lib/much-rails/records/not_destroyable.rb +30 -0
  39. data/lib/much-rails/records/validate_destroy.rb +92 -0
  40. data/lib/much-rails/result.rb +7 -0
  41. data/lib/much-rails/save_action.rb +32 -0
  42. data/lib/much-rails/save_service.rb +68 -0
  43. data/lib/much-rails/service.rb +18 -0
  44. data/lib/much-rails/service_validation_errors.rb +41 -0
  45. data/lib/much-rails/time.rb +28 -0
  46. data/lib/much-rails/version.rb +3 -1
  47. data/lib/much-rails/view_models.rb +3 -0
  48. data/lib/much-rails/view_models/breadcrumb.rb +11 -0
  49. data/lib/much-rails/wrap_and_call_method.rb +41 -0
  50. data/lib/much-rails/wrap_method.rb +45 -0
  51. data/much-rails.gemspec +20 -4
  52. data/test/helper.rb +20 -2
  53. data/test/support/actions/show.rb +11 -0
  54. data/test/support/config/routes/test.rb +3 -0
  55. data/test/support/factory.rb +2 -0
  56. data/test/support/fake_action_controller.rb +63 -0
  57. data/test/unit/action/base_command_result_tests.rb +43 -0
  58. data/test/unit/action/base_result_tests.rb +22 -0
  59. data/test/unit/action/base_router_tests.rb +530 -0
  60. data/test/unit/action/controller_tests.rb +110 -0
  61. data/test/unit/action/head_result_tests.rb +24 -0
  62. data/test/unit/action/redirect_to_result_tests.rb +24 -0
  63. data/test/unit/action/render_result_tests.rb +43 -0
  64. data/test/unit/action/router_tests.rb +252 -0
  65. data/test/unit/action/send_data_result_tests.rb +24 -0
  66. data/test/unit/action/send_file_result_tests.rb +24 -0
  67. data/test/unit/action/unprocessable_entity_result_tests.rb +51 -0
  68. data/test/unit/action_tests.rb +400 -0
  69. data/test/unit/assets_tests.rb +127 -0
  70. data/test/unit/boolean_tests.rb +17 -0
  71. data/test/unit/call_method_callbacks_tests.rb +176 -0
  72. data/test/unit/call_method_tests.rb +62 -0
  73. data/test/unit/change_action_result_tests.rb +113 -0
  74. data/test/unit/change_action_tests.rb +260 -0
  75. data/test/unit/config_tests.rb +68 -0
  76. data/test/unit/date_tests.rb +55 -0
  77. data/test/unit/decimal_tests.rb +17 -0
  78. data/test/unit/destroy_action_tests.rb +83 -0
  79. data/test/unit/destroy_service_tests.rb +238 -0
  80. data/test/unit/has_slug_tests.rb +17 -0
  81. data/test/unit/input_value_tests.rb +34 -0
  82. data/test/unit/json_tests.rb +55 -0
  83. data/test/unit/layout_tests.rb +155 -0
  84. data/test/unit/mixin_tests.rb +17 -0
  85. data/test/unit/much-rails_tests.rb +82 -4
  86. data/test/unit/not_given_tests.rb +17 -0
  87. data/test/unit/rails_routes_tests.rb +28 -0
  88. data/test/unit/records/always_destroyable_tests.rb +43 -0
  89. data/test/unit/records/not_destroyable_tests.rb +40 -0
  90. data/test/unit/records/validate_destroy_tests.rb +252 -0
  91. data/test/unit/result_tests.rb +17 -0
  92. data/test/unit/save_action_tests.rb +83 -0
  93. data/test/unit/save_service_tests.rb +264 -0
  94. data/test/unit/service_tests.rb +33 -0
  95. data/test/unit/service_validation_errors_tests.rb +107 -0
  96. data/test/unit/time_tests.rb +58 -0
  97. data/test/unit/view_models/breadcrumb_tests.rb +53 -0
  98. data/test/unit/wrap_and_call_method_tests.rb +163 -0
  99. data/test/unit/wrap_method_tests.rb +112 -0
  100. metadata +356 -7
  101. data/test/unit/.keep +0 -0
@@ -0,0 +1,238 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "assert"
4
+ require "much-rails/destroy_service"
5
+
6
+ module MuchRails::DestroyService
7
+ class UnitTests < Assert::Context
8
+ desc "MuchRails::DestroyService"
9
+ subject{ unit_class }
10
+
11
+ let(:unit_class){ MuchRails::DestroyService }
12
+
13
+ should "include MuchRails::Mixin" do
14
+ assert_that(subject).includes(MuchRails::Mixin)
15
+ end
16
+ end
17
+
18
+ class ReceiverTests < UnitTests
19
+ desc "receiver"
20
+ subject{ receiver_class }
21
+
22
+ let(:receiver_class) do
23
+ Class.new do
24
+ include MuchRails::DestroyService
25
+
26
+ def initialize(exception: nil)
27
+ @exception = exception
28
+ end
29
+
30
+ def on_call
31
+ raise @exception if @exception
32
+
33
+ MuchRails::Result.success
34
+ end
35
+ end
36
+ end
37
+
38
+ should "include MuchRails::Service" do
39
+ assert_that(subject).includes(MuchRails::Service)
40
+ end
41
+
42
+ should "return success" do
43
+ assert_that(subject.call.success?).is_true
44
+ end
45
+ end
46
+
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"
56
+ setup do
57
+ Assert.stub(
58
+ MuchRails::DestroyService::ValidationErrors,
59
+ :exception_classes,
60
+ ){ exception_classes }
61
+ Assert.stub_on_call(
62
+ MuchRails::DestroyService::ValidationErrors,
63
+ :result_for,
64
+ ) do |call|
65
+ @result_for_call = call
66
+ validation_error_result
67
+ end
68
+ end
69
+
70
+ let(:exception){ exceptions.sample }
71
+ let(:exceptions) do
72
+ [
73
+ RuntimeError.new(Factory.string),
74
+ ArgumentError.new(Factory.string),
75
+ MuchRails::Records::DestructionInvalid.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
91
+ end
92
+
93
+ class ValidationErrorsTests < UnitTests
94
+ desc "ValidationErrors"
95
+ subject{ unit_class::ValidationErrors }
96
+
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(MuchRails::Records::DestructionInvalid)
105
+ end
106
+ end
107
+
108
+ class ValidationErrorsAddTests < ValidationErrorsTests
109
+ desc ".add"
110
+
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])
168
+ end
169
+ end
170
+
171
+ class ValidationErrorsResultForDestructionInvalidTests < ValidationErrorsTests
172
+ desc "when .result_for is passed an MuchRails::Records::DestructionInvalid"
173
+
174
+ let(:exception){ MuchRails::Records::DestructionInvalid.new(record) }
175
+ let(:record){ FakeRecord.new }
176
+
177
+ should "return a failure result with the record and validation errors" do
178
+ result = subject.result_for(exception)
179
+
180
+ assert_that(result.failure?).is_true
181
+ assert_that(result.exception).equals(exception)
182
+ assert_that(result.validation_errors).equals(exception.errors.to_h)
183
+ assert_that(result.validation_error_messages)
184
+ .equals(exception.error_full_messages.to_a)
185
+ end
186
+ end
187
+
188
+ class FailureResultTests < UnitTests
189
+ desc "FailureResult"
190
+ subject{ unit_class::FailureResult }
191
+
192
+ setup do
193
+ Assert.stub_tap_on_call(MuchResult, :failure) do |_, call|
194
+ @much_result_failure_call = call
195
+ end
196
+ end
197
+
198
+ let(:exception){ StandardError.new(Factory.string) }
199
+ let(:validation_errors){ { Factory.symbol => Factory.string } }
200
+ let(:custom_value){ Factory.string }
201
+
202
+ should have_imeths :new
203
+
204
+ should "use MuchResult.failure to build a result" do
205
+ result =
206
+ subject.new(
207
+ exception: exception,
208
+ validation_errors: validation_errors,
209
+ custom: custom_value,
210
+ )
211
+ assert_that(result.failure?).is_true
212
+ assert_that(@much_result_failure_call.kargs)
213
+ .equals(
214
+ exception: exception,
215
+ validation_errors: validation_errors,
216
+ custom: custom_value,
217
+ )
218
+ end
219
+
220
+ should "raise an error without an exception or validation errors" do
221
+ assert_that{
222
+ subject.new(validation_errors: validation_errors)
223
+ }.raises(ArgumentError)
224
+ end
225
+
226
+ should "raise an error without an exception or validation errors" do
227
+ assert_that{
228
+ subject.new(exception: exception)
229
+ }.raises(ArgumentError)
230
+ end
231
+ end
232
+
233
+ class FakeRecord
234
+ def destruction_error_messages
235
+ ["ERROR1", "ERROR2"]
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "assert"
4
+ require "much-rails/has_slug"
5
+
6
+ module MuchRails::HasSlug
7
+ class UnitTests < Assert::Context
8
+ desc "MuchRails::HasSlug"
9
+ subject{ unit_class }
10
+
11
+ let(:unit_class){ MuchRails::HasSlug }
12
+
13
+ should "be MuchSlug" do
14
+ assert_that(unit_class).is(MuchSlug)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "assert"
4
+ require "much-rails/input_value"
5
+
6
+ module MuchRails::InputValue
7
+ class UnitTests < Assert::Context
8
+ desc "MuchRails::InputValue"
9
+ subject{ unit_class }
10
+
11
+ let(:unit_class){ MuchRails::InputValue }
12
+
13
+ should have_imeths :strip, :strip_all
14
+
15
+ should "know its attributes" do
16
+ # strip
17
+ input_value = [nil, "", " "].sample
18
+ assert_that(subject.strip(input_value)).is_nil
19
+
20
+ input_value = [" VALUE ", "\r VALUE\n"].sample
21
+ assert_that(subject.strip(input_value)).equals("VALUE")
22
+
23
+ assert_that(subject.strip([])).is_nil
24
+ assert_that(subject.strip({})).is_nil
25
+
26
+ # strip_all
27
+ input_values = [nil, "", " ", " VALUE "]
28
+ assert_that(subject.strip_all(input_values)).equals(["VALUE"])
29
+
30
+ input_values = [" VALUE1 ", "\r VALUE2\n"]
31
+ assert_that(subject.strip_all(input_values)).equals(["VALUE1", "VALUE2"])
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "assert"
4
+ require "much-rails/json"
5
+
6
+ module MuchRails::JSON
7
+ class UnitTests < Assert::Context
8
+ desc "MuchRails::JSON"
9
+ subject{ unit_class }
10
+
11
+ let(:unit_class){ MuchRails::JSON }
12
+
13
+ let(:object){ { some_key: "SOME VALUE" } }
14
+
15
+ should have_imeths :default_mode, :encode, :decode
16
+
17
+ should "know its attributes" do
18
+ # default_mode
19
+ assert_that(subject.default_mode).equals(:strict)
20
+
21
+ # encode
22
+ assert_that(subject.encode(object))
23
+ .equals(Oj.dump(object, mode: subject.default_mode))
24
+
25
+ options = { ascii_only: true }
26
+ assert_that(subject.encode(object, **options))
27
+ .equals(Oj.dump(object, mode: subject.default_mode, **options))
28
+
29
+ time_object = { some_key: Time.now }
30
+ mode = :null
31
+ assert_that{ subject.encode(time_object) }.raises(TypeError)
32
+ assert_that(subject.encode(time_object, mode: mode))
33
+ .equals(Oj.dump(time_object, mode: mode))
34
+
35
+ # decode
36
+ encoded_object = subject.encode(object)
37
+ assert_that(subject.decode(encoded_object))
38
+ .equals(Oj.load(encoded_object, mode: subject.default_mode))
39
+
40
+ numeric_object = { some_key: 1.25 }
41
+ options = { bigdecimal_load: :bigdecimal }
42
+ encoded_object = subject.encode(numeric_object)
43
+ decoded_object = subject.decode(encoded_object, **options)
44
+ assert_that(decoded_object)
45
+ .equals(Oj.load(encoded_object, mode: subject.default_mode, **options))
46
+ assert_that(decoded_object["some_key"].class).equals(BigDecimal)
47
+
48
+ encoded_object = "some-rando-string-that-isnt-JSON"
49
+ ex =
50
+ assert_that{ subject.decode(encoded_object) }
51
+ .raises(unit_class::InvalidError)
52
+ assert_that(ex.message).includes("Oj::ParseError")
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "assert"
4
+ require "much-rails/layout"
5
+
6
+ module MuchRails::Layout
7
+ class UnitTests < Assert::Context
8
+ desc "MuchRails::Layout"
9
+ subject{ unit_module }
10
+
11
+ let(:unit_module){ MuchRails::Layout }
12
+
13
+ should "include MuchRails::Mixin" do
14
+ assert_that(subject).includes(MuchRails::Mixin)
15
+ end
16
+ end
17
+
18
+ class ReceiverTests < UnitTests
19
+ desc "receiver"
20
+ subject{ receiver_class }
21
+
22
+ let(:receiver_class) do
23
+ Class.new do
24
+ include MuchRails::Layout
25
+
26
+ def attribute1
27
+ "attribute1"
28
+ end
29
+ end
30
+ end
31
+
32
+ should have_imeths :page_title, :page_titles, :application_page_title
33
+ should have_imeths :breadcrumb, :breadcrumbs
34
+ should have_imeths :stylesheet, :stylesheets, :javascript, :javascripts
35
+ should have_imeths :head_link, :head_links, :layout, :layouts
36
+ end
37
+
38
+ class InitTests < ReceiverTests
39
+ desc "when init"
40
+ subject{ receiver_class.new }
41
+
42
+ should "know its page title" do
43
+ assert_that(subject.page_title).is_nil
44
+
45
+ receiver_class.page_title{ "Some Portal" }
46
+ receiver_class.page_title{ "Some Resource #{attribute1}" }
47
+
48
+ assert_that(subject.page_title).equals("Some Resource attribute1")
49
+ end
50
+
51
+ should "know its application page title" do
52
+ assert_that(subject.application_page_title).is_nil
53
+
54
+ receiver_class.application_page_title{ "Some App" }
55
+ receiver_class.application_page_title{ "Some App #{attribute1}" }
56
+
57
+ assert_that(subject.application_page_title).equals("Some App attribute1")
58
+ end
59
+
60
+ should "know its full page title "\
61
+ "given no application page title or page title segments" do
62
+ assert_that(subject.full_page_title).is_nil
63
+ end
64
+
65
+ should "know its full page title "\
66
+ "given an application page title but no page title segments" do
67
+ receiver_class.application_page_title{ "Some App" }
68
+
69
+ assert_that(subject.full_page_title)
70
+ .equals(subject.application_page_title)
71
+ end
72
+
73
+ should "know its full page title "\
74
+ "given no application page title but page title segments" do
75
+ receiver_class.page_title{ "Some Portal" }
76
+ receiver_class.page_title{ "Some Resource #{attribute1}" }
77
+
78
+ assert_that(subject.full_page_title)
79
+ .equals("Some Resource attribute1 - Some Portal")
80
+ end
81
+
82
+ should "know its full page title "\
83
+ "given both an application page title and page title segments" do
84
+ receiver_class.application_page_title{ "Some App" }
85
+ receiver_class.page_title{ "Some Portal" }
86
+ receiver_class.page_title{ "Some Resource #{attribute1}" }
87
+
88
+ assert_that(subject.full_page_title)
89
+ .equals("Some Resource attribute1 - Some Portal | Some App")
90
+ end
91
+
92
+ should "know its breadcrumbs" do
93
+ receiver = receiver_class.new
94
+ assert_that(receiver.any_breadcrumbs?).is_false
95
+ assert_that(receiver.breadcrumbs).is_empty
96
+
97
+ receiver_class.breadcrumb{ ["Item1", "item1-url"] }
98
+ receiver_class.breadcrumb{ "Item2" }
99
+ assert_that(subject.any_breadcrumbs?).is_true
100
+ assert_that(subject.breadcrumbs)
101
+ .equals([
102
+ MuchRails::ViewModels::Breadcrumb.new("Item1", "item1-url"),
103
+ MuchRails::ViewModels::Breadcrumb.new("Item2"),
104
+ ])
105
+ end
106
+
107
+ should "know its stylesheets" do
108
+ receiver = receiver_class.new
109
+ assert_that(receiver.stylesheets).is_empty
110
+
111
+ receiver_class.stylesheet("some/stylesheet.css")
112
+ receiver_class.stylesheet{ "some/other-stylesheet.css" }
113
+
114
+ assert_that(subject.stylesheets)
115
+ .equals([
116
+ "some/stylesheet.css",
117
+ "some/other-stylesheet.css",
118
+ ])
119
+ end
120
+
121
+ should "know its javascripts" do
122
+ receiver = receiver_class.new
123
+ assert_that(receiver.javascripts).is_empty
124
+
125
+ receiver_class.javascript("some/javascript/pack.js")
126
+ receiver_class.javascript{ "some/other/javascript/pack.js" }
127
+ receiver_class.javascript("https://some/javascript/file.js")
128
+ receiver_class.javascript{ "https://some/other/javascript/file.js" }
129
+
130
+ assert_that(subject.javascripts)
131
+ .equals([
132
+ "some/javascript/pack.js",
133
+ "some/other/javascript/pack.js",
134
+ "https://some/javascript/file.js",
135
+ "https://some/other/javascript/file.js",
136
+ ])
137
+ end
138
+
139
+ should "know its head links" do
140
+ receiver = receiver_class.new
141
+ assert_that(receiver.head_links).is_empty
142
+
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)])
149
+ end
150
+
151
+ should "know its #layouts" do
152
+ assert_that(subject.layouts).equals(receiver_class.layouts)
153
+ end
154
+ end
155
+ end