crudable-rails 1.3 → 1.5.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.
- checksums.yaml +4 -4
- data/README.md +194 -10
- data/Rakefile +4 -2
- data/lib/crudable/rails/base.rb +133 -19
- data/lib/crudable/rails/controller.rb +1 -2
- data/lib/crudable/rails/engine.rb +7 -1
- data/lib/crudable/rails/generators/scaffold_controller_extension.rb +55 -0
- data/lib/crudable/rails/generators/scaffold_extension.rb +29 -0
- data/lib/crudable/rails/generators/turbo_forms_generator_option.rb +16 -0
- data/lib/crudable/rails/generators/turbo_forms_scaffold_extension.rb +44 -0
- data/lib/crudable/rails/installer.rb +35 -0
- data/lib/crudable/rails/nestable.rb +78 -9
- data/lib/crudable/rails/resourceable.rb +29 -3
- data/lib/crudable/rails/version.rb +1 -1
- data/lib/crudable-rails.rb +8 -1
- data/lib/generators/crudable/controller/USAGE +23 -0
- data/lib/generators/crudable/controller/controller_generator.rb +19 -0
- data/lib/generators/crudable/controller_helpers.rb +58 -0
- data/lib/generators/crudable/install/USAGE +8 -0
- data/lib/generators/crudable/install/install_generator.rb +19 -0
- data/lib/generators/crudable/scaffold/USAGE +20 -0
- data/lib/generators/crudable/scaffold/scaffold_generator.rb +16 -0
- data/lib/generators/crudable/templates/crudable_controller.rb.tt +257 -0
- data/lib/generators/crudable/templates/turbo_forms/_form.html.erb.tt +37 -0
- data/lib/generators/crudable/templates/turbo_forms/edit.html.erb.tt +14 -0
- data/lib/generators/crudable/templates/turbo_forms/edit.turbo_stream.erb.tt +3 -0
- data/lib/generators/crudable/templates/turbo_forms/new.html.erb.tt +13 -0
- data/lib/generators/crudable/templates/turbo_forms/new.turbo_stream.erb.tt +3 -0
- data/lib/tasks/crudable_tasks.rake +14 -4
- metadata +35 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '082ec216ec1a73916737ffc58f6db08819237176efcaceb3b8c3978b5d3667e7'
|
|
4
|
+
data.tar.gz: 4ff9fd3a946d9ee5bb2839d0c41b72eac71a5139f26b87d01c9a608c8fa9204a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 894987edb86a9fd16948ea85009387f8872667bfa2b49b0852193a3277147ffa39186450dcd7632a0984881ca3fea5723e3a024d8516e1ffacea5f2d72a7d50b
|
|
7
|
+
data.tar.gz: 9b3cd6f17af0ef03e9e95186fac23a7fbfd9e9572d079a93c4e1886fc29b112348bc23f0d39476a5b91843c98781035781e4d81b8cf44e506053f1505de854f4
|
data/README.md
CHANGED
|
@@ -30,22 +30,57 @@ Or install it yourself as:
|
|
|
30
30
|
$ gem install crudable-rails
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
Then run the install generator in your Rails application:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
bin/rails generate crudable:install
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
This sets `config.action_controller.raise_on_missing_callback_actions` to `true` in any environment file under `config/environments/` that currently sets it to `false`. Crudable controllers rely on `before_action` callbacks; enabling this setting helps catch invalid `only` / `except` options early in development and test.
|
|
40
|
+
|
|
41
|
+
You can also run the same step as a Rake task:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
bin/rails crudable:install
|
|
45
|
+
```
|
|
46
|
+
|
|
33
47
|
## Usage
|
|
34
48
|
|
|
35
49
|
### Controller Setup
|
|
36
50
|
|
|
37
|
-
To use crudable-rails in your controllers, call the `crudable` method.
|
|
51
|
+
To use crudable-rails in your controllers, call the `crudable` method.
|
|
38
52
|
|
|
39
|
-
|
|
40
|
-
class ProductsController < ApplicationController
|
|
41
|
-
crudable
|
|
42
|
-
end
|
|
53
|
+
### Generators
|
|
43
54
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
55
|
+
The gem ships Rails generators to scaffold resources with a Crudable controller. Generated controllers include `crudable`, a `permitted_params` method, and commented examples of every override hook with its default implementation — uncomment and tailor as needed.
|
|
56
|
+
|
|
57
|
+
**Full scaffold** — model, migration, views, routes, tests, and a Crudable controller (same output as `rails generate scaffold`, but with a Crudable controller):
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
bin/rails generate crudable:scaffold Post title:string body:text
|
|
61
|
+
bin/rails generate crudable:scaffold Post title:string body:text --turbo-forms
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
With `--turbo-forms`, generated `new` and `edit` views wrap the `_form` partial in a `dom_id(record, :form)` target and add matching `new.turbo_stream.erb` / `edit.turbo_stream.erb` templates. Failed create/update actions rendered by crudable will replace that form partial with validation errors inline.
|
|
65
|
+
|
|
66
|
+
**Controller only** — when you already have a model and views, or want to wire up the controller yourself:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
bin/rails generate crudable:controller Post title:string body:text
|
|
47
70
|
```
|
|
48
71
|
|
|
72
|
+
You can also pass `--crudable` to the built-in Rails generators:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
bin/rails generate scaffold Post title:string body:text --crudable
|
|
76
|
+
bin/rails generate scaffold Post title:string body:text --crudable --turbo-forms
|
|
77
|
+
bin/rails generate scaffold_controller Post title:string body:text --crudable
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Use `rails generate scaffold` without `--crudable` when you want the standard Rails scaffold controller.
|
|
81
|
+
|
|
82
|
+
Nested routing is supported automatically: when a parent `*_id` param is present and an association exists between the parent and resource, collections and member actions are scoped through the parent.
|
|
83
|
+
|
|
49
84
|
## Customizing CRUD Actions
|
|
50
85
|
|
|
51
86
|
You can override the default behavior of CRUD actions by defining the following methods in your controller:
|
|
@@ -93,6 +128,103 @@ By default, Turbo Streams are supported for create, update, and destroy actions.
|
|
|
93
128
|
|
|
94
129
|
This method should return the permitted parameters for the resource as an array. It's required to be defined on all controllers.
|
|
95
130
|
|
|
131
|
+
The example below uses a `Product` with a `has_many :product_sizes` association and `accepts_nested_attributes_for`:
|
|
132
|
+
|
|
133
|
+
```ruby
|
|
134
|
+
# app/models/product.rb
|
|
135
|
+
class Product < ApplicationRecord
|
|
136
|
+
has_many :product_sizes
|
|
137
|
+
accepts_nested_attributes_for :product_sizes, allow_destroy: true
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# app/controllers/products_controller.rb
|
|
141
|
+
class ProductsController < ApplicationController
|
|
142
|
+
crudable
|
|
143
|
+
|
|
144
|
+
private
|
|
145
|
+
|
|
146
|
+
def permitted_params
|
|
147
|
+
[:name, product_sizes_attributes: [[:id, :name, :_destroy]]]
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
A typical request payload for create/update looks like:
|
|
153
|
+
|
|
154
|
+
```json
|
|
155
|
+
{
|
|
156
|
+
"product": {
|
|
157
|
+
"name": "T-shirt",
|
|
158
|
+
"product_sizes_attributes": {
|
|
159
|
+
"0": { "name": "Small" },
|
|
160
|
+
"1": { "id": "42", "name": "Medium" },
|
|
161
|
+
"2": { "id": "43", "_destroy": "1" }
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
For `has_many` nested attributes, Rails 8 `expect` requires **double array brackets** — an array wrapped in an array — to declare that each nested record is a hash (`[[:id, :name, :_destroy]]`). This form also works with `require.permit` on hash-style payloads from `fields_for` / `accepts_nested_attributes_for`, so the same `permitted_params` definition works on both Rails versions.
|
|
168
|
+
|
|
169
|
+
### `param_method`
|
|
170
|
+
|
|
171
|
+
Controls how strong parameters are required and permitted in `resource_params`. On **Rails 8+**, the default is `:expect`, which uses `params.expect` to require and permit in a single step. On earlier Rails versions, it falls back to `:require` (`params.require(...).permit(...)`).
|
|
172
|
+
|
|
173
|
+
**Rails 8 (default — `:expect`)**
|
|
174
|
+
|
|
175
|
+
```ruby
|
|
176
|
+
class ProductsController < ApplicationController
|
|
177
|
+
crudable
|
|
178
|
+
|
|
179
|
+
private
|
|
180
|
+
|
|
181
|
+
def permitted_params
|
|
182
|
+
[:name, product_sizes_attributes: [[:id, :name, :_destroy]]]
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# resource_params is equivalent to:
|
|
187
|
+
# params.expect(product: [:name, product_sizes_attributes: [[:id, :name, :_destroy]]])
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Rails 7 and earlier (default — `:require`)**
|
|
191
|
+
|
|
192
|
+
```ruby
|
|
193
|
+
class ProductsController < ApplicationController
|
|
194
|
+
crudable
|
|
195
|
+
|
|
196
|
+
private
|
|
197
|
+
|
|
198
|
+
def permitted_params
|
|
199
|
+
[:name, product_sizes_attributes: [[:id, :name, :_destroy]]]
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# resource_params is equivalent to:
|
|
204
|
+
# params.require(:product).permit(:name, product_sizes_attributes: [[:id, :name, :_destroy]])
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Override to use `require` on Rails 8**
|
|
208
|
+
|
|
209
|
+
```ruby
|
|
210
|
+
class ProductsController < ApplicationController
|
|
211
|
+
crudable
|
|
212
|
+
|
|
213
|
+
private
|
|
214
|
+
|
|
215
|
+
def param_method
|
|
216
|
+
:require
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def permitted_params
|
|
220
|
+
[:name, product_sizes_attributes: [[:id, :name, :_destroy]]]
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# resource_params is equivalent to:
|
|
225
|
+
# params.require(:product).permit(:name, product_sizes_attributes: [[:id, :name, :_destroy]])
|
|
226
|
+
```
|
|
227
|
+
|
|
96
228
|
### Optional Private Methods
|
|
97
229
|
|
|
98
230
|
The following private methods are available for use in your controllers:
|
|
@@ -137,6 +269,14 @@ This method should return a boolean value to determine if the resource should be
|
|
|
137
269
|
|
|
138
270
|
This method should return a boolean value to determine if the resource is a singleton. Default: `false`.
|
|
139
271
|
|
|
272
|
+
### `use_parent_as_scope?`
|
|
273
|
+
|
|
274
|
+
This method should return a boolean value to determine if the resource should be found using the nested parent. Default: `true`.
|
|
275
|
+
|
|
276
|
+
### `parent_id_param`
|
|
277
|
+
|
|
278
|
+
When using nested routes, this determines which `*_id` param is treated as the parent id. If multiple `*_id` params are present (multi-level nesting), Crudable will default to the **deepest/last** `*_id` from the route path parameters.
|
|
279
|
+
|
|
140
280
|
### `finder_param`
|
|
141
281
|
|
|
142
282
|
This method should return the parameter used to find the resource. Default: `:id`.
|
|
@@ -147,15 +287,59 @@ This method should return a boolean value to determine if the resource should be
|
|
|
147
287
|
|
|
148
288
|
### `friendly_finders?`
|
|
149
289
|
|
|
150
|
-
This method should return a boolean value to determine if the resource should be found using friendly finders.
|
|
290
|
+
This method should return a boolean value to determine if the resource should be found using friendly finders.
|
|
291
|
+
|
|
292
|
+
Default: `true` when `FriendlyId` is available **and** the model responds to `.friendly`, otherwise `false`.
|
|
293
|
+
|
|
294
|
+
### `parent_friendly_finders?`
|
|
295
|
+
|
|
296
|
+
When using nested routes (e.g. `/:parent_id/:id`), this method controls whether the **parent** should be found using FriendlyId.
|
|
297
|
+
|
|
298
|
+
Default: `friendly_finders?` (and additionally requires `FriendlyId` to be defined and the parent model to respond to `.friendly`).
|
|
151
299
|
|
|
152
300
|
### `skip_initialize_create?`
|
|
153
301
|
|
|
154
302
|
This method should return a boolean value to determine if the resource should be initialized on create. Default: `false`.
|
|
155
303
|
|
|
304
|
+
### `authorize_with_pundit?`
|
|
305
|
+
|
|
306
|
+
This method controls whether Pundit authorization should be performed. By default, it returns `true` when Pundit is defined. You can override this method to customize authorization behavior based on feature flags, user roles, or other conditions.
|
|
307
|
+
|
|
308
|
+
For example:
|
|
309
|
+
|
|
310
|
+
```ruby
|
|
311
|
+
class ProductsController < ApplicationController
|
|
312
|
+
crudable
|
|
313
|
+
|
|
314
|
+
private
|
|
315
|
+
|
|
316
|
+
def authorize_with_pundit?
|
|
317
|
+
super && current_user.present? && feature_enabled?(:authorization)
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
```
|
|
321
|
+
|
|
156
322
|
### `(create|update)_params`
|
|
157
323
|
|
|
158
|
-
These methods
|
|
324
|
+
These methods delegate to `resource_params` by default. Override them when create and update actions need different permitted parameters — for example, allowing new sizes on create but only updates and destroys on update:
|
|
325
|
+
|
|
326
|
+
```ruby
|
|
327
|
+
class ProductsController < ApplicationController
|
|
328
|
+
crudable
|
|
329
|
+
|
|
330
|
+
private
|
|
331
|
+
|
|
332
|
+
def create_params
|
|
333
|
+
params.expect(product: [:name, product_sizes_attributes: [[:name]]])
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def update_params
|
|
337
|
+
params.expect(product: [:name, product_sizes_attributes: [[:id, :name, :_destroy]]])
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
See `param_method` and `permitted_params` for the default strong-parameter handling.
|
|
159
343
|
|
|
160
344
|
## Overriding `authorizable_resource` with a Namespace
|
|
161
345
|
|
data/Rakefile
CHANGED
data/lib/crudable/rails/base.rb
CHANGED
|
@@ -5,47 +5,76 @@ module Crudable
|
|
|
5
5
|
module Base
|
|
6
6
|
extend ActiveSupport::Concern
|
|
7
7
|
|
|
8
|
+
# Status code for unprocessable entity (422)
|
|
9
|
+
# Rails 7.1+ uses :unprocessable_content (replaces deprecated :unprocessable_entity)
|
|
10
|
+
UNPROCESSABLE_STATUS = :unprocessable_content
|
|
11
|
+
|
|
8
12
|
included do
|
|
9
13
|
include Crudable::Rails::Resourceable
|
|
14
|
+
include Crudable::Rails::Nestable
|
|
10
15
|
|
|
11
16
|
before_action :find_resource, only: %i[show edit update destroy]
|
|
12
|
-
before_action :authorize_class_action, only: %i[index] if defined?(Pundit)
|
|
13
|
-
after_action :authorize_resource, only: %i[new show edit] if defined?(Pundit)
|
|
14
17
|
|
|
15
18
|
decorates_assigned plural_resource_var_name.to_sym, resource_var_name.to_sym if defined?(Draper)
|
|
16
19
|
end
|
|
17
20
|
|
|
18
21
|
def index
|
|
22
|
+
authorize_class_action if authorize_with_pundit?
|
|
19
23
|
instance_variable_set("@#{plural_resource_var_name}", resource_scope)
|
|
20
24
|
paginate_resource
|
|
21
25
|
end
|
|
22
26
|
|
|
23
|
-
def show
|
|
27
|
+
def show
|
|
28
|
+
render_not_found if instance_variable_get("@#{resource_var_name}").blank?
|
|
29
|
+
authorize_resource if authorize_with_pundit?
|
|
30
|
+
end
|
|
24
31
|
|
|
25
32
|
def new
|
|
26
|
-
|
|
33
|
+
instance = new_instance
|
|
34
|
+
# Ensure we always set a valid instance to prevent nil model warnings in Rails 8
|
|
35
|
+
raise ActiveRecord::RecordNotFound, "Could not create new #{resource_class.name}" if instance.nil?
|
|
36
|
+
|
|
37
|
+
instance_variable_set("@#{resource_var_name}", instance)
|
|
38
|
+
authorize_resource if authorize_with_pundit?
|
|
27
39
|
end
|
|
28
40
|
|
|
29
|
-
def create
|
|
30
|
-
|
|
31
|
-
|
|
41
|
+
def create
|
|
42
|
+
unless skip_initialize_create?
|
|
43
|
+
instance_variable_set("@#{resource_var_name}", new_instance)
|
|
44
|
+
instance_variable_get("@#{resource_var_name}").assign_attributes(create_params)
|
|
45
|
+
end
|
|
46
|
+
resource = instance_variable_get("@#{resource_var_name}")
|
|
47
|
+
# If skip_initialize_create? is true, resource might be nil - that's expected
|
|
48
|
+
# Only return 404 if we tried to initialize but resource is still nil
|
|
49
|
+
return render_not_found if resource.nil? && !skip_initialize_create?
|
|
50
|
+
|
|
51
|
+
# Only authorize if resource exists (skip authorization when skip_initialize_create? is true)
|
|
52
|
+
if authorize_with_pundit? && resource.present?
|
|
32
53
|
before_authorize_create
|
|
33
54
|
authorize_resource
|
|
34
55
|
after_authorize_create
|
|
56
|
+
elsif authorize_with_pundit?
|
|
57
|
+
skip_authorization
|
|
35
58
|
end
|
|
36
|
-
if
|
|
59
|
+
if resource&.save
|
|
37
60
|
on_successful_create
|
|
38
61
|
on_successful_create_render
|
|
39
62
|
else
|
|
40
63
|
on_failed_create_setup
|
|
41
64
|
on_failed_create_render
|
|
42
65
|
end
|
|
66
|
+
rescue ActionController::ParameterMissing
|
|
67
|
+
head :bad_request
|
|
43
68
|
end
|
|
44
69
|
|
|
45
|
-
def edit
|
|
70
|
+
def edit
|
|
71
|
+
render_not_found if instance_variable_get("@#{resource_var_name}").blank?
|
|
72
|
+
authorize_resource if authorize_with_pundit?
|
|
73
|
+
end
|
|
46
74
|
|
|
47
75
|
def update
|
|
48
|
-
if
|
|
76
|
+
render_not_found if instance_variable_get("@#{resource_var_name}").blank?
|
|
77
|
+
if authorize_with_pundit?
|
|
49
78
|
before_authorize_update
|
|
50
79
|
authorize_resource
|
|
51
80
|
after_authorize_update
|
|
@@ -57,10 +86,12 @@ module Crudable
|
|
|
57
86
|
on_failed_update_setup
|
|
58
87
|
on_failed_update_render
|
|
59
88
|
end
|
|
89
|
+
rescue ActionController::ParameterMissing
|
|
90
|
+
head :bad_request
|
|
60
91
|
end
|
|
61
92
|
|
|
62
93
|
def destroy
|
|
63
|
-
authorize_resource(destroy_method) if
|
|
94
|
+
authorize_resource(destroy_method) if authorize_with_pundit?
|
|
64
95
|
if instance_variable_get("@#{resource_var_name}").send(destroy_method)
|
|
65
96
|
on_successful_destroy
|
|
66
97
|
on_successful_destroy_render
|
|
@@ -72,6 +103,10 @@ module Crudable
|
|
|
72
103
|
|
|
73
104
|
private
|
|
74
105
|
|
|
106
|
+
def authorize_with_pundit?
|
|
107
|
+
defined?(Pundit)
|
|
108
|
+
end
|
|
109
|
+
|
|
75
110
|
def destroy_method
|
|
76
111
|
return :destroy unless defined?(Discard)
|
|
77
112
|
return :destroy if params[:destroy]
|
|
@@ -85,11 +120,38 @@ module Crudable
|
|
|
85
120
|
|
|
86
121
|
def find_resource
|
|
87
122
|
resource = friendly_finders? ? resource_class.friendly : resource_class
|
|
88
|
-
instance =
|
|
123
|
+
instance = find_instance(resource)
|
|
89
124
|
|
|
90
125
|
return redirect_to (resource_namespace + [instance]), status: :moved_permanently if should_redirect?(instance)
|
|
91
126
|
|
|
92
127
|
instance_variable_set("@#{resource_var_name}", instance)
|
|
128
|
+
rescue ActiveRecord::RecordNotFound
|
|
129
|
+
render_not_found
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def find_instance(resource)
|
|
133
|
+
if singleton?
|
|
134
|
+
resource.first
|
|
135
|
+
elsif parent_resource_association.present?
|
|
136
|
+
scope = instance_variable_get("@#{parent_var_name}").send(parent_resource_association)
|
|
137
|
+
scope = scope.friendly if friendly_finders?
|
|
138
|
+
scope.find(params[finder_param])
|
|
139
|
+
else
|
|
140
|
+
resource.find(params[finder_param])
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def new_instance
|
|
145
|
+
if parent_resource_association.present?
|
|
146
|
+
parent = instance_variable_get("@#{parent_var_name}")
|
|
147
|
+
# If parent is nil, it means find_parent failed - this shouldn't happen
|
|
148
|
+
# but we guard against it to prevent nil errors
|
|
149
|
+
raise ActiveRecord::RecordNotFound, "Parent #{parent_var_name} not found" if parent.nil?
|
|
150
|
+
|
|
151
|
+
parent.send(parent_resource_association).new
|
|
152
|
+
else
|
|
153
|
+
resource_class.new
|
|
154
|
+
end
|
|
93
155
|
end
|
|
94
156
|
|
|
95
157
|
def should_redirect?(instance)
|
|
@@ -111,7 +173,27 @@ module Crudable
|
|
|
111
173
|
end
|
|
112
174
|
|
|
113
175
|
def authorizable_scope
|
|
114
|
-
|
|
176
|
+
scope = find_scope
|
|
177
|
+
return scope.all unless authorize_with_pundit?
|
|
178
|
+
# Check if Pundit::Authorization is available
|
|
179
|
+
return scope.all unless if respond_to?(:pundit_authorization_available?,
|
|
180
|
+
true)
|
|
181
|
+
pundit_authorization_available?
|
|
182
|
+
else
|
|
183
|
+
self.class.included_modules.include?(Pundit::Authorization) || respond_to?(
|
|
184
|
+
:policy_scope, true
|
|
185
|
+
)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
policy_scope(resource_namespace + [scope])
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def find_scope
|
|
192
|
+
if parent_resource_association.present?
|
|
193
|
+
instance_variable_get("@#{parent_var_name}").send(parent_resource_association)
|
|
194
|
+
else
|
|
195
|
+
resource_class
|
|
196
|
+
end
|
|
115
197
|
end
|
|
116
198
|
|
|
117
199
|
def paginate_resource?
|
|
@@ -140,6 +222,14 @@ module Crudable
|
|
|
140
222
|
end
|
|
141
223
|
|
|
142
224
|
def after_create_redirect_path
|
|
225
|
+
if respond_to?(:parent_present?) && parent_present? && instance_variable_get("@#{parent_var_name}")
|
|
226
|
+
parent = instance_variable_get("@#{parent_var_name}")
|
|
227
|
+
resource_namespace + [parent, plural_resource_var_name.to_sym]
|
|
228
|
+
else
|
|
229
|
+
resource_namespace + [plural_resource_var_name.to_sym]
|
|
230
|
+
end
|
|
231
|
+
rescue NoMethodError
|
|
232
|
+
# Fallback if parent_present? or parent_var_name methods aren't available
|
|
143
233
|
resource_namespace + [plural_resource_var_name.to_sym]
|
|
144
234
|
end
|
|
145
235
|
|
|
@@ -216,16 +306,24 @@ module Crudable
|
|
|
216
306
|
def after_authorize_update; end
|
|
217
307
|
|
|
218
308
|
def on_failed_create_render
|
|
309
|
+
# Ensure resource is set before rendering the new template
|
|
310
|
+
# This prevents Rails 8 deprecation warnings about nil model arguments
|
|
311
|
+
resource = instance_variable_get("@#{resource_var_name}")
|
|
312
|
+
if resource.nil?
|
|
313
|
+
# Always initialize for rendering, even if skip_initialize_create? is true
|
|
314
|
+
# The skip_initialize_create? flag only affects the create action, not rendering
|
|
315
|
+
instance_variable_set("@#{resource_var_name}", new_instance)
|
|
316
|
+
end
|
|
219
317
|
respond_to do |format|
|
|
220
318
|
format.turbo_stream { render_action(:new) }
|
|
221
|
-
format.html { render :new, status:
|
|
319
|
+
format.html { render :new, status: UNPROCESSABLE_STATUS }
|
|
222
320
|
end
|
|
223
321
|
end
|
|
224
322
|
|
|
225
323
|
def on_failed_update_render
|
|
226
324
|
respond_to do |format|
|
|
227
325
|
format.turbo_stream { render_action(:edit) }
|
|
228
|
-
format.html { render :edit, status:
|
|
326
|
+
format.html { render :edit, status: UNPROCESSABLE_STATUS }
|
|
229
327
|
end
|
|
230
328
|
end
|
|
231
329
|
|
|
@@ -237,8 +335,16 @@ module Crudable
|
|
|
237
335
|
resource_params
|
|
238
336
|
end
|
|
239
337
|
|
|
338
|
+
def param_method
|
|
339
|
+
@param_method ||= ActionController::Parameters.method_defined?(:expect) ? :expect : :require
|
|
340
|
+
end
|
|
341
|
+
|
|
240
342
|
def resource_params
|
|
241
|
-
|
|
343
|
+
if param_method == :expect
|
|
344
|
+
params.send(param_method, resource_var_name.to_sym => permitted_params)
|
|
345
|
+
else
|
|
346
|
+
params.send(param_method, resource_var_name).permit(*permitted_params)
|
|
347
|
+
end
|
|
242
348
|
end
|
|
243
349
|
|
|
244
350
|
def permitted_params
|
|
@@ -246,14 +352,22 @@ module Crudable
|
|
|
246
352
|
end
|
|
247
353
|
|
|
248
354
|
def render_action(default_action_name)
|
|
249
|
-
logger.debug "Rendering relevant action for #{controller_path}/#{default_action_name}
|
|
250
|
-
|
|
355
|
+
logger.debug "Rendering relevant action for #{controller_path}/#{default_action_name} " \
|
|
356
|
+
"as #{request.format.symbol}"
|
|
357
|
+
if lookup_context.template_exists?("#{controller_path}/#{default_action_name}")
|
|
358
|
+
return render default_action_name
|
|
359
|
+
end
|
|
251
360
|
|
|
252
|
-
Crudable::Rails.deprecator.warn("Rendering fallback: #{action_name}, format: #{request.format.symbol}.
|
|
361
|
+
Crudable::Rails.deprecator.warn("Rendering fallback: #{action_name}, format: #{request.format.symbol}. " \
|
|
362
|
+
"Rename your template to #{default_action_name}")
|
|
253
363
|
|
|
254
364
|
logger.debug "Rendering fallback: #{action_name}"
|
|
255
365
|
render
|
|
256
366
|
end
|
|
367
|
+
|
|
368
|
+
def render_not_found
|
|
369
|
+
head :not_found
|
|
370
|
+
end
|
|
257
371
|
end
|
|
258
372
|
end
|
|
259
373
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Crudable
|
|
2
4
|
module Rails
|
|
3
5
|
class Engine < ::Rails::Engine
|
|
@@ -5,9 +7,13 @@ module Crudable
|
|
|
5
7
|
::ActionController::Base.include Crudable::Rails::Controller
|
|
6
8
|
end
|
|
7
9
|
|
|
8
|
-
initializer
|
|
10
|
+
initializer 'crudable-rails.deprecator' do |app|
|
|
9
11
|
app.deprecators[:crudable_rails] = Crudable::Rails.deprecator
|
|
10
12
|
end
|
|
13
|
+
|
|
14
|
+
initializer 'crudable-rails.generators' do
|
|
15
|
+
require_relative 'generators/scaffold_controller_extension'
|
|
16
|
+
end
|
|
11
17
|
end
|
|
12
18
|
end
|
|
13
19
|
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../../generators/crudable/controller_helpers'
|
|
4
|
+
require_relative 'turbo_forms_generator_option'
|
|
5
|
+
|
|
6
|
+
module Crudable
|
|
7
|
+
module Rails
|
|
8
|
+
module Generators
|
|
9
|
+
module ScaffoldControllerExtension
|
|
10
|
+
extend ActiveSupport::Concern
|
|
11
|
+
|
|
12
|
+
prepended do
|
|
13
|
+
include TurboFormsGeneratorOption
|
|
14
|
+
include ::Crudable::Generators::ControllerHelpers
|
|
15
|
+
|
|
16
|
+
class_option :crudable, type: :boolean, default: false,
|
|
17
|
+
desc: 'Generate with crudable controller'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def create_controller_files
|
|
21
|
+
return super unless options.crudable?
|
|
22
|
+
|
|
23
|
+
template 'crudable_controller.rb',
|
|
24
|
+
File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb")
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
Rails.application.config.after_initialize do
|
|
32
|
+
require 'rails/generators'
|
|
33
|
+
require 'rails/generators/named_base'
|
|
34
|
+
require 'rails/generators/resource_helpers'
|
|
35
|
+
require 'rails/generators/erb/scaffold/scaffold_generator'
|
|
36
|
+
require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator'
|
|
37
|
+
require 'rails/generators/rails/scaffold/scaffold_generator'
|
|
38
|
+
require_relative 'scaffold_extension'
|
|
39
|
+
require_relative 'turbo_forms_scaffold_extension'
|
|
40
|
+
|
|
41
|
+
unless Rails::Generators::ScaffoldControllerGenerator
|
|
42
|
+
.ancestors.include?(Crudable::Rails::Generators::ScaffoldControllerExtension)
|
|
43
|
+
Rails::Generators::ScaffoldControllerGenerator.prepend Crudable::Rails::Generators::ScaffoldControllerExtension
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
unless Rails::Generators::ScaffoldGenerator
|
|
47
|
+
.included_modules.include?(Crudable::Rails::Generators::ScaffoldExtension)
|
|
48
|
+
Rails::Generators::ScaffoldGenerator.include Crudable::Rails::Generators::ScaffoldExtension
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
unless Erb::Generators::ScaffoldGenerator
|
|
52
|
+
.ancestors.include?(Crudable::Rails::Generators::TurboFormsScaffoldExtension)
|
|
53
|
+
Erb::Generators::ScaffoldGenerator.prepend Crudable::Rails::Generators::TurboFormsScaffoldExtension
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'turbo_forms_generator_option'
|
|
4
|
+
|
|
5
|
+
module Crudable
|
|
6
|
+
module Rails
|
|
7
|
+
module Generators
|
|
8
|
+
module ScaffoldExtension
|
|
9
|
+
extend ActiveSupport::Concern
|
|
10
|
+
|
|
11
|
+
included do
|
|
12
|
+
include TurboFormsGeneratorOption
|
|
13
|
+
include ::Crudable::Generators::ControllerHelpers
|
|
14
|
+
|
|
15
|
+
class_option :crudable, type: :boolean, default: false,
|
|
16
|
+
desc: 'Generate with crudable controller'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def crudable_controller
|
|
20
|
+
return unless options.crudable?
|
|
21
|
+
|
|
22
|
+
template 'crudable_controller.rb',
|
|
23
|
+
File.join('app/controllers', controller_class_path, "#{controller_file_name}_controller.rb"),
|
|
24
|
+
force: true
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Crudable
|
|
4
|
+
module Rails
|
|
5
|
+
module Generators
|
|
6
|
+
module TurboFormsGeneratorOption
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
included do
|
|
10
|
+
class_option :turbo_forms, type: :boolean, default: false,
|
|
11
|
+
desc: 'Generate turbo stream form templates for new and edit actions'
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|