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,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "assert"
4
+ require "much-rails/mixin"
5
+
6
+ module MuchRails::Mixin
7
+ class UnitTests < Assert::Context
8
+ desc "MuchRails::Mixin"
9
+ subject{ unit_class }
10
+
11
+ let(:unit_class){ MuchRails::Mixin }
12
+
13
+ should "be MuchMixin" do
14
+ assert_that(unit_class).is(MuchMixin)
15
+ end
16
+ end
17
+ end
@@ -1,14 +1,92 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "assert"
4
+ require "much-rails"
2
5
 
3
6
  module MuchRails
4
7
  class UnitTests < Assert::Context
5
8
  desc "MuchRails"
6
- subject { unit_class }
9
+ subject{ unit_class }
10
+
11
+ let(:unit_class){ MuchRails }
12
+
13
+ should have_imeths :config, :configure_much_rails, :configure
14
+
15
+ should "be configured as expected" do
16
+ assert_that(subject).includes(MuchRails::Config)
17
+ assert_that(subject).includes(MuchRails::NotGiven)
18
+ assert_that(subject.config).is_not_nil
19
+ end
20
+ end
21
+
22
+ class ConfigTests < UnitTests
23
+ desc ".config"
24
+ subject{ unit_class.config }
25
+
26
+ should have_imeths :action, :layout
27
+ should have_imeths :add_save_service_validation_error
28
+ should have_imeths :add_destroy_service_validation_error
29
+
30
+ should "be configured as expected" do
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)
71
+ end
72
+ end
73
+
74
+ class ActionConfigTests < UnitTests
75
+ desc ".action"
76
+ subject{ unit_class.config.action }
77
+
78
+ should have_accessors :namespace
79
+ should have_accessors :sanitized_exception_classes
80
+ should have_accessors :raise_response_exceptions
7
81
 
8
- let(:unit_class) { Assert }
82
+ should have_imeths :raise_response_exceptions?
9
83
 
10
- should "be" do
11
- assert_that(unit_class).is_not_nil
84
+ should "be configured as expected" do
85
+ assert_that(subject.namespace).equals("")
86
+ assert_that(subject.sanitized_exception_classes)
87
+ .equals([ActiveRecord::RecordInvalid])
88
+ assert_that(subject.raise_response_exceptions).is_false
89
+ assert_that(subject.raise_response_exceptions?).is_false
12
90
  end
13
91
  end
14
92
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "assert"
4
+ require "much-rails/not_given"
5
+
6
+ module MuchRails::NotGiven
7
+ class UnitTests < Assert::Context
8
+ desc "MuchRails::NotGiven"
9
+ subject{ unit_class }
10
+
11
+ let(:unit_class){ MuchRails::NotGiven }
12
+
13
+ should "be MuchNotGiven" do
14
+ assert_that(unit_class).is(MuchNotGiven)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "assert"
4
+ require "much-rails/rails_routes"
5
+
6
+ class MuchRails::RailsRoutes
7
+ class UnitTests < Assert::Context
8
+ desc "MuchRails::RailsRoutes"
9
+ subject{ unit_class }
10
+
11
+ let(:unit_class){ MuchRails::RailsRoutes }
12
+
13
+ should "include Singleton, Rails url helpers" do
14
+ assert_that(subject).includes(Singleton)
15
+ assert_that(subject).includes(::Rails.application.routes.url_helpers)
16
+ end
17
+ end
18
+
19
+ class InitTests < UnitTests
20
+ desc "when init"
21
+ subject{ unit_class.instance }
22
+
23
+ should "know its default URL options" do
24
+ assert_that(subject.__send__(:default_url_options))
25
+ .equals(::Rails.application.config.action_mailer.default_url_options)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "assert"
4
+ require "much-rails/records/always_destroyable"
5
+
6
+ module MuchRails::Records::AlwaysDestroyable
7
+ class UnitTests < Assert::Context
8
+ desc "MuchRails::Records::AlwaysDestroyable"
9
+ subject{ unit_class }
10
+
11
+ let(:unit_class){ MuchRails::Records::AlwaysDestroyable }
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.new }
21
+
22
+ let(:receiver_class) do
23
+ Class.new do
24
+ def destroy(*)
25
+ true
26
+ end
27
+
28
+ include MuchRails::Records::AlwaysDestroyable
29
+ end
30
+ end
31
+
32
+ should "enable destroying a record" do
33
+ assert_that(subject.destruction_error_messages).equals([])
34
+
35
+ assert_that(subject.destroy).is_true
36
+
37
+ # won't raise MuchRails::Records::DestructionInvalid
38
+ subject.destroy!
39
+
40
+ assert_that(subject.destroyable?).is_true
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "assert"
4
+ require "much-rails/records/not_destroyable"
5
+
6
+ module MuchRails::Records::NotDestroyable
7
+ class UnitTests < Assert::Context
8
+ desc "MuchRails::Records::NotDestroyable"
9
+ subject{ unit_class }
10
+
11
+ let(:unit_class){ MuchRails::Records::NotDestroyable }
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.new }
21
+
22
+ let(:receiver_class) do
23
+ Class.new do
24
+ include MuchRails::Records::NotDestroyable
25
+ end
26
+ end
27
+
28
+ should "disable destroying a record" do
29
+ assert_that(subject.destruction_error_messages)
30
+ .equals(["#{subject.class.name} records can't be deleted."])
31
+
32
+ assert_that(subject.destroy).is_false
33
+
34
+ assert_that(->{ subject.destroy! })
35
+ .raises(MuchRails::Records::DestructionInvalid)
36
+
37
+ assert_that(subject.destroyable?).is_false
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,252 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "assert"
4
+ require "much-rails/records/validate_destroy"
5
+
6
+ module MuchRails::Records::ValidateDestroy
7
+ class UnitTests < Assert::Context
8
+ desc "MuchRails::Records::ValidateDestroy"
9
+ subject{ unit_class }
10
+
11
+ let(:unit_class){ MuchRails::Records::ValidateDestroy }
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 "when init"
20
+ subject{ receiver_class.new }
21
+
22
+ setup do
23
+ Assert.stub_tap_on_call(subject, :destroy!) do |_, call|
24
+ @destroy_bang_call = call
25
+ end
26
+ end
27
+
28
+ let(:receiver_class){ FakeRecordMissingValidateDestroyClass }
29
+
30
+ should have_imeths :destruction_error_messages
31
+ should have_imeths :destroy, :destroy!
32
+ should have_imeths :destroyable?, :not_destroyable?
33
+
34
+ should "not implement its #validate_destroy method" do
35
+ assert_that(->{ subject.instance_eval{ validate_destroy } })
36
+ .raises(NotImplementedError)
37
+ end
38
+ end
39
+
40
+ class ReceiverWithDestructionErrorMessagesTests < ReceiverTests
41
+ desc "with destruction error messages"
42
+ setup do
43
+ subject.destruction_errors_exist = true
44
+ end
45
+
46
+ let(:receiver_class){ FakeRecordClass }
47
+
48
+ should "validate destroying records" do
49
+ subject.destroyable?
50
+ assert_that(subject.destruction_error_messages)
51
+ .equals(["TEST DESTRUCTION ERROR1", "TEST DESTRUCTION ERROR2"])
52
+
53
+ subject.destroy
54
+ assert_that(subject.super_destroy_called).is_nil
55
+
56
+ subject.destroy(validate: false)
57
+ assert_that(subject.super_destroy_called).is_true
58
+
59
+ subject.super_destroy_called = nil
60
+ subject.destroy_without_validation
61
+ assert_that(subject.super_destroy_called).is_true
62
+
63
+ exception =
64
+ assert_that(->{ subject.destroy! })
65
+ .raises(MuchRails::Records::DestructionInvalid)
66
+ assert_that(exception.message)
67
+ .equals("TEST DESTRUCTION ERROR1\nTEST DESTRUCTION ERROR2")
68
+ assert_that(exception.errors)
69
+ .equals(base: subject.destruction_error_messages.to_a)
70
+ assert_that(subject.super_destroy_called).is_true
71
+
72
+ exception =
73
+ assert_that(->{ subject.destroy!(as: :thing) })
74
+ .raises(MuchRails::Records::DestructionInvalid)
75
+ assert_that(exception.errors)
76
+ .equals(thing: subject.destruction_error_messages.to_a)
77
+
78
+ subject.super_destroy_called = nil
79
+ subject.destroy!(validate: false)
80
+ assert_that(subject.super_destroy_called).is_true
81
+
82
+ @destroy_bang_call = nil
83
+ subject.destroy_without_validation!
84
+ assert_that(@destroy_bang_call.args).equals([as: :base, validate: false])
85
+
86
+ @destroy_bang_call = nil
87
+ subject.destroy_without_validation!(as: :thing)
88
+ assert_that(@destroy_bang_call.args).equals([as: :thing, validate: false])
89
+
90
+ assert_that(subject.destroyable?).is_false
91
+ assert_that(subject.not_destroyable?).is_true
92
+ end
93
+ end
94
+
95
+ class ReceiverWithNoDestructionErrorMessagesTests < ReceiverTests
96
+ desc "with no destruction error messages"
97
+ setup do
98
+ subject.destruction_errors_exist = false
99
+ end
100
+
101
+ let(:receiver_class){ FakeRecordClass }
102
+
103
+ should "validate destroying records" do
104
+ subject.destroyable?
105
+ assert_that(subject.destruction_error_messages).equals([])
106
+
107
+ subject.destroy
108
+ assert_that(subject.super_destroy_called).is_true
109
+
110
+ subject.super_destroy_called = nil
111
+ subject.destroy_without_validation
112
+ assert_that(subject.super_destroy_called).is_true
113
+
114
+ subject.super_destroy_called = nil
115
+ subject.destroy!
116
+ assert_that(subject.super_destroy_called).is_true
117
+
118
+ @destroy_bang_call = nil
119
+ subject.destroy_without_validation!
120
+ assert_that(@destroy_bang_call.args).equals([as: :base, validate: false])
121
+
122
+ assert_that(subject.destroyable?).is_true
123
+ assert_that(subject.not_destroyable?).is_false
124
+ end
125
+ end
126
+
127
+ class DestroyFalseTests < ReceiverTests
128
+ desc "when #destroy returns false"
129
+ setup do
130
+ Assert.stub(subject, :destroy){ false }
131
+ end
132
+
133
+ let(:receiver_class){ FakeRecordClass }
134
+
135
+ should "raise ActiveRecord::RecordNotDestroyed" do
136
+ assert_that(->{ subject.destroy! })
137
+ .raises(ActiveRecord::RecordNotDestroyed)
138
+ end
139
+ end
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
+
216
+ require "active_record"
217
+
218
+ class FakeRecordBaseClass
219
+ extend ActiveRecord::Translation
220
+ # Include ActiveRecord::Persistence to test the `destroy!` logic
221
+ # (the `_raise_record_not_destroyed` method) that we had to re-implement in
222
+ # the MuchRails::Records::ValidateDestroy.
223
+ include ActiveRecord::Persistence
224
+
225
+ attr_accessor :super_destroy_called
226
+
227
+ attr_accessor :destruction_errors_exist
228
+
229
+ def self.base_class?
230
+ true
231
+ end
232
+
233
+ def destroy
234
+ @super_destroy_called = true
235
+ end
236
+ end
237
+
238
+ class FakeRecordClass < FakeRecordBaseClass
239
+ include MuchRails::Records::ValidateDestroy
240
+
241
+ def validate_destroy
242
+ return unless destruction_errors_exist
243
+
244
+ destruction_error_messages << "TEST DESTRUCTION ERROR1"
245
+ destruction_error_messages << "TEST DESTRUCTION ERROR2"
246
+ end
247
+ end
248
+
249
+ class FakeRecordMissingValidateDestroyClass < FakeRecordBaseClass
250
+ include MuchRails::Records::ValidateDestroy
251
+ end
252
+ end