shoulda_matchmakers 0.1.0

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 (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