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
@@ -50,6 +50,89 @@ module B4um
50
50
  "#{authentication_class_name} email:string password_digest:string"
51
51
  end
52
52
 
53
+ def add_email_presence_validation
54
+ model_path = File.join(
55
+ "app/models",
56
+ "#{authentication_model_name}.rb"
57
+ )
58
+
59
+ full_model_path = File.join(
60
+ destination_root,
61
+ model_path
62
+ )
63
+
64
+ model = File.read(full_model_path)
65
+
66
+ return if model.include?(
67
+ "validates :email, presence: true"
68
+ )
69
+
70
+ inject_into_class(
71
+ model_path,
72
+ authentication_class_name,
73
+ " validates :email, presence: true\n"
74
+ )
75
+ end
76
+
77
+ def mark_registration_fields_required
78
+ form_path = File.join(
79
+ destination_root,
80
+ "app/views",
81
+ authentication_model_name.pluralize,
82
+ "_form.html.erb"
83
+ )
84
+
85
+ return unless File.exist?(form_path)
86
+
87
+ model_path = File.join(
88
+ destination_root,
89
+ "app/models",
90
+ "#{authentication_model_name}.rb"
91
+ )
92
+
93
+ model = File.read(model_path)
94
+ form = File.read(form_path)
95
+
96
+ model_required_fields =
97
+ model
98
+ .scan(
99
+ /validates\s+((?::[a-zA-Z_]\w*\s*,?\s*)+),\s*presence:\s+true/
100
+ )
101
+ .flat_map do |match|
102
+ match.first.scan(/:([a-zA-Z_]\w*)/).flatten
103
+ end
104
+
105
+ required_fields = model_required_fields.dup
106
+
107
+ if model.match?(/\bhas_secure_password\b/)
108
+ required_fields.push(
109
+ "password",
110
+ "password_confirmation"
111
+ )
112
+ end
113
+
114
+ required_fields.uniq!
115
+
116
+ required_fields.each do |field|
117
+ label = "#{field.humanize} *"
118
+
119
+ plain_label = /
120
+ form\.label\s+:#{Regexp.escape(field)},
121
+ \s*class:\s*"form-label"
122
+ /x
123
+
124
+ form.gsub!(
125
+ plain_label,
126
+ %(form.label :#{field}, "#{label}", class: "form-label")
127
+ )
128
+ end
129
+
130
+ File.write(
131
+ form_path,
132
+ form
133
+ )
134
+ end
135
+
53
136
  def validate_protected_controllers
54
137
  protected_controller_names.each do |controller_name|
55
138
  controller_path = File.join(
@@ -94,9 +177,15 @@ module B4um
94
177
  end
95
178
 
96
179
  def create_sessions_controller
180
+ controller_path = "app/controllers/sessions_controller.rb"
181
+
182
+ return if File.exist?(
183
+ File.join(destination_root, controller_path)
184
+ )
185
+
97
186
  template(
98
187
  "sessions_controller.rb.tt",
99
- "app/controllers/sessions_controller.rb"
188
+ controller_path
100
189
  )
101
190
  end
102
191
 
@@ -109,7 +198,10 @@ module B4um
109
198
 
110
199
  def add_authentication_helpers
111
200
  controller_path = "app/controllers/application_controller.rb"
112
- full_controller_path = File.join(destination_root, controller_path)
201
+ full_controller_path = File.join(
202
+ destination_root,
203
+ controller_path
204
+ )
113
205
 
114
206
  unless File.exist?(full_controller_path)
115
207
  raise Thor::Error,
@@ -122,31 +214,52 @@ module B4um
122
214
  "helper_method :#{current_authentication_method}, :logged_in?"
123
215
  )
124
216
 
125
- inject_into_file(
126
- controller_path,
127
- before: /^end\s*$/
128
- ) do
129
- <<~RUBY
130
-
131
- helper_method :#{current_authentication_method}, :logged_in?
217
+ authentication_helpers = [
218
+ "",
219
+ " helper_method :#{current_authentication_method}, :logged_in?",
220
+ "",
221
+ " private",
222
+ "",
223
+ " def #{current_authentication_method}",
224
+ " @#{current_authentication_method} ||= " \
225
+ "#{authentication_class_name}.find_by(" \
226
+ "id: session[:#{authentication_model_name}_id])",
227
+ " end",
228
+ "",
229
+ " def logged_in?",
230
+ " #{current_authentication_method}.present?",
231
+ " end",
232
+ "",
233
+ " def require_login",
234
+ " return if logged_in?",
235
+ "",
236
+ ' redirect_to login_path, alert: "Please log in first."',
237
+ " end",
238
+ ""
239
+ ].join("\n")
240
+
241
+ class_end_position = controller.rindex(/^end\s*$/)
132
242
 
133
- private
134
-
135
- def #{current_authentication_method}
136
- @#{current_authentication_method} ||= #{authentication_class_name}.find_by(id: session[:#{authentication_model_name}_id])
137
- end
243
+ unless class_end_position
244
+ raise Thor::Error,
245
+ "Could not find the end of ApplicationController."
246
+ end
138
247
 
139
- def logged_in?
140
- #{current_authentication_method}.present?
141
- end
248
+ controller.insert(
249
+ class_end_position,
250
+ authentication_helpers
251
+ )
142
252
 
143
- def require_login
144
- return if logged_in?
253
+ File.write(
254
+ full_controller_path,
255
+ controller
256
+ )
145
257
 
146
- redirect_to login_path, alert: "Please log in first."
147
- end
148
- RUBY
149
- end
258
+ say_status(
259
+ :insert,
260
+ controller_path,
261
+ :green
262
+ )
150
263
  end
151
264
 
152
265
  def update_in_place_helper
@@ -414,6 +527,12 @@ module B4um
414
527
  end
415
528
  end
416
529
 
530
+ def update_controller_tests
531
+ update_authentication_fixtures
532
+ update_authentication_resource_controller_tests
533
+ update_protected_controller_tests
534
+ end
535
+
417
536
  def protect_show_actions
418
537
  protected_controller_names.each do |controller_name|
419
538
  protect_show_actions_for(controller_name)
@@ -428,6 +547,300 @@ module B4um
428
547
 
429
548
  private
430
549
 
550
+ def update_authentication_fixtures
551
+ fixture_path = File.join(
552
+ destination_root,
553
+ "test/fixtures",
554
+ "#{authentication_model_name.pluralize}.yml"
555
+ )
556
+
557
+ return unless File.exist?(fixture_path)
558
+
559
+ fixtures = File.read(fixture_path)
560
+
561
+ fixture_name = nil
562
+
563
+ updated_fixtures = fixtures.lines.map do |line|
564
+ if (match = line.match(/^([a-zA-Z0-9_-]+):\s*$/))
565
+ fixture_name = match[1]
566
+ line
567
+ elsif line.match?(/^\s+email:\s*/)
568
+ fixture_label =
569
+ fixture_name
570
+ .to_s
571
+ .tr("_", "-")
572
+
573
+ indentation = line[/^\s*/]
574
+
575
+ "#{indentation}email: " \
576
+ "#{authentication_model_name}-#{fixture_label}@example.com\n"
577
+ elsif line.match?(/^\s+password_digest:\s*/)
578
+ indentation = line[/^\s*/]
579
+
580
+ "#{indentation}" \
581
+ 'password_digest: <%= BCrypt::Password.create("password") %>' \
582
+ "\n"
583
+ else
584
+ line
585
+ end
586
+ end.join
587
+
588
+ return if updated_fixtures == fixtures
589
+
590
+ File.write(
591
+ fixture_path,
592
+ updated_fixtures
593
+ )
594
+ end
595
+
596
+ def update_authentication_resource_controller_tests
597
+ test_path = File.join(
598
+ "test/controllers",
599
+ "#{authentication_model_name.pluralize}_controller_test.rb"
600
+ )
601
+
602
+ full_test_path = File.join(
603
+ destination_root,
604
+ test_path
605
+ )
606
+
607
+ return unless File.exist?(full_test_path)
608
+
609
+ test = File.read(full_test_path)
610
+
611
+ update_authentication_test_password_params(
612
+ test_path,
613
+ test
614
+ )
615
+
616
+ test = File.read(full_test_path)
617
+
618
+ update_authentication_index_test(
619
+ test_path,
620
+ test
621
+ )
622
+
623
+ test = File.read(full_test_path)
624
+
625
+ add_authentication_sessions_to_resource_tests(
626
+ test_path,
627
+ test
628
+ )
629
+
630
+ test = File.read(full_test_path)
631
+
632
+ update_authentication_destroy_redirect_test(
633
+ test_path,
634
+ test
635
+ )
636
+ end
637
+
638
+ def update_protected_controller_tests
639
+ protected_controller_names.each do |controller_name|
640
+ test_path = File.join(
641
+ "test/controllers",
642
+ "#{controller_name.underscore}_controller_test.rb"
643
+ )
644
+
645
+ full_test_path = File.join(
646
+ destination_root,
647
+ test_path
648
+ )
649
+
650
+ next unless File.exist?(full_test_path)
651
+
652
+ add_login_to_protected_controller_test(
653
+ test_path,
654
+ controller_name
655
+ )
656
+ end
657
+ end
658
+
659
+ def update_authentication_test_password_params(test_path, test)
660
+ return unless test.include?(
661
+ "password_digest: @#{authentication_model_name}.password_digest"
662
+ )
663
+
664
+ gsub_file(
665
+ test_path,
666
+ "password_digest: @#{authentication_model_name}.password_digest",
667
+ 'password: "password", password_confirmation: "password"'
668
+ )
669
+ end
670
+
671
+ def update_authentication_index_test(test_path, test)
672
+ resource_name = authentication_model_name
673
+ plural_name = resource_name.pluralize
674
+ test_name = Regexp.escape("should get index")
675
+
676
+ pattern = /
677
+ ^[ \t]*test\s+"#{test_name}"\s+do
678
+ \s+get\s+#{Regexp.escape(plural_name)}_url
679
+ \s+assert_response\s+:success
680
+ \s+end
681
+ /x
682
+
683
+ return unless test.match?(pattern)
684
+
685
+ replacement = [
686
+ " test \"should get index\" do",
687
+ " log_in_as(@#{resource_name})",
688
+ "",
689
+ " get #{plural_name}_url",
690
+ "",
691
+ " assert_redirected_to " \
692
+ "#{resource_name}_url(@#{resource_name})",
693
+ " end"
694
+ ].join("\n")
695
+
696
+ gsub_file(
697
+ test_path,
698
+ pattern,
699
+ replacement
700
+ )
701
+ end
702
+
703
+ def add_authentication_sessions_to_resource_tests(test_path, test)
704
+ %w[show edit update destroy].each do |action|
705
+ test_name =
706
+ if action == "edit"
707
+ "should get edit"
708
+ else
709
+ "should #{action} #{authentication_model_name}"
710
+ end
711
+
712
+ pattern = /
713
+ (test\s+"#{Regexp.escape(test_name)}"\s+do\n)
714
+ (?!\s*log_in_as\(@#{Regexp.escape(authentication_model_name)}\))
715
+ /x
716
+
717
+ next unless test.match?(pattern)
718
+
719
+ gsub_file(test_path, pattern) do |match|
720
+ "#{match} log_in_as(@#{authentication_model_name})\n"
721
+ end
722
+
723
+ test = File.read(
724
+ File.join(destination_root, test_path)
725
+ )
726
+ end
727
+
728
+ add_login_helper_to_controller_test(test_path)
729
+ end
730
+
731
+ def add_login_helper_to_controller_test(test_path)
732
+ full_test_path = File.join(
733
+ destination_root,
734
+ test_path
735
+ )
736
+
737
+ test = File.read(full_test_path)
738
+
739
+ return if test.include?("def log_in_as(user)")
740
+
741
+ final_end = test.rindex(/^end$/)
742
+ return unless final_end
743
+
744
+ helper =
745
+ "\n private\n\n " \
746
+ "def log_in_as(user)\n " \
747
+ 'user.update!(password: "password", password_confirmation: "password")' \
748
+ "\n " \
749
+ 'post login_url, params: { email: user.email, password: "password" }' \
750
+ "\n " \
751
+ "end\n"
752
+
753
+ updated_test =
754
+ test.dup.insert(final_end, helper)
755
+
756
+ File.write(
757
+ full_test_path,
758
+ updated_test
759
+ )
760
+ end
761
+
762
+ def update_authentication_destroy_redirect_test(test_path, test)
763
+ plural_name = authentication_model_name.pluralize
764
+
765
+ old_redirect =
766
+ "assert_redirected_to #{plural_name}_url"
767
+
768
+ return unless test.include?(old_redirect)
769
+
770
+ gsub_file(
771
+ test_path,
772
+ old_redirect,
773
+ "assert_redirected_to root_url"
774
+ )
775
+ end
776
+
777
+ def add_login_to_protected_controller_test(
778
+ test_path,
779
+ controller_name
780
+ )
781
+ full_test_path = File.join(
782
+ destination_root,
783
+ test_path
784
+ )
785
+
786
+ test = File.read(full_test_path)
787
+
788
+ add_authentication_fixture_to_test(
789
+ test_path,
790
+ test
791
+ )
792
+
793
+ test = File.read(full_test_path)
794
+
795
+ protected_test_actions_for(controller_name).each do |test_name|
796
+ pattern = /
797
+ (test\s+"#{Regexp.escape(test_name)}"\s+do\n)
798
+ (?!\s*log_in_as\(@authentication_user\))
799
+ /x
800
+
801
+ next unless test.match?(pattern)
802
+
803
+ gsub_file(test_path, pattern) do |match|
804
+ "#{match} log_in_as(@authentication_user)\n"
805
+ end
806
+
807
+ test = File.read(full_test_path)
808
+ end
809
+
810
+ add_login_helper_to_controller_test(test_path)
811
+ end
812
+
813
+ def add_authentication_fixture_to_test(test_path, test)
814
+ fixture_assignment =
815
+ "@authentication_user = " \
816
+ "#{authentication_model_name.pluralize}(:one)"
817
+
818
+ return if test.include?(fixture_assignment)
819
+
820
+ setup_pattern = /
821
+ (setup\s+do\n)
822
+ /x
823
+
824
+ return unless test.match?(setup_pattern)
825
+
826
+ gsub_file(test_path, setup_pattern) do |match|
827
+ "#{match} #{fixture_assignment}\n"
828
+ end
829
+ end
830
+
831
+ def protected_test_actions_for(controller_name)
832
+ singular_name =
833
+ controller_name.underscore.singularize
834
+
835
+ [
836
+ "should get new",
837
+ "should create #{singular_name}",
838
+ "should get edit",
839
+ "should update #{singular_name}",
840
+ "should destroy #{singular_name}"
841
+ ]
842
+ end
843
+
431
844
  def add_register_navigation_link(navigation_path, navigation)
432
845
  return if navigation.include?(
433
846
  'navigation_link_to "Register",'
@@ -169,20 +169,50 @@ module B4um
169
169
  routes = File.read(full_routes_path)
170
170
  resource_name = commentable.to_s.underscore.pluralize
171
171
 
172
- return if routes.include?(
173
- "resources :#{resource_name} do\n resources :comments"
174
- )
172
+ resource_block = /
173
+ ^(?<indent>[ \t]*)resources\s+:#{Regexp.escape(resource_name)}\s+do[ \t]*\n
174
+ (?<body>.*?)
175
+ ^\k<indent>end[ \t]*$
176
+ /mx
177
+
178
+ if (match = routes.match(resource_block))
179
+ return if match[:body].include?(
180
+ "resources :comments, only: %i[create destroy]"
181
+ )
182
+
183
+ updated_block = [
184
+ "#{match[:indent]}resources :#{resource_name} do",
185
+ match[:body].rstrip,
186
+ "#{match[:indent]} resources :comments, only: %i[create destroy]",
187
+ "#{match[:indent]}end"
188
+ ].reject(&:empty?).join("\n")
189
+
190
+ gsub_file(
191
+ routes_path,
192
+ match[0],
193
+ updated_block
194
+ )
195
+
196
+ return
197
+ end
198
+
199
+ simple_resource =
200
+ /^([ \t]*)resources\s+:#{Regexp.escape(resource_name)}[ \t]*$/
201
+
202
+ return unless routes.match?(simple_resource)
175
203
 
176
204
  gsub_file(
177
205
  routes_path,
178
- " resources :#{resource_name}\n",
206
+ simple_resource
207
+ ) do |resource_line|
208
+ indentation = resource_line[/\A[ \t]*/]
209
+
179
210
  [
180
- " resources :#{resource_name} do",
181
- " resources :comments, only: %i[create destroy]",
182
- " end",
183
- ""
211
+ "#{indentation}resources :#{resource_name} do",
212
+ "#{indentation} resources :comments, only: %i[create destroy]",
213
+ "#{indentation}end"
184
214
  ].join("\n")
185
- )
215
+ end
186
216
  end
187
217
 
188
218
  def add_comments_to_show_view