much-rails 0.0.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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