shoulda_matchmakers 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +21 -0
  4. data/lib/controller/action_controller/action_controller_controller_sm_model.rb +53 -0
  5. data/lib/controller/action_controller/action_controller_controller_sm_model_helper.rb +73 -0
  6. data/lib/controller/action_controller/matchmakers/callbacks.rb +56 -0
  7. data/lib/controller/action_controller/matchmakers/filter_param.rb +69 -0
  8. data/lib/controller/action_controller/matchmakers/permit.rb +510 -0
  9. data/lib/controller/action_controller/matchmakers/redirect_to.rb +126 -0
  10. data/lib/controller/action_controller/matchmakers/render_template.rb +198 -0
  11. data/lib/controller/action_controller/matchmakers/render_with_layout.rb +72 -0
  12. data/lib/controller/action_controller/matchmakers/rescue_from.rb +96 -0
  13. data/lib/controller/action_controller/matchmakers/respond_with.rb +71 -0
  14. data/lib/controller/action_controller/matchmakers/route.rb +586 -0
  15. data/lib/controller/action_controller/matchmakers/set_flash.rb +136 -0
  16. data/lib/controller/action_controller/matchmakers/set_session.rb +115 -0
  17. data/lib/controller/action_controller/permitted_params_definition.rb +60 -0
  18. data/lib/generators/shoulda_matchmakers/controller_matcher_generator.rb +87 -0
  19. data/lib/generators/shoulda_matchmakers/controller_matcher_generator_helper.rb +92 -0
  20. data/lib/generators/shoulda_matchmakers/factory_generator.rb +86 -0
  21. data/lib/generators/shoulda_matchmakers/install_generator.rb +10 -0
  22. data/lib/generators/shoulda_matchmakers/model_matcher_generator.rb +87 -0
  23. data/lib/generators/shoulda_matchmakers/model_matcher_generator_helper.rb +107 -0
  24. data/lib/model/active_record/active_record_model_sm_model.rb +50 -0
  25. data/lib/model/active_record/active_record_model_sm_model_helper.rb +206 -0
  26. data/lib/model/active_record/factory_sm_model.rb +28 -0
  27. data/lib/model/active_record/factory_sm_model_helper.rb +124 -0
  28. data/lib/model/active_record/matchmakers/accept_nested_attributes_for.rb +66 -0
  29. data/lib/model/active_record/matchmakers/allow_value.rb +137 -0
  30. data/lib/model/active_record/matchmakers/associations.rb +52 -0
  31. data/lib/model/active_record/matchmakers/define_enum_for.rb +53 -0
  32. data/lib/model/active_record/matchmakers/delegate_method.rb +101 -0
  33. data/lib/model/active_record/matchmakers/have_db_column.rb +33 -0
  34. data/lib/model/active_record/matchmakers/have_db_index.rb +39 -0
  35. data/lib/model/active_record/matchmakers/have_readonly_attribute.rb +32 -0
  36. data/lib/model/active_record/matchmakers/have_secure_password.rb +32 -0
  37. data/lib/model/active_record/matchmakers/serialize.rb +67 -0
  38. data/lib/model/active_record/matchmakers/validations.rb +169 -0
  39. data/lib/model/active_record/options_definition.rb +160 -0
  40. data/lib/shoulda_matchmakers.rb +48 -0
  41. data/lib/shoulda_matchmakers/version.rb +3 -0
  42. data/lib/support/command_line_interface.rb +100 -0
  43. data/lib/support/controller_generator.rb +198 -0
  44. data/lib/support/indefinite_article.rb +70 -0
  45. data/lib/support/to_discard/belong_to.rb +94 -0
  46. data/lib/support/to_discard/have_and_belong_to_many.rb +83 -0
  47. data/lib/support/to_discard/have_many.rb +96 -0
  48. data/lib/support/to_discard/have_one.rb +98 -0
  49. data/lib/support/to_discard/permit_refactor.rb +270 -0
  50. data/lib/support/to_discard/render_template_new.rb +204 -0
  51. data/lib/support/to_discard/render_template_reconstruction.rb +188 -0
  52. data/lib/support/to_discard/use_around_action.rb +20 -0
  53. data/lib/support/to_discard/use_before_action.rb +20 -0
  54. data/lib/support/to_discard/validate_absence_of.rb +16 -0
  55. data/lib/support/to_discard/validate_acceptance_of.rb +16 -0
  56. data/lib/support/to_discard/validate_confirmation_of.rb +16 -0
  57. data/lib/support/to_discard/validate_exclusion_of.rb +145 -0
  58. data/lib/support/to_discard/validate_inclusion_of.rb +94 -0
  59. data/lib/support/to_discard/validate_length_of.rb +16 -0
  60. data/lib/support/to_discard/validate_numericality_of.rb +16 -0
  61. data/lib/support/to_discard/validate_presence_of.rb +16 -0
  62. data/lib/support/to_discard/validate_uniqueness_of.rb +16 -0
  63. data/lib/support/to_discard/validation_old.rb +37 -0
  64. data/lib/support/to_discard/validations.rb +41 -0
  65. data/lib/support/to_discard/validations_conditional.rb +146 -0
  66. data/lib/support/trace_debug_lines.txt +319 -0
  67. data/lib/support/util.rb +15 -0
  68. data/lib/templates/controller/action_controller/controller_spec_template.haml +146 -0
  69. data/lib/templates/factory/active_record/factory_template.haml +13 -0
  70. data/lib/templates/model/active_record/model_spec_template.haml +233 -0
  71. data/lib/templates/shoulda_matchmakers.rb +112 -0
  72. metadata +263 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a89f47e57114fcca2dc77c4c79a23a5ea277bc32
4
+ data.tar.gz: be011754779db03bd674e9c01bbf8581facd864e
5
+ SHA512:
6
+ metadata.gz: 8e4a59e5ed17a4fa4cf2ecfb8eed4dca6dea9ff538d4d1562125f19beff6790bb29dcbda7a07192f8cfb3460292c898273f3198bdc11b1abc77adb9aa65cf525
7
+ data.tar.gz: 13cd5034e269701147394e088625648bda2bc638b2610b0044a3fa6eccdf1e20a75946818294649311486888210fcd5e7564b5167bc7d4c39431dbc00e382add
@@ -0,0 +1,20 @@
1
+ Copyright 2016 App-o-matix Software
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,21 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'ShouldaMatchmakers'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+ Bundler::GemHelper.install_tasks
21
+
@@ -0,0 +1,53 @@
1
+ require_relative 'matchmakers/callbacks'
2
+ require_relative 'matchmakers/filter_param'
3
+ require_relative 'matchmakers/permit'
4
+ require_relative 'matchmakers/redirect_to'
5
+ require_relative 'matchmakers/render_template'
6
+ require_relative 'matchmakers/render_with_layout'
7
+ require_relative 'matchmakers/rescue_from'
8
+ require_relative 'matchmakers/respond_with'
9
+ require_relative 'matchmakers/route'
10
+ require_relative 'matchmakers/set_flash'
11
+ require_relative 'matchmakers/set_session'
12
+ require_relative 'action_controller_controller_sm_model_helper'
13
+
14
+ module ShouldaMatchmakers
15
+ module Controller
16
+ module ActionController
17
+ class ActionControllerControllerSmModel
18
+
19
+ ### Includes ###
20
+ include Rails.application.routes.url_helpers
21
+ include ActionDispatch::Routing
22
+
23
+ include Matchmaker::Callbacks
24
+ include Matchmaker::FilterParam
25
+ include Matchmaker::Permit
26
+ include Matchmaker::RedirectTo
27
+ include Matchmaker::RenderTemplate
28
+ include Matchmaker::RenderWithLayout
29
+ include Matchmaker::RespondWith
30
+ include Matchmaker::RescueFrom
31
+ include Matchmaker::Route
32
+ include Matchmaker::SetFlash
33
+ include Matchmaker::SetSession
34
+ include ActionControllerControllerSmModelHelper
35
+
36
+
37
+ ### Attribute Accessors ###
38
+ attr_accessor :app_controller_name,
39
+ :working_generated_code_line_length
40
+
41
+
42
+ ### Methods ###
43
+ def initialize(app_controller_name, app_action_controller_descendants_names, code_line_length)
44
+ @app_controller_name = app_controller_name
45
+ @app_action_controller_descendants_names = app_action_controller_descendants_names
46
+ @working_generated_code_line_length = code_line_length
47
+ end
48
+
49
+ end
50
+ end
51
+ end
52
+ end
53
+
@@ -0,0 +1,73 @@
1
+ module ShouldaMatchmakers
2
+ module Controller
3
+ module ActionController
4
+ module ActionControllerControllerSmModelHelper
5
+
6
+ def compose_extended_app_controller_file_path(app_controller_name)
7
+ # Rails.root.join("app", "controllers").to_s + "/" + get_controller_file_path(controller).concat(".rb")
8
+ Rails.root.join("app", "controllers").to_s + "/" + app_controller_name.to_s.underscore.concat(".rb")
9
+ end
10
+
11
+ def get_app_controller_file_path(app_controller_name)
12
+ app_controller_name.constantize.controller_path
13
+ end
14
+
15
+ def get_app_controller_routes_by_action(app_controller_name, app_controller_action)
16
+ Rails.application.routes.set.select { |route| route.defaults[:controller] == app_controller_name && route.defaults[:action] == app_controller_action }
17
+ end
18
+
19
+ def get_app_controller_routes_by_path(app_controller_path)
20
+ Rails.application.routes.set.select { |route| route.name == app_controller_path.to_s.chomp("_path") }
21
+ end
22
+
23
+ def compose_route_controller(app_controller_name)
24
+ app_controller_name.underscore.sub(/_controller$/, "")
25
+ end
26
+
27
+ def get_route_http_method(route)
28
+ route.constraints[:request_method].to_s.gsub("(?-mix:^","").gsub("$)","")
29
+ end
30
+
31
+ def containing_method_is_action(app_controller_name, app_controller_action_candidate)
32
+ app_controller_name.constantize.instance_methods(false).include?(app_controller_action_candidate.to_sym)
33
+ end
34
+
35
+ def append_element(element, element_set)
36
+ if element.present?
37
+ element_set << element
38
+ end
39
+ element_set
40
+ end
41
+
42
+ def format_tests(tests)
43
+ formatted_tests = properly_line_space_tests(tests)
44
+ formatted_tests.flatten.compact.uniq.join("\n")
45
+ end
46
+
47
+ def properly_line_space_tests(tests)
48
+ tests_properly_line_spaced = []
49
+ previous_test = ""
50
+ tests.each do |test|
51
+ if (test.include?("\n") && previous_test.present?) ||
52
+ (!test.include?("\n") && previous_test.include?("\n"))
53
+ test_properly_line_spaced = "\n" + test
54
+ else
55
+ test_properly_line_spaced = test
56
+ end
57
+ tests_properly_line_spaced << test_properly_line_spaced
58
+ previous_test = test
59
+ end
60
+ tests_properly_line_spaced
61
+ end
62
+
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ # Reject procs
69
+ # # TRACE.debug "ALL ACTIONS:"
70
+ # # TRACE.debug all_actions.inspect
71
+ # selected_actions.map(&:raw_filter).reject{ |action| action.class == Proc }
72
+ # all_actions.map { |action| action.is_a?(Symbol) ? ":#{ action }" : action.to_s }
73
+
@@ -0,0 +1,56 @@
1
+ module ShouldaMatchmakers
2
+ module Controller
3
+ module ActionController
4
+ module Matchmaker
5
+ module Callbacks
6
+
7
+
8
+ def use_action_matcher_tests(action_kind)
9
+ use_actions = get_use_actions(@app_controller_name, action_kind)
10
+ if use_actions.present?
11
+ generate_use_action_matcher_tests(use_actions)
12
+ else
13
+ []
14
+ end
15
+ end
16
+
17
+
18
+ private
19
+
20
+ def generate_use_action_matcher_tests(use_actions)
21
+ use_action_tests = []
22
+ use_actions.each do |use_action|
23
+ use_action_test = generate_use_action_test(use_action)
24
+ use_action_tests = append_element(use_action_test, use_action_tests)
25
+ end
26
+ format_tests(use_action_tests)
27
+ end
28
+
29
+ def generate_use_action_test(use_action)
30
+ if use_action[:filter] == "verify_authenticity_token" || use_action[:filter] == "verify_same_origin_request"
31
+ use_action_test = "# IMPLEMENTATION TODO: Need to determine proper implementation of tests involving CSRF token\n"
32
+ use_action_test.concat(" xit { is_expected.to use_#{ use_action[:kind] }_action(:#{ use_action[:filter] }) }")
33
+ else
34
+ use_action_test = " it { is_expected.to use_#{ use_action[:kind] }_action(:#{ use_action[:filter] }) }"
35
+ end
36
+ use_action_test
37
+ end
38
+
39
+ def get_use_actions(app_controller_name, kind = nil)
40
+ selected_actions_hashes = []
41
+ all_actions = app_controller_name.constantize._process_action_callbacks
42
+ if kind
43
+ selected_actions = all_actions.select { |f| f.kind == kind }
44
+ selected_actions = selected_actions.map(&:raw_filter).reject{ |action| action.class == Proc }
45
+ selected_actions.each do |selected_action|
46
+ selected_actions_hashes << { kind: kind.to_s, filter: selected_action.to_s }
47
+ end
48
+ end
49
+ selected_actions_hashes
50
+ end
51
+
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,69 @@
1
+ module ShouldaMatchmakers
2
+ module Controller
3
+ module ActionController
4
+ module Matchmaker
5
+ module FilterParam
6
+
7
+
8
+ def filter_param_matcher_tests
9
+ filter_params = Rails.application.config.filter_parameters
10
+ if @app_controller_name == "ApplicationController" && filter_params.present?
11
+ generate_filter_param_matcher_tests(filter_params)
12
+ else
13
+ []
14
+ end
15
+ end
16
+
17
+
18
+ private
19
+
20
+ def generate_filter_param_matcher_tests(filter_params)
21
+ filter_param_tests = []
22
+ multiple_params_tests_collections = []
23
+ filter_params.each do |param|
24
+ if param.is_a? Symbol
25
+ filter_param_test = " it { is_expected.to filter_param(:#{ param }) }"
26
+ filter_param_tests = append_element(filter_param_test, filter_param_tests)
27
+ else
28
+ if param.to_s.include? "|"
29
+ # IMPLEMENTATION TODO: Determine if this is correct implementation for filtered parameters defined together, separated by boolean '|' operators.
30
+ params_array = param.to_s.sub("(?-mix:^((?-mix", "").gsub("|", ",:").gsub("::", ":").sub("))$)", "").split(',')
31
+ multiple_params_tests_collection = compose_multiple_params_tests_collection(params_array)
32
+ multiple_params_tests_collections = append_element(multiple_params_tests_collection, multiple_params_tests_collections)
33
+ else
34
+ # IMPLEMENTATION TODO: Determine if this is correct implementation for filtered parameter regexes
35
+ param_regex = param.to_s.sub("(?-mix:", "/").sub("$)", "$/")
36
+ filter_param_test = " it { is_expected.to filter_param(#{ param_regex }) }"
37
+ filter_param_tests = append_element(filter_param_test, filter_param_tests)
38
+ end
39
+ end
40
+ end
41
+ filter_param_tests = filter_param_tests + multiple_params_tests_collections
42
+ format_tests(filter_param_tests)
43
+ end
44
+
45
+ def compose_multiple_params_tests_collection(params_array)
46
+ multiple_params_tests_collection = compose_multiple_params_tests_collection_comment(params_array)
47
+ params_array.each_with_index do |single_param, index|
48
+ multiple_params_tests_collection.concat(" it { is_expected.to filter_param(#{ single_param }) }")
49
+ if index < params_array.size - 1
50
+ multiple_params_tests_collection.concat("\n")
51
+ end
52
+ end
53
+ multiple_params_tests_collection
54
+ end
55
+
56
+ def compose_multiple_params_tests_collection_comment(params_array)
57
+ collection_comment = "# IMPLEMENTATION TODO: Determine if this is proper implementation of filtered parameters defined together, separated by boolean operators.\n"
58
+ collection_comment.concat("# The following filtered parameters are defined together in the app, separated by the '|' boolean operator:\n# ")
59
+ params_array.each do |single_param|
60
+ collection_comment.concat(single_param + ", ")
61
+ end
62
+ collection_comment.chomp!(", ").concat("\n")
63
+ end
64
+
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,510 @@
1
+ # IMPLEMENTATION TODO: Determine if the variables used in this matcher generator represent the appropriate values, as shown here:
2
+ #
3
+ # it do
4
+ # params = {
5
+ # <params_class>: {
6
+ # <permitted_params_def.permitted_params.each>: <sample_value>
7
+ # }
8
+ # }
9
+ # should permit(:<permitted_params_def.permitted_params.each>).
10
+ # for(:<calling_class_method>, params: params).
11
+ # on(:<calling_class>)
12
+ # end
13
+ #
14
+
15
+ require 'controller/action_controller/permitted_params_definition'
16
+
17
+ module ShouldaMatchmakers
18
+ module Controller
19
+ module ActionController
20
+ module Matchmaker
21
+ module Permit
22
+
23
+
24
+ def permit_matcher_tests
25
+ app_controller_permitted_params_defs = get_app_controller_permitted_params_defs(@app_controller_name)
26
+ if app_controller_permitted_params_defs.present?
27
+ app_controller_permitted_params_defs_and_calls =
28
+ add_app_controller_permitted_params_calls_to_defs(app_controller_permitted_params_defs)
29
+ permit_matcher_tests = generate_permit_matcher_tests(app_controller_permitted_params_defs_and_calls)
30
+ permit_matcher_tests
31
+ else
32
+ []
33
+ end
34
+ end
35
+
36
+
37
+ private
38
+
39
+ def get_app_controller_permitted_params_defs(app_controller_name)
40
+ app_controller_file_path = compose_extended_app_controller_file_path(app_controller_name)
41
+ if File.exists?(app_controller_file_path)
42
+ app_controller_permitted_params_defs = parse_app_controller_for_permitted_params_defs(app_controller_name, app_controller_file_path)
43
+ app_controller_permitted_params_defs
44
+ else
45
+ []
46
+ end
47
+ end
48
+
49
+ def parse_app_controller_for_permitted_params_defs(app_controller_name, app_controller_file_path)
50
+ current_app_controller_method = ""
51
+ parsing_params = false
52
+ app_controller_permitted_params_def = nil
53
+ app_controller_permitted_params_defs = []
54
+ continued_from_previous_line = false
55
+ File.open(app_controller_file_path, 'r') do |app_controller_file|
56
+ app_controller_file.each_line do |app_controller_file_line|
57
+ simplified_params_line = app_controller_file_line
58
+ if continued_from_previous_line
59
+ app_controller_permitted_params_def.permitted_params_string.concat(app_controller_file_line)
60
+ if app_controller_permitted_params_def.params_array_type == "space_delimited_array_of_symbols" ||
61
+ (app_controller_permitted_params_def.params_array_type == "" && app_controller_file_line.include?("%i"))
62
+ app_controller_permitted_params_def.params_array_type = "space_delimited_array_of_symbols" if app_controller_permitted_params_def.params_array_type == ""
63
+ app_controller_permitted_params_def = update_permitted_params_def(app_controller_permitted_params_def, app_controller_file_line)
64
+ elsif app_controller_permitted_params_def.params_array_type == "comma_delimited_array_of_symbols" ||
65
+ (app_controller_permitted_params_def.params_array_type == "" && app_controller_file_line =~ /^\s+(?::[a-z_][a-z0-9_]*(?:\s*|\s*,?\s*))+(?:\)|\n)/)
66
+ app_controller_permitted_params_def.params_array_type = "comma_delimited_array_of_symbols" if app_controller_permitted_params_def.params_array_type == ""
67
+ app_controller_permitted_params_def = update_permitted_params_def(app_controller_permitted_params_def, app_controller_file_line)
68
+ elsif app_controller_permitted_params_def.params_array_type == "other" ||
69
+ (app_controller_permitted_params_def.params_array_type == "" && app_controller_file_line =~ /^\s+[a-zA-Z:_\[]/)
70
+ app_controller_permitted_params_def.params_array_type = "other" if app_controller_permitted_params_def.params_array_type == ""
71
+ app_controller_permitted_params_def = update_permitted_params_def(app_controller_permitted_params_def, app_controller_file_line)
72
+ end
73
+ # IMPLEMENTATION TODO: Determine if possible to handle more complex params list containing nested parens
74
+ if app_controller_file_line.include?("(") && !(app_controller_file_line.include?("%i") || app_controller_permitted_params_def.params_array_type == "space_delimited_array_of_symbols")
75
+ app_controller_permitted_params_def = nil
76
+ parsing_params = false
77
+ continued_from_previous_line = false
78
+ elsif app_controller_file_line.include?(")")
79
+ app_controller_permitted_params_def.permitted_params.flatten!
80
+ app_controller_permitted_params_def.permitted_params.uniq!
81
+ if app_controller_permitted_params_def.params_array_type == "comma_delimited_array_of_symbols"
82
+ app_controller_permitted_params_def.permitted_params = app_controller_permitted_params_def.permitted_params.each{ |permitted_param| permitted_param.sub!(":", "") }
83
+ end
84
+ app_controller_permitted_params_defs = append_element(app_controller_permitted_params_def, app_controller_permitted_params_defs)
85
+ app_controller_permitted_params_def = nil
86
+ parsing_params = false
87
+ continued_from_previous_line = false
88
+ end
89
+ elsif app_controller_file_line =~ /\s+def\s[A-Za-z0-9_][A-Za-z0-9_!\?=]+/
90
+ current_app_controller_method = update_current_app_controller_method(app_controller_file_line)
91
+ else
92
+ if app_controller_file_line =~ /params\.require\(\s*:/ && current_app_controller_method.present?
93
+ app_controller_permitted_params_def = create_app_controller_permitted_params_def(app_controller_name, current_app_controller_method, app_controller_file_line)
94
+ simplified_params_line = remove_params_require_from_line(simplified_params_line)
95
+ end
96
+ if app_controller_file_line =~ /permit\(/ && app_controller_permitted_params_def.present? && app_controller_permitted_params_def.params_class.present?
97
+ if !app_controller_permitted_params_def.permitted_params_string.include?("permit(")
98
+ app_controller_permitted_params_def.permitted_params_string.concat(app_controller_file_line)
99
+ end
100
+ simplified_params_line = remove_permit_from_line(simplified_params_line)
101
+ if simplified_params_line.include?("(") && !simplified_params_line.include?("%i")
102
+ app_controller_permitted_params_def = nil
103
+ else
104
+ parsing_params = true
105
+ end
106
+ end
107
+ if parsing_params
108
+ if app_controller_file_line =~ /permit\(\s*%i/
109
+ app_controller_permitted_params_def.params_array_type = "space_delimited_array_of_symbols"
110
+ app_controller_permitted_params_def = update_permitted_params_def(app_controller_permitted_params_def, simplified_params_line)
111
+ elsif app_controller_file_line =~ /permit\(\s*:[a-z_]/
112
+ app_controller_permitted_params_def.params_array_type = "comma_delimited_array_of_symbols"
113
+ app_controller_permitted_params_def = update_permitted_params_def(app_controller_permitted_params_def, simplified_params_line)
114
+ elsif app_controller_file_line =~ /permit\(\s*(?:::|[a-zA-Z\[])/
115
+ app_controller_permitted_params_def.params_array_type = "other"
116
+ app_controller_permitted_params_def = update_permitted_params_def(app_controller_permitted_params_def, simplified_params_line)
117
+ end
118
+ if simplified_params_line.include?(")")
119
+ app_controller_permitted_params_def.permitted_params.flatten!
120
+ app_controller_permitted_params_def.permitted_params.uniq!
121
+ if app_controller_permitted_params_def.params_array_type == "comma_delimited_array_of_symbols"
122
+ app_controller_permitted_params_def.permitted_params = app_controller_permitted_params_def.permitted_params.each{ |permitted_param| permitted_param.sub!(":", "") }
123
+ end
124
+ app_controller_permitted_params_defs = append_element(app_controller_permitted_params_def, app_controller_permitted_params_defs)
125
+ app_controller_permitted_params_def = nil
126
+ parsing_params = false
127
+ else
128
+ continued_from_previous_line = true
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+ app_controller_permitted_params_defs
135
+ end
136
+
137
+ def update_permitted_params_def(permitted_params_def, file_line)
138
+ case permitted_params_def.params_array_type
139
+ when "comma_delimited_array_of_symbols"
140
+ update_permitted_params_def_comma_delimited_array_of_symbols_type(permitted_params_def, file_line)
141
+ when "space_delimited_array_of_symbols"
142
+ update_permitted_params_def_space_delimited_array_of_symbols_type(permitted_params_def, file_line)
143
+ when "other"
144
+ update_permitted_params_def_other_type(permitted_params_def)
145
+ else
146
+ permitted_params_def
147
+ end
148
+ end
149
+
150
+ def update_permitted_params_def_comma_delimited_array_of_symbols_type(permitted_params_def, file_line)
151
+ updated_permitted_params_def = permitted_params_def.dup
152
+ permitted_params_contained_in_line = file_line.scan(/((?::[a-z_][a-z0-9_]*\s*,?\s*)+)(?:\)|\n)/)
153
+ if permitted_params_contained_in_line.present?
154
+ permitted_params_string = permitted_params_contained_in_line.flatten.first
155
+ permitted_params = permitted_params_string.gsub(" ", "").split(",")
156
+ if permitted_params.select{ |permitted_param| permitted_param.first != ":"}.count > 0
157
+ updated_permitted_params_def.params_array_type = "other"
158
+ updated_permitted_params_def.permitted_params = []
159
+ else
160
+ permitted_params.uniq!
161
+ updated_permitted_params_def.permitted_params = updated_permitted_params_def.permitted_params + permitted_params
162
+ end
163
+ end
164
+ updated_permitted_params_def
165
+ end
166
+
167
+ def update_permitted_params_def_other_type(permitted_params_def)
168
+ updated_permitted_params_def = permitted_params_def.dup
169
+ updated_permitted_params_def.permitted_params = []
170
+ updated_permitted_params_def
171
+ end
172
+
173
+ def update_permitted_params_def_space_delimited_array_of_symbols_type(permitted_params_def, file_line)
174
+ updated_permitted_params_def = permitted_params_def.dup
175
+ permitted_params_contained_in_line = file_line.scan(/(?!:[a-z0-9_]+)(?:\s|\)|\n)((?:[a-z][a-z0-9_]*\s*)+)(?:\)|\n)/)
176
+ if permitted_params_contained_in_line.present?
177
+ permitted_params_string = permitted_params_contained_in_line.flatten.first
178
+ permitted_params = permitted_params_string.gsub(/\n/," ").gsub(/\s+/, " ").split(" ")
179
+ if permitted_params.select{ |permitted_param| permitted_param.first[/[^a-z_]/] == permitted_param.first }.count > 0
180
+ updated_permitted_params_def.params_array_type = "other"
181
+ updated_permitted_params_def.permitted_params = []
182
+ else
183
+ permitted_params.uniq!
184
+ updated_permitted_params_def.permitted_params = updated_permitted_params_def.permitted_params + permitted_params
185
+ end
186
+ end
187
+ updated_permitted_params_def
188
+ end
189
+
190
+ def remove_params_require_from_line(file_line)
191
+ file_line.sub(/params\.require\(\s*:[a-z][a-z0-9_]*\s*\)/, "")
192
+ end
193
+
194
+ def remove_permit_from_line(file_line)
195
+ file_line.sub(/\.permit\(/, "")
196
+ end
197
+
198
+ def create_app_controller_permitted_params_def(defining_controller, defining_method, app_controller_file_line)
199
+ params_class = app_controller_file_line.scan(/params\.require\(\s*(:[a-z][a-z0-9_]*)\s*\)/).flatten.first
200
+ ::ShouldaMatchmakers::Controller::ActionController::PermittedParamsDefinition.new(defining_controller, defining_method, params_class)
201
+ end
202
+
203
+ def add_app_controller_permitted_params_calls_to_defs(app_controller_permitted_params_defs)
204
+ permitted_params_defs = app_controller_permitted_params_defs.dup
205
+ permitted_params_defs.each do |permitted_params_def|
206
+ @app_action_controller_descendants_names.each do |app_action_controller_controller_name|
207
+ app_controller_file_path = compose_extended_app_controller_file_path(app_action_controller_controller_name)
208
+ if File.exists?(app_controller_file_path)
209
+ File.open(app_controller_file_path, 'r') do |app_controller_file|
210
+ current_app_controller_method = nil
211
+ app_controller_file.each_line do |app_controller_file_line|
212
+ if app_controller_file_line =~ /\s+def\s[A-Za-z0-9_][A-Za-z0-9_!\?=]+/
213
+ current_app_controller_method = update_current_app_controller_method(app_controller_file_line)
214
+ else
215
+ if app_controller_file_line =~ /\(\s*[a-zA-Z0-9_:\s,@]*#{ permitted_params_def.defining_method }/
216
+ permitted_params_def.calls <<
217
+ get_permitted_params_call_hash(permitted_params_def.defining_method, app_action_controller_controller_name, current_app_controller_method, app_controller_file_line)
218
+ end
219
+ end
220
+ end
221
+ end
222
+ end
223
+ end
224
+ if permitted_params_def.calls == []
225
+ call_hash = { calling_controller: "unidentified_calling_controller", calling_method: "unidentified_calling_method",
226
+ calling_class: "unidentified_calling_class", calling_class_method: "unidentified_calling_class_method",
227
+ call_implementation: "missing" }
228
+ permitted_params_def.calls << call_hash
229
+ end
230
+ end
231
+ permitted_params_defs
232
+ end
233
+
234
+ def get_permitted_params_call_hash(defining_method, calling_controller_name, calling_method, file_line)
235
+ if file_line =~ /(?:::)?[A-Z][a-zA-Z0-9:]*\.[a-z]+[a-z0-9_]*\(\s*#{ defining_method }\s*\)/
236
+ calling_class, calling_class_method = file_line.match(/((?:::)?[A-Z][a-zA-Z0-9:]*)\.([a-z]+[a-z0-9_]*)\(\s*#{ defining_method }\s*\)/).captures
237
+ { calling_controller: calling_controller_name, calling_method: calling_method,
238
+ calling_class: calling_class, calling_class_method: calling_class_method,
239
+ call_implementation: "simple" }
240
+ elsif file_line =~ /[a-zA-Z0-9:@_]+\.[a-z]+[a-z0-9_]*\([a-zA-Z0-9_:\s,@]*#{ defining_method }/
241
+ calling_class, calling_class_method = file_line.match(/([a-zA-Z0-9:@_]+)\.([a-z]+[a-z0-9_]*)\([a-zA-Z0-9_:\s,@]*#{ defining_method }/).captures
242
+ { calling_controller: calling_controller_name, calling_method: calling_method,
243
+ calling_class: calling_class, calling_class_method: calling_class_method,
244
+ call_implementation: "complex" }
245
+ else
246
+ { calling_controller: calling_controller_name, calling_method: calling_method,
247
+ calling_class: "unidentified_calling_class", calling_class_method: "unidentified_calling_class_method",
248
+ call_implementation: "complex" }
249
+ end
250
+ end
251
+
252
+ def generate_permit_matcher_tests(permitted_params_defs)
253
+ permit_tests = []
254
+ permitted_params_defs.each do |permitted_params_def|
255
+ permitted_params_def.calls.each do |call|
256
+ if call[:call_implementation] == "missing"
257
+ permit_test = "# Unable to locate params definition call. Locate params definition call to identify missing values\n"
258
+ permit_test.concat("# (see :unidentified_calling_class and :unidentified_calling_class_method below).\n")
259
+ permit_test.concat("# Remove 'x' from 'xit' once these missing values and any additional required values have been provided.\n")
260
+ permit_test.concat(" xit do\n")
261
+ elsif call[:call_implementation] == "complex"
262
+ if call[:calling_class] == "unidentified_calling_class" || call[:calling_class_method] == "unidentified_calling_class_method"
263
+ permit_test = "# Unable to identify params definition calling class and/or calling class method. Examine params definition call\n"
264
+ permit_test.concat("# in the '#{ call[:calling_method] }' method of '#{ call[:calling_controller] }' to identify these missing values.\n")
265
+ permit_test.concat("# Remove 'x' from 'xit' once these missing values and any additional required values have been provided.\n")
266
+ permit_test.concat(" xit do\n")
267
+ else
268
+ permit_test = "# Params definition call contains complexities that make it difficult for ShouldaMatchmakers to parse completely.\n"
269
+ permit_test.concat("# Examine params definition call in the '#{ call[:calling_method] }' method of '#{ call[:calling_controller] }' to identify any additional required values.\n")
270
+ permit_test.concat("# Remove 'x' from 'xit' once all required values have been provided.\n")
271
+ permit_test.concat(" xit do\n")
272
+ end
273
+ else
274
+ permit_test = " it do\n"
275
+ end
276
+ permit_test.concat(compose_permit_test_params_portion(permitted_params_def))
277
+ permit_test.concat(compose_permit_test_expectation_portion(permitted_params_def))
278
+ permit_test.concat(compose_permit_test_for_portion(call[:calling_class_method]))
279
+ permit_test.concat(compose_permit_test_on_portion(call[:calling_class]))
280
+ permit_tests << permit_test
281
+ end
282
+ end
283
+ format_tests(permit_tests)
284
+ end
285
+
286
+ def compose_permit_test_params_portion(permitted_params_def)
287
+ permit_test_params_portion = " params = {\n #{ permitted_params_def.params_class.to_s[1..-1] }: {\n"
288
+ if permitted_params_def.params_array_type == "other"
289
+ permit_test_params_portion.concat(" # Complex params definition. See comment above.")
290
+ else
291
+ permitted_params_def.permitted_params.each do |permitted_param|
292
+ permit_test_params_portion.concat(" #{ permitted_param.to_s }: '',\n")
293
+ end
294
+ permit_test_params_portion.chomp!(",\n")
295
+ end
296
+ permit_test_params_portion.concat("\n }\n }\n")
297
+ end
298
+
299
+ def compose_permit_test_expectation_portion(permitted_params_def)
300
+ permit_test_expectation_portion = " is_expected.to permit(\n"
301
+ if permitted_params_def.params_array_type == "other"
302
+ permit_test_expectation_portion.concat(" # Complex params definition. See comment above.")
303
+ else
304
+ permitted_params_def.permitted_params.each do |permitted_param|
305
+ permit_test_expectation_portion.concat(" :#{ permitted_param },\n")
306
+ end
307
+ permit_test_expectation_portion.chomp!(",\n")
308
+ end
309
+ permit_test_expectation_portion.concat("\n ).\n")
310
+ end
311
+
312
+ def compose_permit_test_for_portion(calling_class_method)
313
+ if calling_class_method.present?
314
+ " for(:#{ calling_class_method }, params: params).\n"
315
+ else
316
+ " for(:unidentified_calling_class_method, params: params).\n"
317
+ end
318
+ end
319
+
320
+ def compose_permit_test_on_portion(calling_class)
321
+ if calling_class.present?
322
+ if calling_class.underscore.include?("/")
323
+ # IMPLEMENTATION TODO: Determine if this is the proper implementation of calling class (colon/quotes) when it is a path (i.e. namespaced).
324
+ " on(:'#{ calling_class.underscore }')\n end"
325
+ else
326
+ " on(:#{ calling_class.underscore })\n end"
327
+ end
328
+ else
329
+ " on(unidentified_calling_class)\n end"
330
+ end
331
+ end
332
+
333
+ def update_current_app_controller_method(line)
334
+ line.scan(/\s+def\s([A-Za-z0-9_][A-Za-z0-9_!\?=]+)/).flatten.first
335
+ end
336
+
337
+ end
338
+ end
339
+ end
340
+ end
341
+ end
342
+
343
+
344
+ # TODO: Nested Attributes
345
+ # def protected_branch_params
346
+ # params.require(:protected_branch).permit(:name,
347
+ # merge_access_levels_attributes: [:access_level, :id],
348
+ # push_access_levels_attributes: [:access_level, :id])
349
+ # end
350
+
351
+
352
+ # TODO:
353
+ # params.require(:runner).permit(Ci::Runner::FORM_EDITABLE)
354
+
355
+ # TODO:
356
+ # params.require(:list).permit(:position).merge(id: params[:id])
357
+
358
+ # TODO:
359
+ # allowed_fields = NotificationSetting::EMAIL_EVENTS.dup
360
+ # allowed_fields << :level
361
+ # params.require(:notification_setting).permit(allowed_fields)
362
+
363
+ # TODO:
364
+ # def project_params
365
+ # project_feature_attributes =
366
+ # {
367
+ # project_feature_attributes:
368
+ # [
369
+ # :issues_access_level, :builds_access_level,
370
+ # :wiki_access_level, :merge_requests_access_level, :snippets_access_level
371
+ # ]
372
+ # }
373
+ #
374
+ # params.require(:project).permit(
375
+ # :name, :path, :description, :issues_tracker, :tag_list, :runners_token,
376
+ # :container_registry_enabled,
377
+ # :issues_tracker_id, :default_branch,
378
+ # :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar,
379
+ # :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex,
380
+ # :public_builds, :only_allow_merge_if_build_succeeds, :request_access_enabled,
381
+ # :lfs_enabled, project_feature_attributes
382
+ # )
383
+ # end
384
+
385
+ # TODO:
386
+ # def configure_permitted_parameters
387
+ # devise_parameter_sanitizer.permit(:sign_in, keys: [:username, :email, :password, :login, :remember_me, :otp_attempt])
388
+ # end
389
+
390
+ # TODO:
391
+ # def auth_params
392
+ # params.permit(:service, :scope, :account, :client_id)
393
+ # end
394
+
395
+ # TODO:
396
+ # def application_params
397
+ # params[:doorkeeper_application].permit(:name, :redirect_uri)
398
+ # end
399
+
400
+ # TODO:
401
+ # def service_params
402
+ # dynamic_params = @service.event_channel_names + @service.event_names
403
+ # service_params = params.permit(:id, service: ALLOWED_PARAMS + dynamic_params)
404
+ #
405
+ # if service_params[:service].is_a?(Hash)
406
+ # FILTER_BLANK_PARAMS.each do |param|
407
+ # service_params[:service].delete(param) if service_params[:service][param].blank?
408
+ # end
409
+ # end
410
+ #
411
+ # service_params
412
+ # end
413
+
414
+ # TODO:
415
+ # def project_params
416
+ # params.require(:variable).permit([:id, :key, :value, :_destroy])
417
+ # end
418
+
419
+ # TODO:
420
+ # def issue_params
421
+ # params.require(:issue).permit(
422
+ # :title, :assignee_id, :position, :description, :confidential,
423
+ # :milestone_id, :due_date, :state_event, :task_num, :lock_version, label_ids: []
424
+ # )
425
+ # end
426
+
427
+ # TODO:
428
+ # def application_setting_params
429
+ # restricted_levels = params[:application_setting][:restricted_visibility_levels]
430
+ # if restricted_levels.nil?
431
+ # params[:application_setting][:restricted_visibility_levels] = []
432
+ # else
433
+ # restricted_levels.map! do |level|
434
+ # level.to_i
435
+ # end
436
+ # end
437
+ #
438
+ # import_sources = params[:application_setting][:import_sources]
439
+ # if import_sources.nil?
440
+ # params[:application_setting][:import_sources] = []
441
+ # else
442
+ # import_sources.map! do |source|
443
+ # source.to_str
444
+ # end
445
+ # end
446
+ #
447
+ # enabled_oauth_sign_in_sources = params[:application_setting].delete(:enabled_oauth_sign_in_sources)
448
+ #
449
+ # params[:application_setting][:disabled_oauth_sign_in_sources] =
450
+ # AuthHelper.button_based_providers.map(&:to_s) -
451
+ # Array(enabled_oauth_sign_in_sources)
452
+ # params.delete(:domain_blacklist_raw) if params[:domain_blacklist_file]
453
+ #
454
+ # params.require(:application_setting).permit(
455
+ # :default_projects_limit,
456
+ # :default_branch_protection,
457
+ # :signup_enabled,
458
+ # :signin_enabled,
459
+ # :require_two_factor_authentication,
460
+ # :two_factor_grace_period,
461
+ # :gravatar_enabled,
462
+ # :sign_in_text,
463
+ # :after_sign_up_text,
464
+ # :help_page_text,
465
+ # :home_page_url,
466
+ # :after_sign_out_path,
467
+ # :max_attachment_size,
468
+ # :session_expire_delay,
469
+ # :default_project_visibility,
470
+ # :default_snippet_visibility,
471
+ # :default_group_visibility,
472
+ # :domain_whitelist_raw,
473
+ # :domain_blacklist_enabled,
474
+ # :domain_blacklist_raw,
475
+ # :domain_blacklist_file,
476
+ # :version_check_enabled,
477
+ # :admin_notification_email,
478
+ # :user_oauth_applications,
479
+ # :user_default_external,
480
+ # :shared_runners_enabled,
481
+ # :shared_runners_text,
482
+ # :max_artifacts_size,
483
+ # :metrics_enabled,
484
+ # :metrics_host,
485
+ # :metrics_port,
486
+ # :metrics_pool_size,
487
+ # :metrics_timeout,
488
+ # :metrics_method_call_threshold,
489
+ # :metrics_sample_interval,
490
+ # :recaptcha_enabled,
491
+ # :recaptcha_site_key,
492
+ # :recaptcha_private_key,
493
+ # :sentry_enabled,
494
+ # :sentry_dsn,
495
+ # :akismet_enabled,
496
+ # :akismet_api_key,
497
+ # :koding_enabled,
498
+ # :koding_url,
499
+ # :email_author_in_body,
500
+ # :repository_checks_enabled,
501
+ # :metrics_packet_size,
502
+ # :send_user_confirmation_email,
503
+ # :container_registry_token_expire_delay,
504
+ # :repository_storage,
505
+ # :enabled_git_access_protocol,
506
+ # restricted_visibility_levels: [],
507
+ # import_sources: [],
508
+ # disabled_oauth_sign_in_sources: []
509
+ # )
510
+ # end