b4um_generators 0.2.11 → 0.2.14
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/.rubocop.yml +2 -2
- data/README.de.md +127 -1
- data/README.md +126 -1
- data/lib/b4um_generators/version.rb +1 -1
- data/lib/generators/b4um/attachment/attachment_generator.rb +83 -23
- data/lib/generators/b4um/authentication/authentication_generator.rb +63 -0
- data/lib/generators/b4um/help/help_generator.rb +100 -3
- data/lib/generators/b4um/in_place/in_place_generator.rb +1231 -0
- data/lib/generators/b4um/in_place/templates/_flash.html.erb +15 -0
- data/lib/generators/b4um/in_place/templates/b4um_in_place_helper.rb +7 -0
- data/lib/generators/b4um/in_place/templates/display_attachment.html.erb.tt +154 -0
- data/lib/generators/b4um/in_place/templates/display_attachments.html.erb.tt +165 -0
- data/lib/generators/b4um/in_place/templates/display_boolean.html.erb.tt +18 -0
- data/lib/generators/b4um/in_place/templates/display_number.html.erb.tt +18 -0
- data/lib/generators/b4um/in_place/templates/display_radio.html.erb.tt +23 -0
- data/lib/generators/b4um/in_place/templates/display_rich_text.html.erb.tt +123 -0
- data/lib/generators/b4um/in_place/templates/display_select.html.erb.tt +23 -0
- data/lib/generators/b4um/in_place/templates/display_text.html.erb.tt +18 -0
- data/lib/generators/b4um/in_place/templates/edit_attachment.html.erb.tt +83 -0
- data/lib/generators/b4um/in_place/templates/edit_attachments.html.erb.tt +65 -0
- data/lib/generators/b4um/in_place/templates/edit_boolean.html.erb.tt +32 -0
- data/lib/generators/b4um/in_place/templates/edit_number.html.erb.tt +26 -0
- data/lib/generators/b4um/in_place/templates/edit_radio.html.erb.tt +32 -0
- data/lib/generators/b4um/in_place/templates/edit_rich_text.html.erb.tt +25 -0
- data/lib/generators/b4um/in_place/templates/edit_select.html.erb.tt +28 -0
- data/lib/generators/b4um/in_place/templates/edit_text.html.erb.tt +25 -0
- data/lib/generators/b4um/in_place/templates/in_place.css +935 -0
- data/lib/generators/b4um/in_place/templates/in_place_controller.js +115 -0
- data/lib/generators/b4um/install/install_generator.rb +18 -13
- data/lib/generators/b4um/install/templates/_flash.html.erb +15 -0
- data/lib/generators/b4um/install/templates/_footer.html.erb +6 -1
- data/lib/generators/b4um/install/templates/b4um/forms.css +127 -0
- data/lib/generators/b4um/install/templates/image_lightbox_controller.js +29 -0
- data/lib/generators/b4um/install/templates/rich_text_controller.js +22 -0
- data/lib/generators/b4um/install/templates/scroll_to_top_controller.js +12 -0
- data/lib/generators/b4um/scaffold/scaffold_generator.rb +106 -0
- data/lib/generators/b4um/scaffold/templates/_form.html.erb.tt +29 -0
- data/lib/generators/b4um/scaffold/templates/_resource.html.erb.tt +35 -0
- data/lib/generators/b4um/scaffold/templates/_table.html.erb.tt +31 -0
- data/lib/generators/b4um/trix/trix_generator.rb +104 -6
- metadata +25 -1
|
@@ -0,0 +1,1231 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "rails/generators/named_base"
|
|
5
|
+
|
|
6
|
+
module B4um
|
|
7
|
+
module Generators
|
|
8
|
+
class InPlaceGenerator < Rails::Generators::NamedBase
|
|
9
|
+
source_root File.expand_path("templates", __dir__)
|
|
10
|
+
|
|
11
|
+
argument :fields,
|
|
12
|
+
type: :array,
|
|
13
|
+
required: true,
|
|
14
|
+
banner: "FIELD [FIELD ...]"
|
|
15
|
+
|
|
16
|
+
class_option :select,
|
|
17
|
+
type: :string,
|
|
18
|
+
desc: "Renders a field as a select list. Example: status:Active=Aktiv,Inactive=Inaktiv"
|
|
19
|
+
|
|
20
|
+
class_option :radio,
|
|
21
|
+
type: :string,
|
|
22
|
+
desc: "Renders a field as radio buttons. Example: condition:new=Neu,used=Gebraucht"
|
|
23
|
+
|
|
24
|
+
class << self
|
|
25
|
+
def desc(_description = nil)
|
|
26
|
+
"Adds B4UM in-place editing to an existing resource."
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def validate_model
|
|
31
|
+
return if File.exist?(model_path)
|
|
32
|
+
|
|
33
|
+
raise Thor::Error,
|
|
34
|
+
"Model not found: app/models/#{file_name}.rb"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def validate_fields
|
|
38
|
+
fields.each do |field|
|
|
39
|
+
next if available_field_names.include?(field)
|
|
40
|
+
|
|
41
|
+
raise Thor::Error,
|
|
42
|
+
"Field #{field} was not found on #{class_name}."
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def validate_editor_options
|
|
47
|
+
configurations = {
|
|
48
|
+
select: select_configuration,
|
|
49
|
+
radio: radio_configuration
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
configurations.each do |option_name, configuration|
|
|
53
|
+
validate_editor_configuration(option_name, configuration)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
validate_editor_configuration_conflict(configurations)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def ensure_flash_support
|
|
60
|
+
flash_path = "app/views/shared/_flash.html.erb"
|
|
61
|
+
full_flash_path = File.join(destination_root, flash_path)
|
|
62
|
+
|
|
63
|
+
unless File.exist?(full_flash_path)
|
|
64
|
+
copy_file(
|
|
65
|
+
"_flash.html.erb",
|
|
66
|
+
flash_path
|
|
67
|
+
)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
ensure_flash_render_in_layout
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def create_in_place_helper
|
|
74
|
+
helper_path = "app/helpers/b4um_in_place_helper.rb"
|
|
75
|
+
full_helper_path = File.join(destination_root, helper_path)
|
|
76
|
+
|
|
77
|
+
unless File.exist?(full_helper_path)
|
|
78
|
+
template(
|
|
79
|
+
"b4um_in_place_helper.rb",
|
|
80
|
+
helper_path
|
|
81
|
+
)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
return unless authentication_installed?
|
|
85
|
+
|
|
86
|
+
helper = File.read(full_helper_path)
|
|
87
|
+
|
|
88
|
+
return if helper.include?(
|
|
89
|
+
"def b4um_in_place_editing_allowed?\n logged_in?"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
gsub_file(
|
|
93
|
+
helper_path,
|
|
94
|
+
"def b4um_in_place_editing_allowed?\n true",
|
|
95
|
+
"def b4um_in_place_editing_allowed?\n logged_in?"
|
|
96
|
+
)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def create_in_place_controller
|
|
100
|
+
copy_file(
|
|
101
|
+
"in_place_controller.js",
|
|
102
|
+
"app/javascript/controllers/in_place_controller.js"
|
|
103
|
+
)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def create_in_place_styles
|
|
107
|
+
copy_file(
|
|
108
|
+
"in_place.css",
|
|
109
|
+
"app/assets/stylesheets/b4um/in_place.css"
|
|
110
|
+
)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def add_in_place_routes
|
|
114
|
+
fields.each do |field|
|
|
115
|
+
add_in_place_route(field)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def add_in_place_fields_constant
|
|
120
|
+
controller_path = File.join(
|
|
121
|
+
"app/controllers",
|
|
122
|
+
"#{plural_table_name}_controller.rb"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
full_controller_path = File.join(
|
|
126
|
+
destination_root,
|
|
127
|
+
controller_path
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
controller = File.read(full_controller_path)
|
|
131
|
+
|
|
132
|
+
if controller.include?("IN_PLACE_FIELDS = %w[")
|
|
133
|
+
add_fields_to_existing_constant(controller_path, controller)
|
|
134
|
+
else
|
|
135
|
+
create_in_place_fields_constant(controller_path)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def add_edit_actions
|
|
140
|
+
fields.each do |field|
|
|
141
|
+
add_edit_action(field)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def add_edit_actions_to_set_product
|
|
146
|
+
fields.each do |field|
|
|
147
|
+
add_edit_action_to_set_product(field)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def create_edit_views
|
|
152
|
+
fields.each do |field|
|
|
153
|
+
create_edit_view(field)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def create_display_partials
|
|
158
|
+
fields.each do |field|
|
|
159
|
+
create_display_partial(field)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def integrate_display_partials
|
|
164
|
+
fields.each do |field|
|
|
165
|
+
integrate_display_partial(field)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def add_in_place_params
|
|
170
|
+
fields.each do |field|
|
|
171
|
+
add_in_place_params_for(field)
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def add_update_integration
|
|
176
|
+
add_in_place_update_integration
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def report_fields
|
|
180
|
+
fields.each do |field|
|
|
181
|
+
say_status(
|
|
182
|
+
:in_place,
|
|
183
|
+
"#{class_name}##{field} (#{field_type(field)})",
|
|
184
|
+
:green
|
|
185
|
+
)
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def protect_in_place_actions
|
|
190
|
+
return unless authentication_installed?
|
|
191
|
+
|
|
192
|
+
controller_path = File.join(
|
|
193
|
+
"app/controllers",
|
|
194
|
+
"#{plural_table_name}_controller.rb"
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
full_controller_path = File.join(
|
|
198
|
+
destination_root,
|
|
199
|
+
controller_path
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
controller = File.read(full_controller_path)
|
|
203
|
+
|
|
204
|
+
callback =
|
|
205
|
+
"before_action :require_login, except: [:index, :show]"
|
|
206
|
+
|
|
207
|
+
return if controller.include?(callback)
|
|
208
|
+
|
|
209
|
+
inject_into_file(
|
|
210
|
+
controller_path,
|
|
211
|
+
after: /^class .*Controller < ApplicationController\s*$/
|
|
212
|
+
) do
|
|
213
|
+
"\n #{callback}"
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
private
|
|
218
|
+
|
|
219
|
+
def ensure_flash_render_in_layout
|
|
220
|
+
layout_path = "app/views/layouts/application.html.erb"
|
|
221
|
+
full_layout_path = File.join(destination_root, layout_path)
|
|
222
|
+
|
|
223
|
+
return unless File.exist?(full_layout_path)
|
|
224
|
+
|
|
225
|
+
layout = File.read(full_layout_path)
|
|
226
|
+
|
|
227
|
+
return if layout.include?('render "shared/flash"')
|
|
228
|
+
|
|
229
|
+
flash_pattern = /
|
|
230
|
+
\s*<%\s+flash\.each\s+do\s+\|type,\s*message\|\s*%>
|
|
231
|
+
.*?
|
|
232
|
+
<%\s+end\s+%>
|
|
233
|
+
/mx
|
|
234
|
+
|
|
235
|
+
return unless layout.match?(flash_pattern)
|
|
236
|
+
|
|
237
|
+
gsub_file(
|
|
238
|
+
layout_path,
|
|
239
|
+
flash_pattern,
|
|
240
|
+
"\n <%= render \"shared/flash\" %>"
|
|
241
|
+
)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def validate_editor_configuration(option_name, configuration)
|
|
245
|
+
return unless options[option_name].present?
|
|
246
|
+
|
|
247
|
+
unless configuration
|
|
248
|
+
raise Thor::Error,
|
|
249
|
+
"Invalid --#{option_name} configuration. " \
|
|
250
|
+
"Expected FIELD:VALUE[,VALUE...]"
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
return if fields.include?(configuration[:field])
|
|
254
|
+
|
|
255
|
+
raise Thor::Error,
|
|
256
|
+
"--#{option_name} field #{configuration[:field]} " \
|
|
257
|
+
"must be one of the generated fields: #{fields.join(", ")}"
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def validate_editor_configuration_conflict(configurations)
|
|
261
|
+
select = configurations[:select]
|
|
262
|
+
radio = configurations[:radio]
|
|
263
|
+
|
|
264
|
+
return unless select && radio
|
|
265
|
+
return unless select[:field] == radio[:field]
|
|
266
|
+
|
|
267
|
+
raise Thor::Error,
|
|
268
|
+
"Field #{select[:field]} cannot use both --select and --radio"
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def model_path
|
|
272
|
+
File.join(
|
|
273
|
+
destination_root,
|
|
274
|
+
"app/models",
|
|
275
|
+
"#{file_name}.rb"
|
|
276
|
+
)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def model_class
|
|
280
|
+
class_name.constantize
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def database_columns
|
|
284
|
+
model_class.columns.index_by(&:name)
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def rich_text_fields
|
|
288
|
+
model_class.reflect_on_all_associations(:has_one)
|
|
289
|
+
.filter_map do |association|
|
|
290
|
+
association.name.to_s.delete_prefix("rich_text_") if
|
|
291
|
+
association.name.to_s.start_with?("rich_text_")
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def single_attachment_fields
|
|
296
|
+
model_class.reflect_on_all_attachments
|
|
297
|
+
.select { |reflection| reflection.macro == :has_one_attached }
|
|
298
|
+
.map { |reflection| reflection.name.to_s }
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def multiple_attachment_fields
|
|
302
|
+
model_class.reflect_on_all_attachments
|
|
303
|
+
.select { |reflection| reflection.macro == :has_many_attached }
|
|
304
|
+
.map { |reflection| reflection.name.to_s }
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def available_field_names
|
|
308
|
+
(
|
|
309
|
+
database_columns.keys +
|
|
310
|
+
rich_text_fields +
|
|
311
|
+
single_attachment_fields +
|
|
312
|
+
multiple_attachment_fields
|
|
313
|
+
).uniq
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def field_type(field)
|
|
317
|
+
return :rich_text if rich_text_fields.include?(field)
|
|
318
|
+
return :attachment if single_attachment_fields.include?(field)
|
|
319
|
+
return :attachments if multiple_attachment_fields.include?(field)
|
|
320
|
+
|
|
321
|
+
database_columns.fetch(field).type
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def select_configuration
|
|
325
|
+
editor_configuration(:select)
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def radio_configuration
|
|
329
|
+
editor_configuration(:radio)
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def editor_configuration(option_name)
|
|
333
|
+
value = options[option_name]
|
|
334
|
+
return unless value.present?
|
|
335
|
+
|
|
336
|
+
field, values = value.split(":", 2)
|
|
337
|
+
|
|
338
|
+
return unless field.present? && values.present?
|
|
339
|
+
|
|
340
|
+
choices = editor_choices(values)
|
|
341
|
+
return if choices.empty?
|
|
342
|
+
|
|
343
|
+
{
|
|
344
|
+
field: field,
|
|
345
|
+
values: choices
|
|
346
|
+
}
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def editor_choices(values)
|
|
350
|
+
values.split(",").filter_map do |choice|
|
|
351
|
+
value, label = choice.strip.split("=", 2)
|
|
352
|
+
|
|
353
|
+
next if value.blank?
|
|
354
|
+
|
|
355
|
+
{
|
|
356
|
+
value: value,
|
|
357
|
+
label: label.presence || value
|
|
358
|
+
}
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def select_field?(field)
|
|
363
|
+
configuration = select_configuration
|
|
364
|
+
|
|
365
|
+
configuration &&
|
|
366
|
+
configuration[:field] == field &&
|
|
367
|
+
configuration[:values].any?
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
def radio_field?(field)
|
|
371
|
+
configuration = radio_configuration
|
|
372
|
+
|
|
373
|
+
configuration &&
|
|
374
|
+
configuration[:field] == field &&
|
|
375
|
+
configuration[:values].any?
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
def editor_type(field)
|
|
379
|
+
configured_editor_type(field) || default_editor_type(field)
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
def configured_editor_type(field)
|
|
383
|
+
return :radio if radio_field?(field)
|
|
384
|
+
return :select if select_field?(field)
|
|
385
|
+
|
|
386
|
+
nil
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def default_editor_type(field)
|
|
390
|
+
{
|
|
391
|
+
rich_text: :rich_text,
|
|
392
|
+
attachment: :attachment,
|
|
393
|
+
attachments: :attachments,
|
|
394
|
+
boolean: :boolean,
|
|
395
|
+
integer: :number,
|
|
396
|
+
float: :number,
|
|
397
|
+
decimal: :number
|
|
398
|
+
}.fetch(field_type(field), :text)
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
def authentication_installed?
|
|
402
|
+
application_controller = File.join(
|
|
403
|
+
destination_root,
|
|
404
|
+
"app/controllers/application_controller.rb"
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
return false unless File.exist?(application_controller)
|
|
408
|
+
|
|
409
|
+
File.read(application_controller).include?(
|
|
410
|
+
"helper_method :current_"
|
|
411
|
+
) &&
|
|
412
|
+
File.read(application_controller).include?(
|
|
413
|
+
":logged_in?"
|
|
414
|
+
)
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
def add_in_place_route(field)
|
|
418
|
+
routes_path = "config/routes.rb"
|
|
419
|
+
full_routes_path = File.join(destination_root, routes_path)
|
|
420
|
+
routes = File.read(full_routes_path)
|
|
421
|
+
route = "get :edit_#{field}"
|
|
422
|
+
|
|
423
|
+
return if route_exists_for_resource?(routes, route)
|
|
424
|
+
|
|
425
|
+
add_route_to_resources(routes_path, routes, route)
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
def route_exists_for_resource?(routes, route)
|
|
429
|
+
member_match = routes.match(resources_with_member_pattern)
|
|
430
|
+
|
|
431
|
+
if member_match
|
|
432
|
+
start_index = member_match.begin(0)
|
|
433
|
+
indentation = member_match[1]
|
|
434
|
+
|
|
435
|
+
resource_block = routes[
|
|
436
|
+
start_index..
|
|
437
|
+
][
|
|
438
|
+
/\A.*?^#{Regexp.escape(indentation)}end\s*$/m,
|
|
439
|
+
0
|
|
440
|
+
]
|
|
441
|
+
|
|
442
|
+
return resource_block&.include?(route)
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
false
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
def add_route_to_resources(routes_path, routes, route)
|
|
449
|
+
if routes.match?(resources_with_member_pattern)
|
|
450
|
+
add_route_to_member_block(routes_path, route)
|
|
451
|
+
elsif routes.match?(resources_block_pattern)
|
|
452
|
+
add_member_block(routes_path, route)
|
|
453
|
+
elsif routes.match?(plain_resources_pattern)
|
|
454
|
+
convert_plain_resources(routes_path, route)
|
|
455
|
+
else
|
|
456
|
+
raise Thor::Error,
|
|
457
|
+
"Could not find resources :#{plural_table_name} in config/routes.rb"
|
|
458
|
+
end
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
def resources_with_member_pattern
|
|
462
|
+
/
|
|
463
|
+
^(\s*)resources\ :#{Regexp.escape(plural_table_name)}\ do\n
|
|
464
|
+
\1\ \ member\ do\n
|
|
465
|
+
/x
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def resources_block_pattern
|
|
469
|
+
/^(\s*)resources :#{Regexp.escape(plural_table_name)} do\n/
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
def plain_resources_pattern
|
|
473
|
+
/^(\s*)resources :#{Regexp.escape(plural_table_name)}\s*$/
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
def add_route_to_member_block(routes_path, route)
|
|
477
|
+
gsub_file(
|
|
478
|
+
routes_path,
|
|
479
|
+
resources_with_member_pattern
|
|
480
|
+
) do |match|
|
|
481
|
+
indentation = match[/\A\s*/]
|
|
482
|
+
|
|
483
|
+
"#{match}#{indentation} #{route}\n"
|
|
484
|
+
end
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
def add_member_block(routes_path, route)
|
|
488
|
+
gsub_file(
|
|
489
|
+
routes_path,
|
|
490
|
+
resources_block_pattern
|
|
491
|
+
) do |match|
|
|
492
|
+
indentation = match[/\A\s*/]
|
|
493
|
+
|
|
494
|
+
[
|
|
495
|
+
match.chomp,
|
|
496
|
+
"#{indentation} member do",
|
|
497
|
+
"#{indentation} #{route}",
|
|
498
|
+
"#{indentation} end",
|
|
499
|
+
""
|
|
500
|
+
].join("\n")
|
|
501
|
+
end
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
def convert_plain_resources(routes_path, route)
|
|
505
|
+
gsub_file(
|
|
506
|
+
routes_path,
|
|
507
|
+
plain_resources_pattern
|
|
508
|
+
) do |match|
|
|
509
|
+
indentation = match[/\A\s*/]
|
|
510
|
+
|
|
511
|
+
[
|
|
512
|
+
"#{indentation}resources :#{plural_table_name} do",
|
|
513
|
+
"#{indentation} member do",
|
|
514
|
+
"#{indentation} #{route}",
|
|
515
|
+
"#{indentation} end",
|
|
516
|
+
"#{indentation}end"
|
|
517
|
+
].join("\n")
|
|
518
|
+
end
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
def create_in_place_fields_constant(controller_path)
|
|
522
|
+
lines = fields.map do |field|
|
|
523
|
+
" #{field}"
|
|
524
|
+
end.join("\n")
|
|
525
|
+
|
|
526
|
+
inject_into_file(
|
|
527
|
+
controller_path,
|
|
528
|
+
after: /^class .*Controller < ApplicationController\s*$/
|
|
529
|
+
) do
|
|
530
|
+
<<~RUBY
|
|
531
|
+
|
|
532
|
+
IN_PLACE_FIELDS = %w[
|
|
533
|
+
#{lines}
|
|
534
|
+
].freeze
|
|
535
|
+
RUBY
|
|
536
|
+
end
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
def add_fields_to_existing_constant(controller_path, controller)
|
|
540
|
+
fields.each do |field|
|
|
541
|
+
next if in_place_field_defined?(controller, field)
|
|
542
|
+
|
|
543
|
+
inject_into_file(
|
|
544
|
+
controller_path,
|
|
545
|
+
before: /^\s*\]\.freeze\s*$/
|
|
546
|
+
) do
|
|
547
|
+
" #{field}\n"
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
controller = File.read(
|
|
551
|
+
File.join(destination_root, controller_path)
|
|
552
|
+
)
|
|
553
|
+
end
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
def in_place_field_defined?(controller, field)
|
|
557
|
+
constant = controller[
|
|
558
|
+
/IN_PLACE_FIELDS = %w\[(.*?)\]\.freeze/m,
|
|
559
|
+
1
|
|
560
|
+
]
|
|
561
|
+
|
|
562
|
+
return false unless constant
|
|
563
|
+
|
|
564
|
+
constant.split.include?(field)
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
def add_edit_action(field)
|
|
568
|
+
controller_path = File.join(
|
|
569
|
+
"app/controllers",
|
|
570
|
+
"#{plural_table_name}_controller.rb"
|
|
571
|
+
)
|
|
572
|
+
|
|
573
|
+
full_controller_path = File.join(
|
|
574
|
+
destination_root,
|
|
575
|
+
controller_path
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
controller = File.read(full_controller_path)
|
|
579
|
+
|
|
580
|
+
return if controller.match?(
|
|
581
|
+
/^\s*def edit_#{Regexp.escape(field)}\s*$/
|
|
582
|
+
)
|
|
583
|
+
|
|
584
|
+
inject_into_file(
|
|
585
|
+
controller_path,
|
|
586
|
+
before: /^\s*def create\s*$/
|
|
587
|
+
) do
|
|
588
|
+
" def edit_#{field}\n end\n\n"
|
|
589
|
+
end
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
def add_edit_action_to_set_product(field)
|
|
593
|
+
controller_path = File.join(
|
|
594
|
+
"app/controllers",
|
|
595
|
+
"#{plural_table_name}_controller.rb"
|
|
596
|
+
)
|
|
597
|
+
|
|
598
|
+
full_controller_path = File.join(
|
|
599
|
+
destination_root,
|
|
600
|
+
controller_path
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
controller = File.read(full_controller_path)
|
|
604
|
+
action = "edit_#{field}"
|
|
605
|
+
|
|
606
|
+
callback = controller[
|
|
607
|
+
/before_action :set_\w+, only: %i\[(.*?)\]/m,
|
|
608
|
+
0
|
|
609
|
+
]
|
|
610
|
+
|
|
611
|
+
unless callback
|
|
612
|
+
raise Thor::Error,
|
|
613
|
+
"Could not find the set_#{singular_table_name} before_action in #{controller_path}"
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
return if callback.match?(/\b#{Regexp.escape(action)}\b/)
|
|
617
|
+
|
|
618
|
+
unless callback.match?(/\bupdate\b/)
|
|
619
|
+
raise Thor::Error,
|
|
620
|
+
"Could not find update in the set_#{singular_table_name} before_action"
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
updated_callback = callback.sub(
|
|
624
|
+
/\bupdate\b/,
|
|
625
|
+
"#{action} update"
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
gsub_file(
|
|
629
|
+
controller_path,
|
|
630
|
+
callback,
|
|
631
|
+
updated_callback
|
|
632
|
+
)
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
def add_in_place_update_integration
|
|
636
|
+
controller_path = File.join(
|
|
637
|
+
"app/controllers",
|
|
638
|
+
"#{plural_table_name}_controller.rb"
|
|
639
|
+
)
|
|
640
|
+
|
|
641
|
+
full_controller_path = File.join(
|
|
642
|
+
destination_root,
|
|
643
|
+
controller_path
|
|
644
|
+
)
|
|
645
|
+
|
|
646
|
+
controller = File.read(full_controller_path)
|
|
647
|
+
|
|
648
|
+
original_update_method = controller[
|
|
649
|
+
/^\s*def update\s*$.*?(?=^\s*def \w+)/m,
|
|
650
|
+
0
|
|
651
|
+
]
|
|
652
|
+
|
|
653
|
+
unless original_update_method
|
|
654
|
+
raise Thor::Error,
|
|
655
|
+
"Could not find update action in #{controller_path}"
|
|
656
|
+
end
|
|
657
|
+
|
|
658
|
+
update_method = original_update_method
|
|
659
|
+
attachment_blocks = attachment_update_blocks_for_fields
|
|
660
|
+
|
|
661
|
+
update_method = integrate_attachment_updates(
|
|
662
|
+
update_method,
|
|
663
|
+
attachment_blocks
|
|
664
|
+
)
|
|
665
|
+
|
|
666
|
+
update_method = integrate_attachment_reloads(update_method)
|
|
667
|
+
update_method = integrate_turbo_streams(update_method, controller_path)
|
|
668
|
+
|
|
669
|
+
return if update_method == original_update_method
|
|
670
|
+
|
|
671
|
+
gsub_file(
|
|
672
|
+
controller_path,
|
|
673
|
+
original_update_method,
|
|
674
|
+
update_method
|
|
675
|
+
)
|
|
676
|
+
end
|
|
677
|
+
|
|
678
|
+
def integrate_attachment_updates(update_method, attachment_blocks)
|
|
679
|
+
update_method = integrate_before_attachment_updates(
|
|
680
|
+
update_method,
|
|
681
|
+
attachment_blocks[:before_update]
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
integrate_after_attachment_updates(
|
|
685
|
+
update_method,
|
|
686
|
+
attachment_blocks[:after_update]
|
|
687
|
+
)
|
|
688
|
+
end
|
|
689
|
+
|
|
690
|
+
def integrate_before_attachment_updates(update_method, lines)
|
|
691
|
+
lines = lines.reject { |line| update_method.include?(line) }
|
|
692
|
+
|
|
693
|
+
return update_method if lines.empty?
|
|
694
|
+
|
|
695
|
+
original_update_call =
|
|
696
|
+
"if @#{singular_table_name}.update(#{singular_table_name}_params)"
|
|
697
|
+
|
|
698
|
+
in_place_update_call =
|
|
699
|
+
"if @#{singular_table_name}.update(update_params)"
|
|
700
|
+
|
|
701
|
+
update_call =
|
|
702
|
+
if update_method.include?(in_place_update_call)
|
|
703
|
+
in_place_update_call
|
|
704
|
+
elsif update_method.include?(original_update_call)
|
|
705
|
+
original_update_call
|
|
706
|
+
end
|
|
707
|
+
|
|
708
|
+
unless update_call
|
|
709
|
+
raise Thor::Error,
|
|
710
|
+
"Could not find update call in update action"
|
|
711
|
+
end
|
|
712
|
+
|
|
713
|
+
before_update_block = lines.join("\n").indent(4)
|
|
714
|
+
|
|
715
|
+
replacement = <<~RUBY.chomp
|
|
716
|
+
#{before_update_block}
|
|
717
|
+
if @#{singular_table_name}.update(update_params)
|
|
718
|
+
RUBY
|
|
719
|
+
|
|
720
|
+
update_method.sub(update_call, replacement)
|
|
721
|
+
end
|
|
722
|
+
|
|
723
|
+
def integrate_after_attachment_updates(update_method, blocks)
|
|
724
|
+
blocks = blocks.reject { |block| update_method.include?(block) }
|
|
725
|
+
|
|
726
|
+
return update_method if blocks.empty?
|
|
727
|
+
|
|
728
|
+
success_line = update_method[
|
|
729
|
+
/^\s*if @#{Regexp.escape(singular_table_name)}\.update\(update_params\)\s*$/,
|
|
730
|
+
0
|
|
731
|
+
]
|
|
732
|
+
|
|
733
|
+
unless success_line
|
|
734
|
+
raise Thor::Error,
|
|
735
|
+
"Could not find attachment update success line"
|
|
736
|
+
end
|
|
737
|
+
|
|
738
|
+
after_update_block = blocks.join("\n\n").indent(8)
|
|
739
|
+
|
|
740
|
+
update_method.sub(
|
|
741
|
+
success_line,
|
|
742
|
+
"#{success_line}\n#{after_update_block}"
|
|
743
|
+
)
|
|
744
|
+
end
|
|
745
|
+
|
|
746
|
+
def integrate_attachment_reloads(update_method)
|
|
747
|
+
attachment_reload_block_for_fields.lines.each do |reload_line|
|
|
748
|
+
reload_line = reload_line.strip
|
|
749
|
+
|
|
750
|
+
next if reload_line.empty?
|
|
751
|
+
next if update_method.include?(reload_line)
|
|
752
|
+
|
|
753
|
+
turbo_success_anchor =
|
|
754
|
+
update_method[
|
|
755
|
+
/^\s*format\.turbo_stream do\s*$.*?flash\.now\[:notice\]/m,
|
|
756
|
+
0
|
|
757
|
+
]
|
|
758
|
+
|
|
759
|
+
next unless turbo_success_anchor
|
|
760
|
+
|
|
761
|
+
first_line = turbo_success_anchor.lines.first
|
|
762
|
+
|
|
763
|
+
update_method = update_method.sub(
|
|
764
|
+
first_line,
|
|
765
|
+
"#{first_line} #{reload_line}\n\n"
|
|
766
|
+
)
|
|
767
|
+
end
|
|
768
|
+
|
|
769
|
+
update_method
|
|
770
|
+
end
|
|
771
|
+
|
|
772
|
+
def upgrade_flash_turbo_stream(update_method)
|
|
773
|
+
update_method.sub(
|
|
774
|
+
/turbo_stream\.update\(\s*"flash-messages",/,
|
|
775
|
+
'turbo_stream.replace("flash-messages",'
|
|
776
|
+
)
|
|
777
|
+
end
|
|
778
|
+
|
|
779
|
+
def integrate_turbo_streams(update_method, controller_path)
|
|
780
|
+
update_method = upgrade_flash_turbo_stream(update_method)
|
|
781
|
+
|
|
782
|
+
return update_method if update_method.include?(
|
|
783
|
+
"IN_PLACE_FIELDS.include?(params[:in_place_field])"
|
|
784
|
+
)
|
|
785
|
+
|
|
786
|
+
success_anchor = update_method[
|
|
787
|
+
/^\s*format\.html \{ redirect_to .*successfully updated.*$/,
|
|
788
|
+
0
|
|
789
|
+
]
|
|
790
|
+
|
|
791
|
+
unless success_anchor
|
|
792
|
+
raise Thor::Error,
|
|
793
|
+
"Could not find update success response in #{controller_path}"
|
|
794
|
+
end
|
|
795
|
+
|
|
796
|
+
error_anchor = update_method[
|
|
797
|
+
/^\s*format\.html \{ render :edit, status: :unprocessable_content \}$/,
|
|
798
|
+
0
|
|
799
|
+
]
|
|
800
|
+
|
|
801
|
+
unless error_anchor
|
|
802
|
+
raise Thor::Error,
|
|
803
|
+
"Could not find update error response in #{controller_path}"
|
|
804
|
+
end
|
|
805
|
+
|
|
806
|
+
attachment_reload_block = attachment_reload_block_for_fields
|
|
807
|
+
|
|
808
|
+
turbo_stream_block = <<~RUBY.indent(8)
|
|
809
|
+
#{attachment_reload_block}format.turbo_stream do
|
|
810
|
+
flash.now[:notice] = "#{class_name} was successfully updated."
|
|
811
|
+
|
|
812
|
+
streams = [
|
|
813
|
+
turbo_stream.replace(
|
|
814
|
+
"flash-messages",
|
|
815
|
+
partial: "shared/flash"
|
|
816
|
+
)
|
|
817
|
+
]
|
|
818
|
+
|
|
819
|
+
if IN_PLACE_FIELDS.include?(params[:in_place_field])
|
|
820
|
+
streams << turbo_stream.replace(
|
|
821
|
+
helpers.dom_id(
|
|
822
|
+
@#{singular_table_name},
|
|
823
|
+
params[:in_place_field]
|
|
824
|
+
),
|
|
825
|
+
partial: "#{plural_table_name}/\#{params[:in_place_field]}",
|
|
826
|
+
locals: { #{singular_table_name}: @#{singular_table_name} }
|
|
827
|
+
)
|
|
828
|
+
end
|
|
829
|
+
|
|
830
|
+
render turbo_stream: streams
|
|
831
|
+
end
|
|
832
|
+
|
|
833
|
+
RUBY
|
|
834
|
+
|
|
835
|
+
error_turbo_stream_block = <<~RUBY.indent(8)
|
|
836
|
+
format.turbo_stream do
|
|
837
|
+
if IN_PLACE_FIELDS.include?(params[:in_place_field])
|
|
838
|
+
render turbo_stream: turbo_stream.replace(
|
|
839
|
+
helpers.dom_id(
|
|
840
|
+
@#{singular_table_name},
|
|
841
|
+
params[:in_place_field]
|
|
842
|
+
),
|
|
843
|
+
template: "#{plural_table_name}/edit_\#{params[:in_place_field]}"
|
|
844
|
+
),
|
|
845
|
+
status: :unprocessable_content
|
|
846
|
+
else
|
|
847
|
+
head :unprocessable_content
|
|
848
|
+
end
|
|
849
|
+
end
|
|
850
|
+
|
|
851
|
+
RUBY
|
|
852
|
+
|
|
853
|
+
update_method = update_method.sub(
|
|
854
|
+
success_anchor,
|
|
855
|
+
"#{turbo_stream_block}#{success_anchor}"
|
|
856
|
+
)
|
|
857
|
+
|
|
858
|
+
update_method.sub(
|
|
859
|
+
error_anchor,
|
|
860
|
+
"#{error_turbo_stream_block}#{error_anchor}"
|
|
861
|
+
)
|
|
862
|
+
end
|
|
863
|
+
|
|
864
|
+
def add_in_place_params_for(field)
|
|
865
|
+
case field_type(field)
|
|
866
|
+
when :attachment
|
|
867
|
+
add_single_attachment_params(field)
|
|
868
|
+
when :attachments
|
|
869
|
+
add_multiple_attachment_params(field)
|
|
870
|
+
else
|
|
871
|
+
add_regular_field_param(field)
|
|
872
|
+
end
|
|
873
|
+
end
|
|
874
|
+
|
|
875
|
+
def add_regular_field_param(field)
|
|
876
|
+
controller_path = File.join(
|
|
877
|
+
"app/controllers",
|
|
878
|
+
"#{plural_table_name}_controller.rb"
|
|
879
|
+
)
|
|
880
|
+
|
|
881
|
+
full_controller_path = File.join(
|
|
882
|
+
destination_root,
|
|
883
|
+
controller_path
|
|
884
|
+
)
|
|
885
|
+
|
|
886
|
+
controller = File.read(full_controller_path)
|
|
887
|
+
|
|
888
|
+
params_method = controller[
|
|
889
|
+
/def #{Regexp.escape(singular_table_name)}_params.*?^\s*end/m,
|
|
890
|
+
0
|
|
891
|
+
]
|
|
892
|
+
|
|
893
|
+
unless params_method
|
|
894
|
+
raise Thor::Error,
|
|
895
|
+
"Could not find #{singular_table_name}_params in #{controller_path}"
|
|
896
|
+
end
|
|
897
|
+
|
|
898
|
+
parameter = ":#{field}"
|
|
899
|
+
|
|
900
|
+
return if params_method.include?(parameter)
|
|
901
|
+
|
|
902
|
+
updated_method =
|
|
903
|
+
if params_method.match?(/\w+:\s*\[\]/)
|
|
904
|
+
params_method.sub(
|
|
905
|
+
/(?=\w+:\s*\[\])/,
|
|
906
|
+
"#{parameter}, "
|
|
907
|
+
)
|
|
908
|
+
else
|
|
909
|
+
params_method.sub(
|
|
910
|
+
/\]\s*\)\s*$/,
|
|
911
|
+
", #{parameter}])"
|
|
912
|
+
)
|
|
913
|
+
end
|
|
914
|
+
|
|
915
|
+
gsub_file(
|
|
916
|
+
controller_path,
|
|
917
|
+
params_method,
|
|
918
|
+
updated_method
|
|
919
|
+
)
|
|
920
|
+
end
|
|
921
|
+
|
|
922
|
+
def add_single_attachment_params(field)
|
|
923
|
+
controller_path = File.join(
|
|
924
|
+
"app/controllers",
|
|
925
|
+
"#{plural_table_name}_controller.rb"
|
|
926
|
+
)
|
|
927
|
+
|
|
928
|
+
full_controller_path = File.join(
|
|
929
|
+
destination_root,
|
|
930
|
+
controller_path
|
|
931
|
+
)
|
|
932
|
+
|
|
933
|
+
controller = File.read(full_controller_path)
|
|
934
|
+
|
|
935
|
+
params_method = controller[
|
|
936
|
+
/def #{Regexp.escape(singular_table_name)}_params.*?^\s*end/m,
|
|
937
|
+
0
|
|
938
|
+
]
|
|
939
|
+
|
|
940
|
+
unless params_method
|
|
941
|
+
raise Thor::Error,
|
|
942
|
+
"Could not find #{singular_table_name}_params in #{controller_path}"
|
|
943
|
+
end
|
|
944
|
+
|
|
945
|
+
parameters = [
|
|
946
|
+
":#{field}",
|
|
947
|
+
":remove_#{field}"
|
|
948
|
+
]
|
|
949
|
+
|
|
950
|
+
parameters.each do |parameter|
|
|
951
|
+
next if params_method.include?(parameter)
|
|
952
|
+
|
|
953
|
+
updated_method =
|
|
954
|
+
if params_method.match?(/\w+:\s*\[\]/)
|
|
955
|
+
params_method.sub(
|
|
956
|
+
/(?=\w+:\s*\[\])/,
|
|
957
|
+
"#{parameter}, "
|
|
958
|
+
)
|
|
959
|
+
else
|
|
960
|
+
params_method.sub(
|
|
961
|
+
/\]\s*\)\s*$/,
|
|
962
|
+
", #{parameter}])"
|
|
963
|
+
)
|
|
964
|
+
end
|
|
965
|
+
|
|
966
|
+
gsub_file(
|
|
967
|
+
controller_path,
|
|
968
|
+
params_method,
|
|
969
|
+
updated_method
|
|
970
|
+
)
|
|
971
|
+
|
|
972
|
+
params_method = updated_method
|
|
973
|
+
end
|
|
974
|
+
end
|
|
975
|
+
|
|
976
|
+
def add_multiple_attachment_params(field)
|
|
977
|
+
controller_path = File.join(
|
|
978
|
+
"app/controllers",
|
|
979
|
+
"#{plural_table_name}_controller.rb"
|
|
980
|
+
)
|
|
981
|
+
|
|
982
|
+
full_controller_path = File.join(
|
|
983
|
+
destination_root,
|
|
984
|
+
controller_path
|
|
985
|
+
)
|
|
986
|
+
|
|
987
|
+
controller = File.read(full_controller_path)
|
|
988
|
+
|
|
989
|
+
params_method = controller[
|
|
990
|
+
/def #{Regexp.escape(singular_table_name)}_params.*?^\s*end/m,
|
|
991
|
+
0
|
|
992
|
+
]
|
|
993
|
+
|
|
994
|
+
unless params_method
|
|
995
|
+
raise Thor::Error,
|
|
996
|
+
"Could not find #{singular_table_name}_params in #{controller_path}"
|
|
997
|
+
end
|
|
998
|
+
|
|
999
|
+
parameters = [
|
|
1000
|
+
"#{field}: []",
|
|
1001
|
+
"remove_#{field}_ids: []"
|
|
1002
|
+
]
|
|
1003
|
+
|
|
1004
|
+
parameters.each do |parameter|
|
|
1005
|
+
next if params_method.include?(parameter)
|
|
1006
|
+
|
|
1007
|
+
updated_method = params_method.sub(
|
|
1008
|
+
/\s*\]\s*\)\s*$/,
|
|
1009
|
+
", #{parameter} ])"
|
|
1010
|
+
)
|
|
1011
|
+
|
|
1012
|
+
gsub_file(
|
|
1013
|
+
controller_path,
|
|
1014
|
+
params_method,
|
|
1015
|
+
updated_method
|
|
1016
|
+
)
|
|
1017
|
+
|
|
1018
|
+
params_method = updated_method
|
|
1019
|
+
end
|
|
1020
|
+
end
|
|
1021
|
+
|
|
1022
|
+
def create_edit_view(field)
|
|
1023
|
+
template_name = edit_template_for(field)
|
|
1024
|
+
|
|
1025
|
+
return unless template_name
|
|
1026
|
+
|
|
1027
|
+
destination = File.join(
|
|
1028
|
+
"app/views",
|
|
1029
|
+
plural_table_name,
|
|
1030
|
+
"edit_#{field}.html.erb"
|
|
1031
|
+
)
|
|
1032
|
+
|
|
1033
|
+
return if File.exist?(
|
|
1034
|
+
File.join(destination_root, destination)
|
|
1035
|
+
)
|
|
1036
|
+
|
|
1037
|
+
@field = field
|
|
1038
|
+
|
|
1039
|
+
template(
|
|
1040
|
+
template_name,
|
|
1041
|
+
destination
|
|
1042
|
+
)
|
|
1043
|
+
ensure
|
|
1044
|
+
@field = nil
|
|
1045
|
+
end
|
|
1046
|
+
|
|
1047
|
+
def edit_template_for(field)
|
|
1048
|
+
{
|
|
1049
|
+
text: "edit_text.html.erb.tt",
|
|
1050
|
+
rich_text: "edit_rich_text.html.erb.tt",
|
|
1051
|
+
boolean: "edit_boolean.html.erb.tt",
|
|
1052
|
+
number: "edit_number.html.erb.tt",
|
|
1053
|
+
select: "edit_select.html.erb.tt",
|
|
1054
|
+
radio: "edit_radio.html.erb.tt",
|
|
1055
|
+
attachment: "edit_attachment.html.erb.tt",
|
|
1056
|
+
attachments: "edit_attachments.html.erb.tt"
|
|
1057
|
+
}[editor_type(field)]
|
|
1058
|
+
end
|
|
1059
|
+
|
|
1060
|
+
def resource_field_range(resource, field)
|
|
1061
|
+
label = "#{field.humanize}:"
|
|
1062
|
+
|
|
1063
|
+
label_position = resource.index(
|
|
1064
|
+
%(<strong class="resource-label">#{label}</strong>)
|
|
1065
|
+
)
|
|
1066
|
+
|
|
1067
|
+
return unless label_position
|
|
1068
|
+
|
|
1069
|
+
field_start = resource.rindex(
|
|
1070
|
+
'<div class="resource-field">',
|
|
1071
|
+
label_position
|
|
1072
|
+
)
|
|
1073
|
+
|
|
1074
|
+
return unless field_start
|
|
1075
|
+
|
|
1076
|
+
position = field_start
|
|
1077
|
+
depth = 0
|
|
1078
|
+
|
|
1079
|
+
while (match = resource.match(%r{<div\b[^>]*>|</div>}, position))
|
|
1080
|
+
if match[0].start_with?("<div")
|
|
1081
|
+
depth += 1
|
|
1082
|
+
else
|
|
1083
|
+
depth -= 1
|
|
1084
|
+
|
|
1085
|
+
return field_start...match.end(0) if depth.zero?
|
|
1086
|
+
end
|
|
1087
|
+
|
|
1088
|
+
position = match.end(0)
|
|
1089
|
+
end
|
|
1090
|
+
|
|
1091
|
+
nil
|
|
1092
|
+
end
|
|
1093
|
+
|
|
1094
|
+
def integrate_display_partial(field)
|
|
1095
|
+
resource_path = File.join(
|
|
1096
|
+
"app/views",
|
|
1097
|
+
plural_table_name,
|
|
1098
|
+
"_#{singular_table_name}.html.erb"
|
|
1099
|
+
)
|
|
1100
|
+
|
|
1101
|
+
full_resource_path = File.join(
|
|
1102
|
+
destination_root,
|
|
1103
|
+
resource_path
|
|
1104
|
+
)
|
|
1105
|
+
|
|
1106
|
+
return unless File.exist?(full_resource_path)
|
|
1107
|
+
|
|
1108
|
+
resource = File.read(full_resource_path)
|
|
1109
|
+
|
|
1110
|
+
render_statement =
|
|
1111
|
+
"<%= render \"#{plural_table_name}/#{field}\", " \
|
|
1112
|
+
"#{singular_table_name}: #{singular_table_name}, " \
|
|
1113
|
+
"compact: local_assigns[:compact] %>"
|
|
1114
|
+
|
|
1115
|
+
return if resource.include?(render_statement)
|
|
1116
|
+
|
|
1117
|
+
field_range = resource_field_range(resource, field)
|
|
1118
|
+
|
|
1119
|
+
unless field_range
|
|
1120
|
+
say_status(
|
|
1121
|
+
:warning,
|
|
1122
|
+
"Could not integrate in-place field #{field} into #{resource_path}",
|
|
1123
|
+
:yellow
|
|
1124
|
+
)
|
|
1125
|
+
return
|
|
1126
|
+
end
|
|
1127
|
+
|
|
1128
|
+
resource[field_range] = render_statement
|
|
1129
|
+
|
|
1130
|
+
File.write(
|
|
1131
|
+
full_resource_path,
|
|
1132
|
+
resource
|
|
1133
|
+
)
|
|
1134
|
+
end
|
|
1135
|
+
|
|
1136
|
+
def create_display_partial(field)
|
|
1137
|
+
template_name = display_template_for(field)
|
|
1138
|
+
|
|
1139
|
+
return unless template_name
|
|
1140
|
+
|
|
1141
|
+
destination = File.join(
|
|
1142
|
+
"app/views",
|
|
1143
|
+
plural_table_name,
|
|
1144
|
+
"_#{field}.html.erb"
|
|
1145
|
+
)
|
|
1146
|
+
|
|
1147
|
+
return if File.exist?(
|
|
1148
|
+
File.join(destination_root, destination)
|
|
1149
|
+
)
|
|
1150
|
+
|
|
1151
|
+
@field = field
|
|
1152
|
+
|
|
1153
|
+
template(
|
|
1154
|
+
template_name,
|
|
1155
|
+
destination
|
|
1156
|
+
)
|
|
1157
|
+
ensure
|
|
1158
|
+
@field = nil
|
|
1159
|
+
end
|
|
1160
|
+
|
|
1161
|
+
def display_template_for(field)
|
|
1162
|
+
{
|
|
1163
|
+
text: "display_text.html.erb.tt",
|
|
1164
|
+
rich_text: "display_rich_text.html.erb.tt",
|
|
1165
|
+
boolean: "display_boolean.html.erb.tt",
|
|
1166
|
+
number: "display_number.html.erb.tt",
|
|
1167
|
+
select: "display_select.html.erb.tt",
|
|
1168
|
+
radio: "display_radio.html.erb.tt",
|
|
1169
|
+
attachment: "display_attachment.html.erb.tt",
|
|
1170
|
+
attachments: "display_attachments.html.erb.tt"
|
|
1171
|
+
}[editor_type(field)]
|
|
1172
|
+
end
|
|
1173
|
+
|
|
1174
|
+
def attachment_update_blocks_for_fields
|
|
1175
|
+
before_update_lines = []
|
|
1176
|
+
after_update_lines = []
|
|
1177
|
+
|
|
1178
|
+
fields.each do |field|
|
|
1179
|
+
case field_type(field)
|
|
1180
|
+
when :attachment
|
|
1181
|
+
remove_variable = "remove_#{field}"
|
|
1182
|
+
|
|
1183
|
+
before_update_lines <<
|
|
1184
|
+
"#{remove_variable} = update_params.delete(\"remove_#{field}\")"
|
|
1185
|
+
|
|
1186
|
+
after_update_lines <<
|
|
1187
|
+
"@#{singular_table_name}.#{field}.purge if #{remove_variable} == \"1\""
|
|
1188
|
+
|
|
1189
|
+
when :attachments
|
|
1190
|
+
new_variable = "new_#{field}"
|
|
1191
|
+
remove_variable = "remove_#{field}_ids"
|
|
1192
|
+
|
|
1193
|
+
before_update_lines <<
|
|
1194
|
+
"#{new_variable} = update_params.delete(\"#{field}\")"
|
|
1195
|
+
|
|
1196
|
+
before_update_lines <<
|
|
1197
|
+
"#{remove_variable} = update_params.delete(\"remove_#{field}_ids\")"
|
|
1198
|
+
|
|
1199
|
+
after_update_lines <<
|
|
1200
|
+
"@#{singular_table_name}.#{field}.attach(#{new_variable}) if #{new_variable}.present?"
|
|
1201
|
+
|
|
1202
|
+
after_update_lines << <<~RUBY.strip
|
|
1203
|
+
if #{remove_variable}.present?
|
|
1204
|
+
@#{singular_table_name}.#{field}.attachments
|
|
1205
|
+
.where(id: #{remove_variable})
|
|
1206
|
+
.find_each(&:purge)
|
|
1207
|
+
end
|
|
1208
|
+
RUBY
|
|
1209
|
+
end
|
|
1210
|
+
end
|
|
1211
|
+
|
|
1212
|
+
{
|
|
1213
|
+
before_update: before_update_lines,
|
|
1214
|
+
after_update: after_update_lines
|
|
1215
|
+
}
|
|
1216
|
+
end
|
|
1217
|
+
|
|
1218
|
+
def attachment_reload_block_for_fields
|
|
1219
|
+
reload_lines = fields.filter_map do |field|
|
|
1220
|
+
next unless field_type(field) == :attachments
|
|
1221
|
+
|
|
1222
|
+
"@#{singular_table_name}.#{field}.reload"
|
|
1223
|
+
end
|
|
1224
|
+
|
|
1225
|
+
return "" if reload_lines.empty?
|
|
1226
|
+
|
|
1227
|
+
"#{reload_lines.join("\n")}\n\n"
|
|
1228
|
+
end
|
|
1229
|
+
end
|
|
1230
|
+
end
|
|
1231
|
+
end
|