b4um_generators 0.4.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.
@@ -198,7 +198,10 @@ module B4um
198
198
 
199
199
  def add_authentication_helpers
200
200
  controller_path = "app/controllers/application_controller.rb"
201
- full_controller_path = File.join(destination_root, controller_path)
201
+ full_controller_path = File.join(
202
+ destination_root,
203
+ controller_path
204
+ )
202
205
 
203
206
  unless File.exist?(full_controller_path)
204
207
  raise Thor::Error,
@@ -211,31 +214,52 @@ module B4um
211
214
  "helper_method :#{current_authentication_method}, :logged_in?"
212
215
  )
213
216
 
214
- inject_into_file(
215
- controller_path,
216
- before: /^end\s*$/
217
- ) do
218
- <<~RUBY
219
-
220
- 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*$/)
221
242
 
222
- private
223
-
224
- def #{current_authentication_method}
225
- @#{current_authentication_method} ||= #{authentication_class_name}.find_by(id: session[:#{authentication_model_name}_id])
226
- end
243
+ unless class_end_position
244
+ raise Thor::Error,
245
+ "Could not find the end of ApplicationController."
246
+ end
227
247
 
228
- def logged_in?
229
- #{current_authentication_method}.present?
230
- end
248
+ controller.insert(
249
+ class_end_position,
250
+ authentication_helpers
251
+ )
231
252
 
232
- def require_login
233
- return if logged_in?
253
+ File.write(
254
+ full_controller_path,
255
+ controller
256
+ )
234
257
 
235
- redirect_to login_path, alert: "Please log in first."
236
- end
237
- RUBY
238
- end
258
+ say_status(
259
+ :insert,
260
+ controller_path,
261
+ :green
262
+ )
239
263
  end
240
264
 
241
265
  def update_in_place_helper
@@ -503,6 +527,12 @@ module B4um
503
527
  end
504
528
  end
505
529
 
530
+ def update_controller_tests
531
+ update_authentication_fixtures
532
+ update_authentication_resource_controller_tests
533
+ update_protected_controller_tests
534
+ end
535
+
506
536
  def protect_show_actions
507
537
  protected_controller_names.each do |controller_name|
508
538
  protect_show_actions_for(controller_name)
@@ -517,6 +547,300 @@ module B4um
517
547
 
518
548
  private
519
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
+
520
844
  def add_register_navigation_link(navigation_path, navigation)
521
845
  return if navigation.include?(
522
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
@@ -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(