b4um_generators 0.3.0 → 0.4.2

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 (30) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -2
  3. data/CHANGELOG.md +72 -0
  4. data/README.de.md +338 -4
  5. data/README.md +330 -5
  6. data/lib/b4um_generators/version.rb +1 -1
  7. data/lib/generators/b4um/admin/admin_generator.rb +725 -0
  8. data/lib/generators/b4um/admin/templates/add_admin_fields_migration.rb.tt +10 -0
  9. data/lib/generators/b4um/admin/templates/admin_base_controller.rb.tt +5 -0
  10. data/lib/generators/b4um/admin/templates/admin_dashboard.html.erb.tt +19 -0
  11. data/lib/generators/b4um/admin/templates/admin_dashboard_controller.rb.tt +6 -0
  12. data/lib/generators/b4um/admin/templates/admin_users_controller.rb.tt +70 -0
  13. data/lib/generators/b4um/admin/templates/admin_users_index.html.erb.tt +110 -0
  14. data/lib/generators/b4um/admin/templates/b4um_admin.rake.tt +106 -0
  15. data/lib/generators/b4um/authentication/authentication_generator.rb +436 -23
  16. data/lib/generators/b4um/comments/comments_generator.rb +39 -9
  17. data/lib/generators/b4um/guest/guest_generator.rb +673 -0
  18. data/lib/generators/b4um/guest/templates/add_guest_fields_migration.rb.tt +12 -0
  19. data/lib/generators/b4um/guest/templates/b4um_guests.rake.tt +12 -0
  20. data/lib/generators/b4um/guest/templates/edit.html.erb.tt +156 -0
  21. data/lib/generators/b4um/guest/templates/guest_expiration_warning.html.erb.tt +12 -0
  22. data/lib/generators/b4um/guest/templates/guests_controller.rb.tt +88 -0
  23. data/lib/generators/b4um/guest/templates/new.html.erb.tt +136 -0
  24. data/lib/generators/b4um/help/help_generator.rb +162 -0
  25. data/lib/generators/b4um/in_place/in_place_generator.rb +132 -7
  26. data/lib/generators/b4um/readable/readable_generator.rb +8 -5
  27. data/lib/generators/b4um/scaffold/scaffold_generator.rb +213 -73
  28. data/lib/generators/b4um/scaffold/templates/_form.html.erb.tt +18 -14
  29. data/lib/generators/b4um/sortable/sortable_generator.rb +104 -36
  30. metadata +16 -1
@@ -137,9 +137,48 @@ module B4um
137
137
  end
138
138
 
139
139
  def add_edit_actions
140
- fields.each do |field|
141
- add_edit_action(field)
142
- end
140
+ controller_path = File.join(
141
+ "app/controllers",
142
+ "#{plural_table_name}_controller.rb"
143
+ )
144
+
145
+ full_controller_path = File.join(
146
+ destination_root,
147
+ controller_path
148
+ )
149
+
150
+ controller = File.read(full_controller_path)
151
+
152
+ fields_to_insert =
153
+ prepare_in_place_edit_actions(controller)
154
+
155
+ create_comment =
156
+ extract_create_comment(controller)
157
+
158
+ create_match =
159
+ find_create_action(controller, controller_path)
160
+
161
+ replacement = build_edit_actions_block(
162
+ fields_to_insert,
163
+ create_match[1],
164
+ create_comment
165
+ )
166
+
167
+ controller.sub!(
168
+ /\n*^[ \t]*def[ \t]+create[ \t]*$/,
169
+ replacement
170
+ )
171
+
172
+ File.write(
173
+ full_controller_path,
174
+ controller
175
+ )
176
+
177
+ say_status(
178
+ :gsub,
179
+ controller_path,
180
+ :green
181
+ )
143
182
  end
144
183
 
145
184
  def add_edit_actions_to_set_product
@@ -216,6 +255,77 @@ module B4um
216
255
 
217
256
  private
218
257
 
258
+ def prepare_in_place_edit_actions(controller)
259
+ fields.filter_map do |field|
260
+ empty_edit_action = /
261
+ ^[ \t]*def[ \t]+edit_#{Regexp.escape(field)}[ \t]*\n
262
+ ^[ \t]*end[ \t]*\n?
263
+ /x
264
+
265
+ removed_empty_action =
266
+ controller.gsub!(empty_edit_action, "")
267
+
268
+ custom_action =
269
+ controller.match?(
270
+ /^[ \t]*def[ \t]+edit_#{Regexp.escape(field)}[ \t]*$/
271
+ )
272
+
273
+ field unless custom_action && !removed_empty_action
274
+ end
275
+ end
276
+
277
+ def extract_create_comment(controller)
278
+ create_comment_pattern =
279
+ %r{^[ \t]*# POST /#{Regexp.escape(plural_table_name)} or /#{Regexp.escape(plural_table_name)}\.json[ \t]*\n?}
280
+
281
+ create_comment =
282
+ controller[create_comment_pattern]&.rstrip
283
+
284
+ controller.gsub!(
285
+ create_comment_pattern,
286
+ ""
287
+ )
288
+
289
+ create_comment
290
+ end
291
+
292
+ def find_create_action(controller, controller_path)
293
+ create_match =
294
+ controller.match(
295
+ /^([ \t]*)def[ \t]+create[ \t]*$/
296
+ )
297
+
298
+ return create_match if create_match
299
+
300
+ raise Thor::Error,
301
+ "Could not find create action in #{controller_path}"
302
+ end
303
+
304
+ def build_edit_actions_block(
305
+ fields_to_insert,
306
+ indentation,
307
+ create_comment
308
+ )
309
+ edit_actions = fields_to_insert.flat_map do |field|
310
+ [
311
+ "#{indentation}def edit_#{field}",
312
+ "#{indentation}end",
313
+ ""
314
+ ]
315
+ end
316
+
317
+ replacement = [
318
+ "",
319
+ "",
320
+ *edit_actions
321
+ ]
322
+
323
+ replacement << create_comment if create_comment
324
+ replacement << "#{indentation}def create"
325
+
326
+ replacement.join("\n")
327
+ end
328
+
219
329
  def ensure_flash_render_in_layout
220
330
  layout_path = "app/views/layouts/application.html.erb"
221
331
  full_layout_path = File.join(destination_root, layout_path)
@@ -581,11 +691,26 @@ module B4um
581
691
  /^\s*def edit_#{Regexp.escape(field)}\s*$/
582
692
  )
583
693
 
584
- inject_into_file(
694
+ create_action = /^([ \t]*)def create\s*$/
695
+
696
+ unless controller.match?(create_action)
697
+ raise Thor::Error,
698
+ "Could not find create action in #{controller_path}"
699
+ end
700
+
701
+ gsub_file(
585
702
  controller_path,
586
- before: /^\s*def create\s*$/
587
- ) do
588
- " def edit_#{field}\n end\n\n"
703
+ /\n*[ \t]*def create\s*$/
704
+ ) do |match|
705
+ indentation = match[/[ \t]*(?=def create)/]
706
+
707
+ [
708
+ "",
709
+ "#{indentation}def edit_#{field}",
710
+ "#{indentation}end",
711
+ "",
712
+ "#{indentation}def create"
713
+ ].join("\n")
589
714
  end
590
715
  end
591
716
 
@@ -39,14 +39,17 @@ module B4um
39
39
  "#{class_name} already defines to_param."
40
40
  end
41
41
 
42
+ readable_method = [
43
+ " def to_param",
44
+ " \"\#{id} \#{#{attribute}}\".parameterize",
45
+ " end",
46
+ ""
47
+ ].join("\n")
48
+
42
49
  inject_into_class(
43
50
  model_path,
44
51
  class_name,
45
- <<~RUBY
46
- def to_param
47
- "\#{id} \#{#{attribute}}".parameterize
48
- end
49
- RUBY
52
+ readable_method
50
53
  )
51
54
 
52
55
  say_status(
@@ -33,6 +33,43 @@ module B4um
33
33
  type: :string,
34
34
  desc: "Renders a field as radio buttons. Example: condition:new=Neu,used=Gebraucht"
35
35
 
36
+ class_option :required,
37
+ type: :string,
38
+ desc: "Adds presence validation to fields. Example: name,email"
39
+
40
+ def initialize(args, options, config)
41
+ super
42
+
43
+ validate_required_fields
44
+ end
45
+
46
+ no_tasks do
47
+ def validate_required_fields
48
+ return if required_fields.empty?
49
+
50
+ attribute_names = attributes.map(&:name)
51
+
52
+ unknown_fields = required_fields - attribute_names
53
+
54
+ return if unknown_fields.empty?
55
+
56
+ raise Thor::Error,
57
+ "Unknown required field: #{unknown_fields.first}"
58
+ end
59
+
60
+ def required_fields
61
+ options[:required]
62
+ .to_s
63
+ .split(",")
64
+ .map(&:strip)
65
+ .reject(&:empty?)
66
+ end
67
+
68
+ def revoking?
69
+ behavior == :revoke
70
+ end
71
+ end
72
+
36
73
  def validate_editor_options
37
74
  configurations = {
38
75
  select: select_configuration,
@@ -147,7 +184,26 @@ module B4um
147
184
  )
148
185
  end
149
186
 
187
+ def add_required_validations
188
+ return if revoking?
189
+ return if required_fields.empty?
190
+
191
+ validation = required_fields
192
+ .map { |field| ":#{field}" }
193
+ .join(", ")
194
+
195
+ inject_into_class(
196
+ File.join(
197
+ "app/models",
198
+ "#{singular_table_name}.rb"
199
+ ),
200
+ class_name,
201
+ " validates #{validation}, presence: true\n"
202
+ )
203
+ end
204
+
150
205
  def add_secure_password
206
+ return if revoking?
151
207
  return unless attributes.any? { |attribute| attribute.name == "password_digest" }
152
208
 
153
209
  inject_into_class(
@@ -158,6 +214,7 @@ module B4um
158
214
  end
159
215
 
160
216
  def add_bcrypt
217
+ return if revoking?
161
218
  return unless attributes.any? { |attribute| attribute.name == "password_digest" }
162
219
 
163
220
  gemfile_path = "Gemfile"
@@ -171,6 +228,7 @@ module B4um
171
228
  end
172
229
 
173
230
  def update_password_params
231
+ return if revoking?
174
232
  return unless attributes.any? { |attribute| attribute.name == "password_digest" }
175
233
 
176
234
  controller_path = File.join(
@@ -183,74 +241,41 @@ module B4um
183
241
  ":password_digest",
184
242
  ":password, :password_confirmation"
185
243
  )
244
+
245
+ update_password_test_params
186
246
  end
187
247
 
188
248
  def update_multiple_attachments
189
- attachment_attributes = attributes.select do |attribute|
190
- attribute.type == :attachments
191
- end
249
+ return if revoking?
250
+
251
+ attachment_names = multiple_attachment_names
192
252
 
193
- return if attachment_attributes.empty?
253
+ return if attachment_names.empty?
194
254
 
195
255
  controller_path = File.join(
196
256
  "app/controllers",
197
257
  "#{plural_table_name}_controller.rb"
198
258
  )
199
259
 
200
- attachment_names = attachment_attributes.map(&:name)
201
-
202
- setup_lines = attachment_names.map do |name|
203
- [
204
- " new_#{name} = update_params.delete(\"#{name}\")",
205
- " remove_#{name}_ids = update_params.delete(\"remove_#{name}_ids\")"
206
- ]
207
- end.flatten.join("\n")
208
-
209
- attachment_lines = attachment_names.map do |name|
210
- [
211
- " @#{singular_table_name}.#{name}.attach(new_#{name}) if new_#{name}.present?",
212
- "",
213
- " if remove_#{name}_ids.present?",
214
- " @#{singular_table_name}.#{name}.attachments",
215
- " .where(id: remove_#{name}_ids)",
216
- " .find_each(&:purge)",
217
- " end"
218
- ]
219
- end.flatten.join("\n")
220
-
221
- gsub_file(
260
+ add_multiple_attachment_setup(
222
261
  controller_path,
223
- " def update\n respond_to do |format|\n",
224
- [
225
- " def update",
226
- " update_params = #{singular_table_name}_params",
227
- setup_lines,
228
- "",
229
- " respond_to do |format|",
230
- ""
231
- ].join("\n")
262
+ attachment_names
232
263
  )
233
264
 
234
- gsub_file(
265
+ add_multiple_attachment_handling(
235
266
  controller_path,
236
- " if @#{singular_table_name}.update(#{singular_table_name}_params)\n",
237
- [
238
- " if @#{singular_table_name}.update(update_params)",
239
- attachment_lines,
240
- ""
241
- ].join("\n")
267
+ attachment_names
242
268
  )
243
269
 
244
- attachment_names.each do |name|
245
- gsub_file(
246
- controller_path,
247
- "#{name}: []",
248
- "#{name}: [], remove_#{name}_ids: []"
249
- )
250
- end
270
+ add_multiple_attachment_removal_params(
271
+ controller_path,
272
+ attachment_names
273
+ )
251
274
  end
252
275
 
253
276
  def add_readable_param
277
+ return if revoking?
278
+
254
279
  param_name = options[:param]
255
280
 
256
281
  return if param_name.blank?
@@ -265,18 +290,24 @@ module B4um
265
290
  return
266
291
  end
267
292
 
293
+ readable_method = [
294
+ " def to_param",
295
+ " \"\#{id} \#{#{param_name}}\".parameterize",
296
+ " end",
297
+ "",
298
+ ""
299
+ ].join("\n")
300
+
268
301
  inject_into_class(
269
302
  File.join("app/models", "#{singular_table_name}.rb"),
270
303
  class_name,
271
- <<~RUBY
272
- def to_param
273
- "\#{id} \#{#{param_name}}".parameterize
274
- end
275
- RUBY
304
+ readable_method
276
305
  )
277
306
  end
278
307
 
279
308
  def update_destroy_flash
309
+ return if revoking?
310
+
280
311
  controller_path = File.join(
281
312
  "app/controllers",
282
313
  "#{plural_table_name}_controller.rb"
@@ -293,16 +324,16 @@ module B4um
293
324
  gsub_file(
294
325
  controller_path,
295
326
  /
296
- format\.html\ \{\ redirect_to\ ([^,]+),
327
+ ^([ \t]*)format\.html\ \{\ redirect_to\ ([^,]+),
297
328
  \ notice:\ ("[^"]*\ was\ successfully\ destroyed\."),
298
329
  \ status:\ :see_other\ \}
299
330
  /x,
300
- <<~RUBY.chomp
301
- format.html do
302
- flash[:deleted] = \\2
303
- redirect_to \\1, status: :see_other
304
- end
305
- RUBY
331
+ [
332
+ "\\1format.html do",
333
+ "\\1 flash[:deleted] = \\3",
334
+ "\\1 redirect_to \\2, status: :see_other",
335
+ "\\1end"
336
+ ].join("\n")
306
337
  )
307
338
 
308
339
  return
@@ -311,11 +342,11 @@ module B4um
311
342
  # Rails-Scaffold ohne respond_to
312
343
  gsub_file(
313
344
  controller_path,
314
- /redirect_to ([^,]+), notice: ("[^"]* was successfully destroyed\."), status: :see_other/,
315
- <<~RUBY.chomp
316
- flash[:deleted] = \\2
317
- redirect_to \\1, status: :see_other
318
- RUBY
345
+ /^([ \t]*)redirect_to ([^,]+), notice: ("[^"]* was successfully destroyed\."), status: :see_other/,
346
+ [
347
+ "\\1flash[:deleted] = \\3",
348
+ "\\1redirect_to \\2, status: :see_other"
349
+ ].join("\n")
319
350
  )
320
351
  end
321
352
 
@@ -326,15 +357,8 @@ module B4um
326
357
  return unless File.exist?(full_navigation_path)
327
358
 
328
359
  navigation = File.read(full_navigation_path)
329
-
330
- return if navigation.include?(
331
- "controller: :#{plural_table_name}"
332
- )
333
-
334
360
  marker = "<%# B4UM_NAVIGATION_LINKS %>"
335
361
 
336
- return unless navigation.include?(marker)
337
-
338
362
  navigation_link = <<~ERB
339
363
  <%= navigation_link_to "#{plural_table_name.humanize}",
340
364
  #{plural_route_name}_path,
@@ -343,6 +367,34 @@ module B4um
343
367
  #{marker}
344
368
  ERB
345
369
 
370
+ if revoking?
371
+ generated_link = navigation_link.chomp
372
+
373
+ return unless navigation.include?(generated_link)
374
+
375
+ File.write(
376
+ full_navigation_path,
377
+ navigation.sub(
378
+ generated_link,
379
+ marker
380
+ )
381
+ )
382
+
383
+ say_status(
384
+ :remove,
385
+ "#{plural_table_name.humanize} navigation link",
386
+ :red
387
+ )
388
+
389
+ return
390
+ end
391
+
392
+ return if navigation.include?(
393
+ "controller: :#{plural_table_name}"
394
+ )
395
+
396
+ return unless navigation.include?(marker)
397
+
346
398
  gsub_file(
347
399
  navigation_path,
348
400
  marker,
@@ -352,6 +404,94 @@ module B4um
352
404
 
353
405
  private
354
406
 
407
+ def multiple_attachment_names
408
+ attributes
409
+ .select { |attribute| attribute.type == :attachments }
410
+ .map(&:name)
411
+ end
412
+
413
+ def add_multiple_attachment_setup(
414
+ controller_path,
415
+ attachment_names
416
+ )
417
+ setup_lines = attachment_names.flat_map do |name|
418
+ [
419
+ " new_#{name} = update_params.delete(\"#{name}\")",
420
+ " remove_#{name}_ids = update_params.delete(\"remove_#{name}_ids\")"
421
+ ]
422
+ end.join("\n")
423
+
424
+ gsub_file(
425
+ controller_path,
426
+ " def update\n respond_to do |format|\n",
427
+ [
428
+ " def update",
429
+ " update_params = #{singular_table_name}_params",
430
+ setup_lines,
431
+ "",
432
+ " respond_to do |format|",
433
+ ""
434
+ ].join("\n")
435
+ )
436
+ end
437
+
438
+ def add_multiple_attachment_handling(
439
+ controller_path,
440
+ attachment_names
441
+ )
442
+ attachment_lines = attachment_names.flat_map do |name|
443
+ [
444
+ " @#{singular_table_name}.#{name}.attach(new_#{name}) if new_#{name}.present?",
445
+ "",
446
+ " if remove_#{name}_ids.present?",
447
+ " @#{singular_table_name}.#{name}.attachments",
448
+ " .where(id: remove_#{name}_ids)",
449
+ " .find_each(&:purge)",
450
+ " end"
451
+ ]
452
+ end.join("\n")
453
+
454
+ gsub_file(
455
+ controller_path,
456
+ " if @#{singular_table_name}.update(#{singular_table_name}_params)\n",
457
+ [
458
+ " if @#{singular_table_name}.update(update_params)",
459
+ attachment_lines,
460
+ ""
461
+ ].join("\n")
462
+ )
463
+ end
464
+
465
+ def add_multiple_attachment_removal_params(
466
+ controller_path,
467
+ attachment_names
468
+ )
469
+ attachment_names.each do |name|
470
+ gsub_file(
471
+ controller_path,
472
+ "#{name}: []",
473
+ "#{name}: [], remove_#{name}_ids: []"
474
+ )
475
+ end
476
+ end
477
+
478
+ def update_password_test_params
479
+ test_path = File.join(
480
+ "test/controllers",
481
+ "#{plural_table_name}_controller_test.rb"
482
+ )
483
+
484
+ return unless File.exist?(
485
+ File.join(destination_root, test_path)
486
+ )
487
+
488
+ gsub_file(
489
+ test_path,
490
+ "password_digest: @#{singular_table_name}.password_digest",
491
+ 'password: "password", password_confirmation: "password"'
492
+ )
493
+ end
494
+
355
495
  def validate_editor_configuration(option_name, configuration)
356
496
  return unless options[option_name].present?
357
497