b4um_generators 0.2.11 → 0.2.16

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -2
  3. data/README.de.md +165 -8
  4. data/README.md +162 -8
  5. data/lib/b4um_generators/version.rb +1 -1
  6. data/lib/generators/b4um/attachment/attachment_generator.rb +83 -23
  7. data/lib/generators/b4um/authentication/authentication_generator.rb +63 -0
  8. data/lib/generators/b4um/bento/bento_generator.rb +24 -0
  9. data/lib/generators/b4um/help/help_generator.rb +148 -11
  10. data/lib/generators/b4um/in_place/in_place_generator.rb +1231 -0
  11. data/lib/generators/b4um/in_place/templates/_flash.html.erb +15 -0
  12. data/lib/generators/b4um/in_place/templates/b4um_in_place_helper.rb +7 -0
  13. data/lib/generators/b4um/in_place/templates/display_attachment.html.erb.tt +154 -0
  14. data/lib/generators/b4um/in_place/templates/display_attachments.html.erb.tt +165 -0
  15. data/lib/generators/b4um/in_place/templates/display_boolean.html.erb.tt +18 -0
  16. data/lib/generators/b4um/in_place/templates/display_number.html.erb.tt +18 -0
  17. data/lib/generators/b4um/in_place/templates/display_radio.html.erb.tt +23 -0
  18. data/lib/generators/b4um/in_place/templates/display_rich_text.html.erb.tt +123 -0
  19. data/lib/generators/b4um/in_place/templates/display_select.html.erb.tt +23 -0
  20. data/lib/generators/b4um/in_place/templates/display_text.html.erb.tt +18 -0
  21. data/lib/generators/b4um/in_place/templates/edit_attachment.html.erb.tt +83 -0
  22. data/lib/generators/b4um/in_place/templates/edit_attachments.html.erb.tt +65 -0
  23. data/lib/generators/b4um/in_place/templates/edit_boolean.html.erb.tt +32 -0
  24. data/lib/generators/b4um/in_place/templates/edit_number.html.erb.tt +26 -0
  25. data/lib/generators/b4um/in_place/templates/edit_radio.html.erb.tt +32 -0
  26. data/lib/generators/b4um/in_place/templates/edit_rich_text.html.erb.tt +25 -0
  27. data/lib/generators/b4um/in_place/templates/edit_select.html.erb.tt +28 -0
  28. data/lib/generators/b4um/in_place/templates/edit_text.html.erb.tt +25 -0
  29. data/lib/generators/b4um/in_place/templates/in_place.css +935 -0
  30. data/lib/generators/b4um/in_place/templates/in_place_controller.js +115 -0
  31. data/lib/generators/b4um/install/install_generator.rb +18 -13
  32. data/lib/generators/b4um/install/templates/_flash.html.erb +15 -0
  33. data/lib/generators/b4um/install/templates/_footer.html.erb +6 -1
  34. data/lib/generators/b4um/install/templates/b4um/forms.css +127 -0
  35. data/lib/generators/b4um/install/templates/b4um/tables.css +49 -0
  36. data/lib/generators/b4um/install/templates/image_lightbox_controller.js +34 -1
  37. data/lib/generators/b4um/install/templates/rich_text_controller.js +22 -0
  38. data/lib/generators/b4um/install/templates/scroll_to_top_controller.js +12 -0
  39. data/lib/generators/b4um/scaffold/scaffold_generator.rb +106 -0
  40. data/lib/generators/b4um/scaffold/templates/_form.html.erb.tt +29 -0
  41. data/lib/generators/b4um/scaffold/templates/_resource.html.erb.tt +35 -0
  42. data/lib/generators/b4um/scaffold/templates/_table.html.erb.tt +31 -0
  43. data/lib/generators/b4um/table/table_generator.rb +187 -5
  44. data/lib/generators/b4um/table/templates/_table.html.erb.tt +286 -0
  45. data/lib/generators/b4um/trix/trix_generator.rb +104 -6
  46. metadata +27 -3
@@ -14,6 +14,14 @@ module B4um
14
14
  default: [],
15
15
  banner: "field:type field:type"
16
16
 
17
+ class_option :select,
18
+ type: :string,
19
+ desc: "Renders a field as a select value. Example: status:Active=Aktiv,Inactive=Inaktiv"
20
+
21
+ class_option :radio,
22
+ type: :string,
23
+ desc: "Renders a field as a radio value. Example: condition:new=Neu,used=Gebraucht"
24
+
17
25
  class << self
18
26
  def desc(_description = nil)
19
27
  "Generates a B4UM table partial for an existing model."
@@ -27,6 +35,22 @@ module B4um
27
35
  "Model not found: app/models/#{file_name}.rb"
28
36
  end
29
37
 
38
+ def validate_editor_configurations
39
+ configurations = {
40
+ select: select_configuration,
41
+ radio: radio_configuration
42
+ }
43
+
44
+ configurations.each do |option_name, configuration|
45
+ validate_editor_configuration(
46
+ option_name,
47
+ configuration
48
+ )
49
+ end
50
+
51
+ validate_editor_configuration_conflict(configurations)
52
+ end
53
+
30
54
  def create_table_partial
31
55
  template(
32
56
  "_table.html.erb.tt",
@@ -38,19 +62,177 @@ module B4um
38
62
  )
39
63
  end
40
64
 
65
+ def update_index_layout
66
+ return unless File.exist?(index_path)
67
+
68
+ index = File.read(index_path)
69
+
70
+ updated_index = index.sub(
71
+ /render\s+["'](?:bento|table|list|alternating)["']/,
72
+ 'render "table"'
73
+ )
74
+
75
+ return if updated_index == index
76
+
77
+ File.write(index_path, updated_index)
78
+ end
79
+
41
80
  private
42
81
 
43
- def table_attributes
44
- fields.map do |field|
45
- name, type = field.split(":", 2)
82
+ def model_class
83
+ class_name.constantize
84
+ end
85
+
86
+ def database_columns
87
+ model_class.columns.index_by(&:name)
88
+ end
89
+
90
+ def rich_text_fields
91
+ model_class.reflect_on_all_associations(:has_one)
92
+ .filter_map do |association|
93
+ association.name.to_s.delete_prefix("rich_text_") if
94
+ association.name.to_s.start_with?("rich_text_")
95
+ end
96
+ end
97
+
98
+ def single_attachment_fields
99
+ model_class.reflect_on_all_attachments
100
+ .select { |reflection| reflection.macro == :has_one_attached }
101
+ .map { |reflection| reflection.name.to_s }
102
+ end
103
+
104
+ def multiple_attachment_fields
105
+ model_class.reflect_on_all_attachments
106
+ .select { |reflection| reflection.macro == :has_many_attached }
107
+ .map { |reflection| reflection.name.to_s }
108
+ end
109
+
110
+ def available_field_names
111
+ (
112
+ database_field_names +
113
+ rich_text_fields +
114
+ single_attachment_fields +
115
+ multiple_attachment_fields
116
+ ).uniq
117
+ end
118
+
119
+ def database_field_names
120
+ database_columns.keys - %w[id created_at updated_at]
121
+ end
122
+
123
+ def field_type(field)
124
+ return :rich_text if rich_text_fields.include?(field)
125
+ return :attachment if single_attachment_fields.include?(field)
126
+ return :attachments if multiple_attachment_fields.include?(field)
127
+
128
+ database_columns.fetch(field).type
129
+ end
130
+
131
+ def table_field_names
132
+ return available_field_names if fields.empty?
133
+
134
+ fields.map { |field| field.split(":", 2).first }
135
+ end
136
+
137
+ def validate_editor_configuration(option_name, configuration)
138
+ return unless options[option_name].present?
139
+
140
+ unless configuration
141
+ raise Thor::Error,
142
+ "Invalid --#{option_name} configuration. " \
143
+ "Expected FIELD:VALUE[,VALUE...]"
144
+ end
145
+
146
+ return if table_field_names.include?(configuration[:field])
147
+
148
+ raise Thor::Error,
149
+ "--#{option_name} field #{configuration[:field]} " \
150
+ "must be one of the generated fields: #{fields.join(", ")}"
151
+ end
152
+
153
+ def validate_editor_configuration_conflict(configurations)
154
+ select = configurations[:select]
155
+ radio = configurations[:radio]
156
+
157
+ return unless select && radio
158
+ return unless select[:field] == radio[:field]
159
+
160
+ raise Thor::Error,
161
+ "Field #{select[:field]} cannot use both --select and --radio"
162
+ end
46
163
 
164
+ def select_configuration
165
+ field_configuration(:select)
166
+ end
167
+
168
+ def radio_configuration
169
+ field_configuration(:radio)
170
+ end
171
+
172
+ def field_configuration(option_name)
173
+ value = options[option_name]
174
+ return unless value.present?
175
+
176
+ field, values = value.split(":", 2)
177
+
178
+ return unless field.present? && values.present?
179
+
180
+ choices = field_choices(values)
181
+ return if choices.empty?
182
+
183
+ {
184
+ field: field,
185
+ values: choices
186
+ }
187
+ end
188
+
189
+ def field_choices(values)
190
+ values.split(",").filter_map do |choice|
191
+ value, label = choice.strip.split("=", 2)
192
+
193
+ next if value.blank?
194
+
195
+ {
196
+ value: value,
197
+ label: label.presence || value
198
+ }
199
+ end
200
+ end
201
+
202
+ def select_field?(field)
203
+ configuration = select_configuration
204
+
205
+ configuration &&
206
+ configuration[:field] == field &&
207
+ configuration[:values].any?
208
+ end
209
+
210
+ def radio_field?(field)
211
+ configuration = radio_configuration
212
+
213
+ configuration &&
214
+ configuration[:field] == field &&
215
+ configuration[:values].any?
216
+ end
217
+
218
+ def table_attributes
219
+ table_field_names.map do |field|
47
220
  TableAttribute.new(
48
- name,
49
- (type || "string").to_sym
221
+ field,
222
+ field_type(field)
50
223
  )
51
224
  end
52
225
  end
53
226
 
227
+ def index_path
228
+ File.join(
229
+ destination_root,
230
+ "app/views",
231
+ plural_table_name,
232
+ "index.html.erb"
233
+ )
234
+ end
235
+
54
236
  def model_path
55
237
  File.join(
56
238
  destination_root,
@@ -14,12 +14,298 @@
14
14
  <tr>
15
15
  <% table_attributes.each do |attribute| -%>
16
16
  <td>
17
+ <% if select_field?(attribute.name) -%>
17
18
  <% if attribute.name == "status" -%>
19
+ <span class="b4um-badge">
20
+ <%%=
21
+ <%= select_configuration[:values].inspect %>.to_h {
22
+ |choice| [choice[:value], choice[:label]]
23
+ }.fetch(
24
+ <%= file_name %>.<%= attribute.name %>,
25
+ <%= file_name %>.<%= attribute.name %>
26
+ )
27
+ %>
28
+ </span>
29
+ <% else -%>
30
+ <%%=
31
+ <%= select_configuration[:values].inspect %>.to_h {
32
+ |choice| [choice[:value], choice[:label]]
33
+ }.fetch(
34
+ <%= file_name %>.<%= attribute.name %>,
35
+ <%= file_name %>.<%= attribute.name %>
36
+ )
37
+ %>
38
+ <% end -%>
39
+ <% elsif radio_field?(attribute.name) -%>
40
+ <%%=
41
+ <%= radio_configuration[:values].inspect %>.to_h {
42
+ |choice| [choice[:value], choice[:label]]
43
+ }.fetch(
44
+ <%= file_name %>.<%= attribute.name %>,
45
+ <%= file_name %>.<%= attribute.name %>
46
+ )
47
+ %>
48
+ <% elsif attribute.type == :rich_text -%>
49
+ <div
50
+ class="b4um-table__rich-text trix-content"
51
+ data-controller="image-lightbox rich-text"
52
+ data-action="keydown->image-lightbox#keydown"
53
+ >
54
+ <%%= <%= file_name %>.<%= attribute.name %> %>
55
+
56
+ <div
57
+ class="image-lightbox"
58
+ data-image-lightbox-target="overlay"
59
+ data-action="click->image-lightbox#closeOnBackground"
60
+ tabindex="-1"
61
+ role="dialog"
62
+ aria-modal="true"
63
+ aria-label="Image preview"
64
+ hidden
65
+ >
66
+ <div
67
+ class="image-lightbox__counter"
68
+ data-image-lightbox-target="counter"
69
+ ></div>
70
+
71
+ <button
72
+ type="button"
73
+ class="image-lightbox__close"
74
+ aria-label="Close image"
75
+ data-action="click->image-lightbox#close"
76
+ >
77
+ &times;
78
+ </button>
79
+
80
+ <button
81
+ type="button"
82
+ class="image-lightbox__navigation image-lightbox__navigation--previous"
83
+ aria-label="Previous image"
84
+ data-action="click->image-lightbox#previous"
85
+ >
86
+ &#8249;
87
+ </button>
88
+
89
+ <div
90
+ class="image-lightbox__stage"
91
+ data-image-lightbox-target="stage"
92
+ data-action="
93
+ pointerdown->image-lightbox#pointerDown
94
+ pointermove->image-lightbox#pointerMove
95
+ pointerup->image-lightbox#pointerUp
96
+ pointercancel->image-lightbox#pointerCancel
97
+ "
98
+ >
99
+ <img
100
+ class="image-lightbox__image"
101
+ data-image-lightbox-target="image"
102
+ alt=""
103
+ >
104
+ </div>
105
+
106
+ <button
107
+ type="button"
108
+ class="image-lightbox__navigation image-lightbox__navigation--next"
109
+ aria-label="Next image"
110
+ data-action="click->image-lightbox#next"
111
+ >
112
+ &#8250;
113
+ </button>
114
+
115
+ <div
116
+ class="image-lightbox__thumbnails"
117
+ data-image-lightbox-target="thumbnails"
118
+ ></div>
119
+
120
+ <div
121
+ class="image-lightbox__caption"
122
+ data-image-lightbox-target="caption"
123
+ ></div>
124
+ </div>
125
+ </div>
126
+ <% elsif attribute.type == :attachment -%>
127
+ <%% if <%= file_name %>.<%= attribute.name %>.attached? %>
128
+ <div
129
+ class="b4um-table__media"
130
+ data-controller="image-lightbox"
131
+ data-action="keydown->image-lightbox#keydown"
132
+ >
133
+ <button
134
+ type="button"
135
+ class="b4um-table__image-button"
136
+ data-image-lightbox-target="item"
137
+ data-image-lightbox-url="<%%= url_for(<%= file_name %>.<%= attribute.name %>) %>"
138
+ data-image-lightbox-alt="<%%= <%= file_name %>.<%= attribute.name %>.filename.to_s %>"
139
+ data-action="click->image-lightbox#open"
140
+ >
141
+ <%%= image_tag <%= file_name %>.<%= attribute.name %>,
142
+ class: "b4um-table__image",
143
+ alt: <%= file_name %>.<%= attribute.name %>.filename.to_s %>
144
+ </button>
145
+
146
+ <div
147
+ class="image-lightbox"
148
+ data-image-lightbox-target="overlay"
149
+ data-action="click->image-lightbox#closeOnBackground"
150
+ tabindex="-1"
151
+ role="dialog"
152
+ aria-modal="true"
153
+ aria-label="Image preview"
154
+ hidden
155
+ >
156
+ <div
157
+ class="image-lightbox__counter"
158
+ data-image-lightbox-target="counter"
159
+ ></div>
160
+
161
+ <button
162
+ type="button"
163
+ class="image-lightbox__close"
164
+ aria-label="Close image"
165
+ data-action="click->image-lightbox#close"
166
+ >
167
+ &times;
168
+ </button>
169
+
170
+ <div
171
+ class="image-lightbox__stage"
172
+ data-image-lightbox-target="stage"
173
+ data-action="
174
+ pointerdown->image-lightbox#pointerDown
175
+ pointermove->image-lightbox#pointerMove
176
+ pointerup->image-lightbox#pointerUp
177
+ pointercancel->image-lightbox#pointerCancel
178
+ "
179
+ >
180
+ <img
181
+ class="image-lightbox__image"
182
+ data-image-lightbox-target="image"
183
+ alt=""
184
+ >
185
+ </div>
186
+
187
+ <div
188
+ class="image-lightbox__thumbnails"
189
+ data-image-lightbox-target="thumbnails"
190
+ ></div>
191
+
192
+ <div
193
+ class="image-lightbox__caption"
194
+ data-image-lightbox-target="caption"
195
+ ></div>
196
+ </div>
197
+ </div>
198
+ <%% else %>
199
+ <span class="b4um-table__media-empty">No image</span>
200
+ <%% end %>
201
+ <% elsif attribute.type == :attachments -%>
202
+ <%% if <%= file_name %>.<%= attribute.name %>.attached? %>
203
+ <div
204
+ class="b4um-table__media"
205
+ data-controller="image-lightbox"
206
+ data-action="keydown->image-lightbox#keydown"
207
+ >
208
+ <%% <%= file_name %>.<%= attribute.name %>.each do |image| %>
209
+ <button
210
+ type="button"
211
+ class="b4um-table__image-button"
212
+ data-image-lightbox-target="item"
213
+ data-image-lightbox-url="<%%= url_for(image) %>"
214
+ data-image-lightbox-alt="<%%= image.filename.to_s %>"
215
+ data-action="click->image-lightbox#open"
216
+ >
217
+ <%%= image_tag image,
218
+ class: "b4um-table__image",
219
+ alt: image.filename.to_s %>
220
+ </button>
221
+ <%% end %>
222
+
223
+ <div
224
+ class="image-lightbox"
225
+ data-image-lightbox-target="overlay"
226
+ data-action="click->image-lightbox#closeOnBackground"
227
+ tabindex="-1"
228
+ role="dialog"
229
+ aria-modal="true"
230
+ aria-label="Image gallery"
231
+ hidden
232
+ >
233
+ <div
234
+ class="image-lightbox__counter"
235
+ data-image-lightbox-target="counter"
236
+ ></div>
237
+
238
+ <button
239
+ type="button"
240
+ class="image-lightbox__close"
241
+ aria-label="Close image"
242
+ data-action="click->image-lightbox#close"
243
+ >
244
+ &times;
245
+ </button>
246
+
247
+ <button
248
+ type="button"
249
+ class="image-lightbox__navigation image-lightbox__navigation--previous"
250
+ aria-label="Previous image"
251
+ data-action="click->image-lightbox#previous"
252
+ >
253
+ &#8249;
254
+ </button>
255
+
256
+ <div
257
+ class="image-lightbox__stage"
258
+ data-image-lightbox-target="stage"
259
+ data-action="
260
+ pointerdown->image-lightbox#pointerDown
261
+ pointermove->image-lightbox#pointerMove
262
+ pointerup->image-lightbox#pointerUp
263
+ pointercancel->image-lightbox#pointerCancel
264
+ "
265
+ >
266
+ <img
267
+ class="image-lightbox__image"
268
+ data-image-lightbox-target="image"
269
+ alt=""
270
+ >
271
+ </div>
272
+
273
+ <button
274
+ type="button"
275
+ class="image-lightbox__navigation image-lightbox__navigation--next"
276
+ aria-label="Next image"
277
+ data-action="click->image-lightbox#next"
278
+ >
279
+ &#8250;
280
+ </button>
281
+
282
+ <div
283
+ class="image-lightbox__thumbnails"
284
+ data-image-lightbox-target="thumbnails"
285
+ ></div>
286
+
287
+ <div
288
+ class="image-lightbox__caption"
289
+ data-image-lightbox-target="caption"
290
+ ></div>
291
+ </div>
292
+ </div>
293
+ <%% else %>
294
+ <span class="b4um-table__media-empty">No images</span>
295
+ <%% end %>
296
+ <% elsif attribute.name == "status" -%>
18
297
  <span class="b4um-badge">
19
298
  <%%= <%= file_name %>.<%= attribute.name %> %>
20
299
  </span>
21
300
  <% elsif attribute.type == :text -%>
22
301
  <%%= truncate(<%= file_name %>.<%= attribute.name %>, length: 100) %>
302
+ <% elsif attribute.type == :decimal -%>
303
+ <%%= number_with_precision(
304
+ <%= file_name %>.<%= attribute.name %>,
305
+ precision: 2,
306
+ delimiter: ".",
307
+ separator: ","
308
+ ) %>
23
309
  <% else -%>
24
310
  <%%= <%= file_name %>.<%= attribute.name %> %>
25
311
  <% end -%>
@@ -133,16 +133,75 @@ module B4um
133
133
  end
134
134
 
135
135
  def update_form
136
- gsub_file(
136
+ form = File.read(form_path)
137
+
138
+ return if form.include?("form.rich_text_area :#{attribute}")
139
+
140
+ if form.include?("form.text_area :#{attribute}")
141
+ gsub_file(
142
+ form_path,
143
+ "form.text_area :#{attribute}",
144
+ "form.rich_text_area :#{attribute}"
145
+ )
146
+
147
+ gsub_file(
148
+ form_path,
149
+ /<div class="form-field form-field--textarea">(?=\s*<%= form\.rich_text_area :#{Regexp.escape(attribute)})/,
150
+ '<div class="form-field form-field--rich-text">'
151
+ )
152
+
153
+ return
154
+ end
155
+
156
+ insert_into_file(
137
157
  form_path,
138
- "form.text_area :#{attribute}",
139
- "form.rich_text_area :#{attribute}"
158
+ <<~ERB,
159
+ <div class="form-field form-field--rich-text">
160
+ <%= form.rich_text_area :#{attribute} %>
161
+ <%= form.label :#{attribute}, class: "form-label" %>
162
+ </div>
163
+
164
+ ERB
165
+ before: ' <div class="form-actions">'
140
166
  )
167
+ end
168
+
169
+ def update_controller_params
170
+ return unless File.exist?(controller_path)
171
+
172
+ controller = File.read(controller_path)
173
+
174
+ params_method = controller[
175
+ /def #{Regexp.escape(name.underscore)}_params.*?^\s*end/m,
176
+ 0
177
+ ]
178
+
179
+ unless params_method
180
+ raise Thor::Error,
181
+ "Could not find #{name.underscore}_params in #{controller_path}"
182
+ end
183
+
184
+ parameter = ":#{attribute}"
185
+
186
+ return if params_method.include?(parameter)
187
+
188
+ updated_method =
189
+ if params_method.match?(/\w+:\s*\[\]/)
190
+ params_method.sub(
191
+ /(?=\w+:\s*\[\])/,
192
+ "#{parameter}, "
193
+ )
194
+ else
195
+ params_method.sub(
196
+ /\s*\]\s*\)\s*$/,
197
+ ", #{parameter} ])"
198
+ )
199
+ end
141
200
 
142
201
  gsub_file(
143
- form_path,
144
- /<div class="form-field form-field--textarea">(?=\s*<%= form\.rich_text_area :#{Regexp.escape(attribute)})/,
145
- '<div class="form-field form-field--rich-text">'
202
+ controller_path,
203
+ params_method,
204
+ updated_method
146
205
  )
147
206
  end
148
207
 
@@ -151,6 +210,8 @@ module B4um
151
210
 
152
211
  resource_name = name.underscore
153
212
 
213
+ add_rich_text_resource_field(resource_name)
214
+
154
215
  preview_length =
155
216
  'local_assigns\.fetch\(:preview_length,\s*160\)'
156
217
 
@@ -374,6 +435,36 @@ module B4um
374
435
 
375
436
  private
376
437
 
438
+ def add_rich_text_resource_field(resource_name)
439
+ partial = File.read(resource_partial_path)
440
+
441
+ return if partial.include?("#{resource_name}.#{attribute}")
442
+
443
+ field = <<~ERB
444
+ <div class="resource-field">
445
+ <strong class="resource-label">#{attribute.humanize}:</strong>
446
+ <span class="resource-value">
447
+ <% if local_assigns[:compact] %>
448
+ <%= b4um_rich_text_preview(
449
+ #{resource_name}.#{attribute},
450
+ length: local_assigns.fetch(:preview_length, 160)
451
+ ) %>
452
+ <% else %>
453
+ <%= #{resource_name}.#{attribute} %>
454
+ <% end %>
455
+ </span>
456
+ </div>
457
+ ERB
458
+
459
+ closing_div = partial.rindex("</div>")
460
+
461
+ return unless closing_div
462
+
463
+ partial.insert(closing_div, field)
464
+
465
+ File.write(resource_partial_path, partial)
466
+ end
467
+
377
468
  def action_text_installed?
378
469
  Dir.glob(
379
470
  File.join(
@@ -401,6 +492,13 @@ module B4um
401
492
  )
402
493
  end
403
494
 
495
+ def controller_path
496
+ File.join(
497
+ destination_root,
498
+ "app/controllers/#{name.underscore.pluralize}_controller.rb"
499
+ )
500
+ end
501
+
404
502
  def resource_partial_path
405
503
  File.join(
406
504
  destination_root,