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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 27f3cbd1c25d6f0cf73e2c03fa74b46eafde3b21de611f2d3780dc0931673bef
4
- data.tar.gz: c912adf462f52b7ad317ff8e5e490cc8af11b97e40bc5ee62409143506590c15
3
+ metadata.gz: 612174b3a0ff8ace3d3f203a69a69586be9e2d2a7946ab20c8934e07209f8e95
4
+ data.tar.gz: d74beef0988406dc358b7bbccd6f5d23dc47e8f2997616b1323af8467c16dfff
5
5
  SHA512:
6
- metadata.gz: ce101e8082e13fafc1ae6333d2b3e741541a6de012c2d476c3befcbd589af8103f5d0980c34f0663f08c8796f2a9340c0292db987b6681a3880c04eb34ef4a4e
7
- data.tar.gz: '08807f38e83e8983ab6e5bd14bda024e39ed59d226093b669ad3cc379eeae0756c2725b4d8346978f2dd822537ccbd52f730dea86c35581b97eb3e79d997e6b4'
6
+ metadata.gz: ffe4bf14bdafb94b01c7078b70d0f01eda42e45aedc5dcc935d616e2fc5149c7b1901577af8da172c2df457da8cbeed9dbd30327276dca94ce214f808fac51be
7
+ data.tar.gz: 77a0d880a84289e969c99644429458a991c125287205b086953f73c683e1b730842d727e42621561c1b9d7cfb122de2c7f025951b7e7cafc852f50efce3a345a
data/Gemfile CHANGED
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
5
  ruby "~> 2.5"
4
6
 
5
7
  gemspec
6
8
 
7
- gem "pry", "~> 0.12.2"
9
+ gem "pry"
@@ -1,4 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support"
4
+ require "active_support/core_ext"
5
+
1
6
  require "much-rails/version"
7
+ require "much-rails/action"
8
+ require "much-rails/boolean"
9
+ require "much-rails/call_method"
10
+ require "much-rails/call_method_callbacks"
11
+ require "much-rails/change_action"
12
+ require "much-rails/change_action_result"
13
+ require "much-rails/config"
14
+ require "much-rails/date"
15
+ require "much-rails/decimal"
16
+ require "much-rails/destroy_action"
17
+ require "much-rails/destroy_service"
18
+ require "much-rails/has_slug"
19
+ require "much-rails/input_value"
20
+ require "much-rails/json"
21
+ require "much-rails/layout"
22
+ require "much-rails/not_given"
23
+ require "much-rails/mixin"
24
+ require "much-rails/records"
25
+ require "much-rails/result"
26
+ require "much-rails/save_action"
27
+ require "much-rails/save_service"
28
+ require "much-rails/service"
29
+ require "much-rails/time"
30
+ require "much-rails/view_models"
31
+ require "much-rails/wrap_and_call_method"
32
+ require "much-rails/wrap_method"
33
+
34
+ require "much-rails/railtie" if defined?(Rails::Railtie)
2
35
 
3
36
  module MuchRails
37
+ include MuchRails::Config
38
+ include MuchRails::NotGiven
39
+
40
+ add_config :much_rails, method_name: :config
41
+ singleton_class.alias_method :configure, :configure_much_rails
42
+
43
+ class MuchRailsConfig
44
+ include MuchRails::Config
45
+
46
+ add_instance_config :action, method_name: :action
47
+ add_instance_config :layout, method_name: :layout
48
+
49
+ def add_save_service_validation_error(exception_class, &block)
50
+ MuchRails::SaveService::ValidationErrors.add(exception_class, &block)
51
+ end
52
+
53
+ def add_destroy_service_validation_error(exception_class, &block)
54
+ MuchRails::DestroyService::ValidationErrors.add(exception_class, &block)
55
+ end
56
+
57
+ class ActionConfig
58
+ attr_accessor :namespace
59
+ attr_accessor :sanitized_exception_classes
60
+ attr_accessor :raise_response_exceptions
61
+
62
+ def initialize
63
+ @namespace = ""
64
+ @sanitized_exception_classes = [ActiveRecord::RecordInvalid]
65
+ @raise_response_exceptions = false
66
+ end
67
+
68
+ def raise_response_exceptions?
69
+ !!@raise_response_exceptions
70
+ end
71
+ end
72
+
73
+ class LayoutConfig
74
+ attr_accessor :full_page_title_segment_separator
75
+ attr_accessor :full_page_title_application_separator
76
+
77
+ # Override as desired in an initializer, e.g.:
78
+ #
79
+ # Example:
80
+ # # in config/initializers/much_rails.rb
81
+ # MuchRails.config.layout.full_page_title_segment_separator " / "
82
+ # MuchRails.config.layout.full_page_title_application_separator " :: "
83
+ def initialize
84
+ @full_page_title_segment_separator = " - "
85
+ @full_page_title_application_separator = " | "
86
+ end
87
+ end
88
+ end
4
89
  end
@@ -0,0 +1,415 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+ require "much-rails/action/controller"
5
+ require "much-rails/action/head_result"
6
+ require "much-rails/action/redirect_to_result"
7
+ require "much-rails/action/render_result"
8
+ require "much-rails/action/router"
9
+ require "much-rails/action/send_data_result"
10
+ require "much-rails/action/send_file_result"
11
+ require "much-rails/action/unprocessable_entity_result"
12
+ require "much-rails/boolean"
13
+ require "much-rails/config"
14
+ require "much-rails/date"
15
+ require "much-rails/mixin"
16
+ require "much-rails/time"
17
+ require "much-rails/wrap_and_call_method"
18
+
19
+ module MuchRails; end
20
+
21
+ # MuchRails::Action defines the common behaviors for all view action classes.
22
+ module MuchRails::Action
23
+ ForbiddenError = Class.new(StandardError)
24
+ ActionError = Class.new(StandardError)
25
+
26
+ include MuchRails::Mixin
27
+
28
+ mixin_included do
29
+ include MuchRails::Config
30
+ include MuchRails::WrapAndCallMethod
31
+
32
+ add_config :much_rails_action
33
+
34
+ attr_reader :params, :current_user, :request, :errors
35
+ end
36
+
37
+ mixin_class_methods do
38
+ def action_error_class
39
+ MuchRails::Action::ActionError
40
+ end
41
+
42
+ def params_root(*args)
43
+ much_rails_action_config.set_params_root(args) if args.any?
44
+ much_rails_action_config.params_root
45
+ end
46
+
47
+ def required_params(*args)
48
+ much_rails_action_config.add_required_params(args) if args.any?
49
+ much_rails_action_config.required_params
50
+ end
51
+
52
+ def date_params(*args)
53
+ much_rails_action_config.add_date_params(self, args) if args.any?
54
+ much_rails_action_config.date_params
55
+ end
56
+
57
+ def time_params(*args)
58
+ much_rails_action_config.add_time_params(self, args) if args.any?
59
+ much_rails_action_config.time_params
60
+ end
61
+
62
+ def boolean_params(*args)
63
+ much_rails_action_config.add_boolean_params(self, args) if args.any?
64
+ much_rails_action_config.boolean_params
65
+ end
66
+
67
+ def format(*args)
68
+ much_rails_action_config.set_format(args.first) if args.any?
69
+ much_rails_action_config.format
70
+ end
71
+
72
+ def on_validation(&block)
73
+ much_rails_action_config.add_on_validation_block(block)
74
+ much_rails_action_config.on_validation_blocks
75
+ end
76
+
77
+ def on_validation_blocks
78
+ much_rails_action_config.on_validation_blocks
79
+ end
80
+
81
+ def on_call(&block)
82
+ much_rails_action_config.set_on_call_block(block)
83
+ much_rails_action_config.on_call_block
84
+ end
85
+
86
+ def on_call_block
87
+ much_rails_action_config.on_call_block
88
+ end
89
+
90
+ def on_before_call(&block)
91
+ much_rails_action_config.add_on_before_call_block(block)
92
+ much_rails_action_config.on_before_call_blocks
93
+ end
94
+
95
+ def on_before_call_blocks
96
+ much_rails_action_config.on_before_call_blocks
97
+ end
98
+
99
+ def on_after_call(&block)
100
+ much_rails_action_config.add_on_after_call_block(block)
101
+ much_rails_action_config.on_after_call_blocks
102
+ end
103
+
104
+ def on_after_call_blocks
105
+ much_rails_action_config.on_after_call_blocks
106
+ end
107
+
108
+ def default_action_template_name
109
+ @default_action_template_name ||=
110
+ to_s
111
+ .remove(/\A#{MuchRails.config.action.namespace}/)
112
+ .tableize
113
+ .singularize
114
+ end
115
+ end
116
+
117
+ mixin_instance_methods do
118
+ def initialize(params: nil, current_user: nil, request: nil)
119
+ @params = params.to_h.with_indifferent_access
120
+ @current_user = current_user
121
+ @request = request
122
+ @errors = Hash.new{ |hash, key| hash[key] = [] }
123
+ end
124
+
125
+ def on_call
126
+ return @much_rails_action_result if @much_rails_action_result
127
+
128
+ catch(:halt) do
129
+ validate_action
130
+ on_action_call if valid_action?
131
+ end
132
+
133
+ if valid_action? && successful_action?
134
+ @much_rails_action_result ||= default_action_success_result
135
+ else
136
+ @much_rails_action_result = default_action_failure_result
137
+ end
138
+ end
139
+
140
+ def valid_action?
141
+ if @much_rails_valid_action.nil?
142
+ @much_rails_valid_action = errors.compact.empty?
143
+ end
144
+
145
+ @much_rails_valid_action
146
+ end
147
+
148
+ def successful_action?
149
+ if @much_rails_successful_action.nil?
150
+ @much_rails_successful_action = errors.compact.empty?
151
+ end
152
+
153
+ @much_rails_successful_action
154
+ end
155
+
156
+ private
157
+
158
+ def default_action_success_result
159
+ MuchRails::Action::HeadResult.new(:ok)
160
+ end
161
+
162
+ def default_action_failure_result
163
+ MuchRails::Action::UnprocessableEntityResult.new(errors)
164
+ end
165
+
166
+ def validate_action
167
+ validate_action_required_params
168
+ validate_action_date_params
169
+ validate_action_time_params
170
+ call_action_on_validation_blocks
171
+ end
172
+
173
+ def validate_action_required_params
174
+ self.class.required_params.each do |param_name|
175
+ add_required_param_error(param_name) if params[param_name].blank?
176
+ end
177
+ end
178
+
179
+ def validate_action_date_param(name)
180
+ __send__(name.to_s)
181
+ rescue MuchRails::Date::InvalidError
182
+ errors[name] << action_date_param_error_message
183
+ end
184
+
185
+ def validate_action_date_params
186
+ self.class.date_params.each do |param|
187
+ validate_action_date_param(param)
188
+ end
189
+ end
190
+
191
+ def validate_action_time_param(name)
192
+ __send__(name.to_s)
193
+ rescue MuchRails::Time::InvalidError
194
+ errors[name] << action_time_param_error_message
195
+ end
196
+
197
+ def validate_action_time_params
198
+ self.class.time_params.each do |param|
199
+ validate_action_time_param(param)
200
+ end
201
+ end
202
+
203
+ def call_action_on_validation_blocks
204
+ self.class.on_validation_blocks.each do |on_validation_block|
205
+ instance_eval(&on_validation_block)
206
+ end
207
+ end
208
+
209
+ def action_required_param_error_message
210
+ "can't be blank"
211
+ end
212
+
213
+ def action_date_param_error_message
214
+ "invalid date"
215
+ end
216
+
217
+ def action_time_param_error_message
218
+ "invalid time"
219
+ end
220
+
221
+ def on_action_call
222
+ call_action_on_before_call_blocks
223
+ catch(:halt){ instance_exec(&self.class.on_call_block) }
224
+ call_action_on_after_call_blocks
225
+ rescue *MuchRails.config.action.sanitized_exception_classes => ex
226
+ raise(self.class.action_error_class, ex.message, ex.backtrace, cause: ex)
227
+ end
228
+
229
+ def call_action_on_before_call_blocks
230
+ self.class.on_before_call_blocks.each do |on_before_call_block|
231
+ instance_eval(&on_before_call_block)
232
+ end
233
+ end
234
+
235
+ def call_action_on_after_call_blocks
236
+ self.class.on_after_call_blocks.each do |on_after_call_block|
237
+ instance_eval(&on_after_call_block)
238
+ end
239
+ end
240
+
241
+ def add_required_param_error(param_name)
242
+ errors[param_name] << action_required_param_error_message
243
+ end
244
+
245
+ def halt
246
+ throw(:halt)
247
+ end
248
+
249
+ def render(*args, **kargs)
250
+ template, view_model =
251
+ [
252
+ args.last.is_a?(::String) ? args.pop : nil,
253
+ args.pop,
254
+ ]
255
+ result_kargs =
256
+ {
257
+ template: template || default_action_template_name,
258
+ }.merge(**kargs)
259
+
260
+ @much_rails_action_result =
261
+ MuchRails::Action::RenderResult.new(view_model, *args, **result_kargs)
262
+ halt
263
+ end
264
+
265
+ def redirect_to(*args)
266
+ @much_rails_action_result =
267
+ MuchRails::Action::RedirectToResult.new(*args)
268
+ halt
269
+ end
270
+
271
+ def head(*args)
272
+ @much_rails_action_result =
273
+ MuchRails::Action::HeadResult.new(*args)
274
+ halt
275
+ end
276
+
277
+ def send_file(*args)
278
+ @much_rails_action_result =
279
+ MuchRails::Action::SendFileResult.new(*args)
280
+ halt
281
+ end
282
+
283
+ def send_data(*args)
284
+ @much_rails_action_result =
285
+ MuchRails::Action::SendDataResult.new(*args)
286
+ halt
287
+ end
288
+
289
+ def default_action_template_name
290
+ self.class.default_action_template_name
291
+ end
292
+ end
293
+
294
+ class MuchRailsActionConfig
295
+ attr_reader :params_root, :required_params, :date_params, :time_params
296
+ attr_reader :boolean_params, :format, :on_validation_blocks
297
+ attr_reader :on_call_block, :on_before_call_blocks, :on_after_call_blocks
298
+
299
+ def initialize
300
+ @params_root = nil
301
+ @required_params = []
302
+ @date_params = []
303
+ @time_params = []
304
+ @boolean_params = []
305
+
306
+ @format = nil
307
+
308
+ @on_validation_blocks = []
309
+ @on_before_call_blocks = []
310
+ @on_after_call_blocks = []
311
+ @on_call_block = ->{}
312
+ end
313
+
314
+ def set_params_root(value)
315
+ @params_root = value
316
+ end
317
+
318
+ def add_required_params(param_names)
319
+ @required_params.concat(Array.wrap(param_names))
320
+ end
321
+
322
+ def add_date_params(klass, param_names)
323
+ Array.wrap(param_names).tap do |names|
324
+ @date_params.concat(names)
325
+
326
+ names.each do |name|
327
+ klass.public_send(:define_method, name, &memoize_date_decode(name))
328
+ end
329
+ end
330
+ end
331
+
332
+ def add_time_params(klass, param_names)
333
+ Array.wrap(param_names).tap do |names|
334
+ @time_params.concat(names)
335
+
336
+ names.each do |name|
337
+ klass.public_send(:define_method, name, &memoize_time_decode(name))
338
+ end
339
+ end
340
+ end
341
+
342
+ def add_boolean_params(klass, param_names)
343
+ Array.wrap(param_names).tap do |names|
344
+ @boolean_params.concat(names)
345
+
346
+ names.each do |name|
347
+ klass.public_send(
348
+ :define_method, "#{name}?", &memoize_boolean_decode(name)
349
+ )
350
+ end
351
+ end
352
+ end
353
+
354
+ def set_format(value)
355
+ @format = value
356
+ end
357
+
358
+ def add_on_validation_block(block)
359
+ @on_validation_blocks << block
360
+ end
361
+
362
+ def set_on_call_block(block)
363
+ @on_call_block = block
364
+ end
365
+
366
+ def add_on_before_call_block(block)
367
+ @on_before_call_blocks << block
368
+ end
369
+
370
+ def add_on_after_call_block(block)
371
+ @on_after_call_blocks << block
372
+ end
373
+
374
+ private
375
+
376
+ def memoize_date_decode(method_name)
377
+ ->{
378
+ instance_variable_name = "@#{method_name}"
379
+ if instance_variable_get(instance_variable_name).nil?
380
+ instance_variable_set(
381
+ instance_variable_name,
382
+ MuchRails::Date.for(params[method_name]),
383
+ )
384
+ end
385
+ instance_variable_get(instance_variable_name)
386
+ }
387
+ end
388
+
389
+ def memoize_time_decode(method_name)
390
+ ->{
391
+ instance_variable_name = "@#{method_name}"
392
+ if instance_variable_get(instance_variable_name).nil?
393
+ instance_variable_set(
394
+ instance_variable_name,
395
+ MuchRails::Time.for(params[method_name]),
396
+ )
397
+ end
398
+ instance_variable_get(instance_variable_name)
399
+ }
400
+ end
401
+
402
+ def memoize_boolean_decode(method_name)
403
+ ->{
404
+ instance_variable_name = "@#{method_name}"
405
+ if instance_variable_get(instance_variable_name).nil?
406
+ instance_variable_set(
407
+ instance_variable_name,
408
+ MuchRails::Boolean.convert(params[method_name]),
409
+ )
410
+ end
411
+ instance_variable_get(instance_variable_name)
412
+ }
413
+ end
414
+ end
415
+ end